将Mesh.SetVertices()替换为SetVertexBufferParams/Data后网格异常排查
问题描述
为提升网格生成性能,我尝试将Unity中Mesh.SetVertices()的使用替换为SetVertexBufferParams()与SetVertexBufferData()的组合模式。现有代码中,使用SetVertices()可生成正确的平面网格,但替换为新模式后,生成的网格显示异常。代码示例如下:
// verts is a NativeArray<float3> // numVerts is the length of verts Mesh mesh = new Mesh(); // this works and creates a series of flat faces mesh.SetVertices(verts); // this creates a mesh but it's very wrong mesh.SetVertexBufferParams(numVerts, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3)); mesh.SetVertexBufferData(verts, 0, 0, numVerts); // afterwards I set indices, norms, etc.
同时附上了预期网格效果与新模式生成的错误网格效果对比图,请问我哪里操作有误?
问题原因与解决方法
你的核心问题是顶点缓冲区的布局和数据不匹配:
- 调用
SetVertexBufferParams()时,你声明了两个顶点属性:Position和Normal,这意味着每个顶点的数据结构是「3个float的位置 + 3个float的法线」,总共6个float。 - 但你调用
SetVertexBufferData()时只传入了verts(仅位置数据),相当于把位置数据同时填充给了位置和法线属性,导致顶点数据错位,渲染出异常网格。
针对这个问题,有两种解决思路:
思路1:分别设置位置和法线缓冲区
如果想分开管理位置和法线数据,可以单独为每个属性设置缓冲区:
Mesh mesh = new Mesh(); // 只声明位置属性,使用默认的缓冲区0 mesh.SetVertexBufferParams(numVerts, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3)); // 传入位置数据到缓冲区0 mesh.SetVertexBufferData(verts, 0, 0, numVerts); // 为法线声明第二个缓冲区(索引1) mesh.SetVertexBufferParams(numVerts, new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3), 1); // 将法线数据传入缓冲区1 mesh.SetVertexBufferData(norms, 0, 0, numVerts, 1); // 后续设置索引等操作不变
思路2:使用交错式顶点数据
如果想使用单个缓冲区存储所有顶点属性,需要先把位置和法线数据合并成一个连续的数组,每个顶点包含位置+法线的完整数据:
// 创建交错数据数组,每个顶点占6个float(3个位置 + 3个法线) NativeArray<float> interleavedData = new NativeArray<float>(numVerts * 6, Allocator.Temp); for (int i = 0; i < numVerts; i++) { int baseIdx = i * 6; // 填充位置数据 interleavedData[baseIdx] = verts[i].x; interleavedData[baseIdx + 1] = verts[i].y; interleavedData[baseIdx + 2] = verts[i].z; // 填充法线数据 interleavedData[baseIdx + 3] = norms[i].x; interleavedData[baseIdx + 4] = norms[i].y; interleavedData[baseIdx + 5] = norms[i].z; } Mesh mesh = new Mesh(); // 声明包含位置和法线的交错布局 mesh.SetVertexBufferParams(numVerts, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3)); // 传入完整的交错数据到缓冲区0 mesh.SetVertexBufferData(interleavedData, 0, 0, numVerts); // 记得释放NativeArray interleavedData.Dispose();
补充说明:之前用SetVertices()+SetNormals()时,Unity会自动帮你维护两个独立的顶点缓冲区;而SetVertexBufferParams()+SetVertexBufferData()需要你完全控制缓冲区的布局和数据对应关系,所以必须保证声明的属性和传入的数据结构完全匹配。
内容的提问来源于stack exchange,提问作者Jam
相关产品推荐
相关产品推荐

