问题描述
我有一个图像并将其作为可读/可写精灵添加到 unity 中。它是 32x32。但我不知道如何为顶点向量提供坐标,以便它给我一个与我的精灵具有相同形状的四边形区域。例如,如果我有一个 Heart.png 32x32 像素精灵,那么我想要一个来自四边形的心形区域
void Awake()
{
texture2D = sprite.texture;
mesh = new Mesh();
mf = GetComponent<MeshFilter>();
for(int i=0; i< texture2D.width; i++)
{
for(int j=0; j< texture2D.height; j++)
{
Color color = texture2D.GetPixel(i,j);
if(color.a == 0)
{
continue;
}
vertices = new Vector3[] { new Vector3(),new Vector3(),new Vector3() };
indeces = new int[]{ 0,1,2,3 };
mesh.vertices = vertices;
mesh.triangles = indeces;
}
}
mf.sharedMesh = mesh;
}
解决方法
其实这有点棘手。
我会做什么(因为它更容易实现 - 不是因为它更有效!)基本上是为每个像素创建一个具有相应颜色的小四边形(4 个顶点 -> 2 个三角形)。
[SerializeField] private Texture2D texture;
[SerializeField] private Mesh mesh;
[SerializeField] private MeshFilter mf;
[ContextMenu("TEST")]
private void Awake()
{
// Just in case use the mesh format that allows more vertices
if(!mf) mf = GetComponent<MeshFilter>();
mesh = new Mesh {indexFormat = IndexFormat.UInt32};
// Create these ONCE and fill them bit by bit
var verticesList = new List<Vector3>();
var trianglesList = new List<int>();
var colors = new List<Color>();
// Do this in ONE call -> more efficient
var pixels = texture.GetPixels();
// Place the pivot in the center of the texture
var pivotOffset = -new Vector3(texture.width / 2f,texture.height / 2f);
for (var i = 0; i < texture.width; i++)
{
for (var j = 0; j < texture.height; j++)
{
var color = pixels[i + j * texture.width];
if (color.a == 0)
{
continue;
}
// Add the pixel color for the FOUR vertices we create
colors.Add(color);
colors.Add(color);
colors.Add(color);
colors.Add(color);
// Create the four vertices around this pixel position
var vertex0 = pivotOffset + new Vector3(i - 0.5f,j - 0.5f,0);
var vertex1 = pivotOffset + new Vector3(i + 0.5f,0);
var vertex2 = pivotOffset + new Vector3(i - 0.5f,j + 0.5f,0);
var vertex3 = pivotOffset + new Vector3(i + 0.5f,0);
// Store the current length of so far vertices => first new vertex index
var currentIndex = verticesList.Count;
// Add the vertices to the list
verticesList.Add(vertex0);
verticesList.Add(vertex1);
verticesList.Add(vertex2);
verticesList.Add(vertex3);
// Two triangles for front side
trianglesList.Add(currentIndex);
trianglesList.Add(currentIndex + 2);
trianglesList.Add(currentIndex + 3);
trianglesList.Add(currentIndex);
trianglesList.Add(currentIndex + 3);
trianglesList.Add(currentIndex + 1);
// For double sided add the inverted faces for the back as well
trianglesList.Add(currentIndex);
trianglesList.Add(currentIndex + 3);
trianglesList.Add(currentIndex + 2);
trianglesList.Add(currentIndex);
trianglesList.Add(currentIndex + 1);
trianglesList.Add(currentIndex + 3);
}
}
// Finally apply them ONCE to the mesh
mesh.vertices = verticesList.ToArray();
mesh.triangles = trianglesList.ToArray();
mesh.colors = colors.ToArray();
mf.sharedMesh = mesh;
}
看起来像
如您所见,您可以玩转材料。通过将像素颜色存储到网格颜色中,您可以简单地使用支持顶点颜色的着色器。
进一步说明:
-
GetPixels
返回一个平面的一维数组。在该阵列中,纹理的像素逐行水平排列。这意味着假设你有一个像
这样的网格7 8 9 | 4 5 6 |-height=3 1 2 3 | _____ | width=3
变成
1,2,3,4,5,6,7,8,9
(Unity 纹理坐标从左下角从 0 开始)
所以为了访问某个条目,例如位于像素坐标
6
的x=2,y=1
你必须去1,9 | x(=2) + y(=1) * width(=3)
-
如前所述,我的简单解决方案是不要像您尝试的那样为每个像素创建一个顶点,而是创建一个正方形。为此,我们需要像
这样的顶点p-0.5x+0.5y p+0.5x+0.5y V------------------V | | | | | p | | (actualPosition) | | | V------------------V p-0.5x-0.5y p+0.5x-0.5y
我希望很棒的 ascii 艺术让它以某种方式清晰:D
-
最后,Unity 中网格的
triangles
只是一个int[]
,其中每个元素都指向vertices
数组中的一个顶点索引,并且始终3
连续的索引形成一个三角形。在一个三角形内,顶点的缠绕顺序定义了它面向的方向。
- 如果顶点按顺时针顺序排列,则法线朝向您。
- 如果顶点按逆时针顺序排列,则法线背对您。
参见例如Unity Manual - Creating a Quad,我希望这些图表能解释其余的内容
在我的示例中,对于每个像素,我按顺序添加了一个由四个顶点组成的四边形
2----3 | / | | / | 0----1
然后我顺时针添加两个三角形
-
0
2
3
-
0
3
1
逆时针(使它们双面)
-
0
3
2
-
0
1
3
最后,因为我们不希望索引从 0 开始,而是希望在
currentIndex
之前的值相同-
currentIndex+0
currentIndex+2
currentIndex+3
-
currentIndex+0
currentIndex+3
currentIndex+1
和
-
currentIndex+0
currentIndex+3
currentIndex+2
-
currentIndex+0
currentIndex+1
currentIndex+3