You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:51:18