C# XNA 4.0获取模型切线与副法线遇错误的解决方案咨询
报错原因梳理
第一个错误 The array is not the correct size for the amount of data requested,核心是你自定义的VertexPositionNormalTextureTangentBinormal顶点结构体大小,和原模型顶点缓冲区的字节数不匹配——要么原模型没带切线/副法线数据,要么结构体的元素布局和原数据完全不一致,导致读取时字节数对不上。
第二个错误 The current vertex declaration does not include all the elements required by the current vertex shader. Tangent0 is missing,是因为你改用不含切线的顶点结构体后,顶点着色器要求的Tangent0输入没有对应的数据,出现了语义不匹配。
分方向解决方案
1. Blender模型导出(确保包含切线/副法线)
用Better FBX Exporter导出时,严格做以下设置:
- 导出面板的Geometry组里,勾选
Tangent Space - 同时勾选
Normals和Smooth Groups(切线依赖法线计算) - 导出格式选FBX 7.4或更高版本,避免旧格式的兼容性问题
- 导出前给模型执行
Shift+N重新计算法线,确保法线数据正确,切线才能正常生成
2. 代码修正(保证顶点结构体与数据匹配)
首先要确保自定义顶点结构体的VertexDeclaration布局正确,示例如下:
public struct VertexPositionNormalTextureTangentBinormal : IVertexType { public Vector3 Position; public Vector3 Normal; public Vector2 TextureCoordinate; public Vector3 Tangent; public Vector3 Binormal; public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration( new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0), new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0), new VertexElement(32, VertexElementFormat.Vector3, VertexElementUsage.Tangent, 0), new VertexElement(44, VertexElementFormat.Vector3, VertexElementUsage.Binormal, 0) ); VertexDeclaration IVertexType.VertexDeclaration => VertexDeclaration; }
其次,不要直接用原meshPart.VertexBuffer.VertexCount创建数组,原缓冲区的顶点字节数可能和你的结构体不同,正确计算顶点数量的方式:
int vertexStride = VertexPositionNormalTextureTangentBinormal.VertexDeclaration.VertexStride; int vertexCount = meshPart.VertexBuffer.SizeInBytes / vertexStride; VertexBuffer vtxBuffer = new VertexBuffer(graphics.GraphicsDevice, typeof(VertexPositionNormalTextureTangentBinormal), vertexCount, BufferUsage.WriteOnly); VertexPositionNormalTextureTangentBinormal[] data = new VertexPositionNormalTextureTangentBinormal[vertexCount]; meshPart.VertexBuffer.GetData(data);
如果原模型确实没有切线数据,可以通过代码生成:遍历模型索引缓冲区,用顶点位置、UV、法线计算切线和副法线。
3. 管线适配(Shader与顶点数据对齐)
如果着色器要求Tangent0输入,顶点缓冲区必须包含对应数据,要么模型导出时带,要么代码生成。另外可以优化管线,不需要单独存储副法线——在Shader里通过法线和切线叉乘实时计算:
float3 binormal = cross(normal, tangent) * tangent.w;
(tangent的w分量用来标记副法线的方向)
同时要确保顶点结构体的VertexElementUsage和Shader输入语义完全对应,比如float3 tangent : TANGENT0对应VertexElementUsage.Tangent。
额外排查步骤
- 用FBX Viewer等工具打开导出的模型,检查是否包含Tangent和Binormal属性,确认模型数据本身正确
- 打印原
meshPart.VertexBuffer.VertexDeclaration的元素列表,对比原顶点数据和自定义结构体的差异
内容的提问来源于stack exchange,提问作者Axel Annand

