未使用Assimp加载OBJ文件时的法线加载Bug求助
问题:自定义OBJ加载器加载法线后模型变形
我正在开发不依赖Assimp的OBJ文件加载程序,遇到如下问题:不加载法线时,模型在世界空间显示正常;加载法线后,模型形状发生扭曲变形。尝试过新增法线索引缓冲区、定义Face结构体等方法,均未解决(曾出现LNK2019链接错误),求帮助。
显示效果对比
- 带法线:模型几何结构扭曲,形状错误
- 不带法线:立方体模型显示正常,几何结构正确
顶点数据加载代码
myFile.open("3dModels/Box.obj"); struct Vertex { XMFLOAT4 Pos; XMFLOAT3 Normal; //XMFLOAT2 Texture; }; std::vector<Vertex> vertices(14); std::vector<unsigned int> indices; std::vector<UINT> normalIndice; std::string Space_delimeter = " "; int index=0; int index1 = 0; int index2= 0; while (std::getline(myFile, line)) { std::istringstream lineStream(line); std::string lineType; lineStream >> lineType; if (line.starts_with("v ")) { Vertex vertex; lineStream >> vertex.Pos.x >> vertex.Pos.y >> vertex.Pos.z; vertices[index1].Pos.x = vertex.Pos.x; vertices[index1].Pos.y = vertex.Pos.y; vertices[index1].Pos.z = vertex.Pos.z; vertices[index1].Pos.w = 1.0f; index1++; } else if (lineType == "vn") { // vertex normal line float nx, ny, nz; lineStream >> nx >> ny >> nz; vertices[index].Normal.x = nx; vertices[index].Normal.y = ny; vertices[index].Normal.z = nz; index++; } else if (lineType == "vt") { //CURENTLY NOT USING TEXTURES // vertex texture line float u, v; lineStream >> u >> v; //vertices[index2].Texture.x = u; //vertices[index2].Texture.y = v; index2++; } else if (line.starts_with("f ")) { std::vector<std::string> values{}; size_t pos = 0; while ((pos = line.find(Space_delimeter)) != string::npos) { pos = line.find(Space_delimeter); values.push_back(line.substr(0, pos)); line.erase(0, pos + Space_delimeter.length()); } values.push_back(line.substr(0, pos)); for (int i=1;i<values.size();i++) { std::string Slash_delimeter = "/"; std::string linePart = values[i]; size_t pos1 = linePart.find(Slash_delimeter); std::string valueStr = linePart.substr(0, pos1); //Converting from string to int (vertices) unsigned int indice = 0; for (int j = 0; j < valueStr.length(); j++) { indice = indice * 10 + (valueStr[j] - '0'); } indices.push_back(indice - 1); //Setting normals indices linePart= linePart.erase(0, linePart.find(Slash_delimeter) + Slash_delimeter.length()); linePart = linePart.erase(0, linePart.find(Slash_delimeter) + Slash_delimeter.length()); linePart = linePart.erase(0, linePart.find(Slash_delimeter) + Slash_delimeter.length()); //Converting from string to int (normals) unsigned int indice1=0; for (int j = 0; j < linePart.length(); j++) { indice1 = indice1 * 10 + (linePart[j] - '0'); } normalIndice.push_back(indice1-1); } } } AddStaticBind(std::make_unique<PlaneVertexBuffer>(gfx, vertices)); auto pvs = std::make_unique<VertexShader>(gfx, L"VertexShader.cso"); auto pvsbc = pvs->GetBytecode(); AddStaticBind(std::move(pvs)); AddStaticBind(std::make_unique<PixelShader>(gfx, L"PixelShader.cso")); AddStaticIndexBuffer(std::make_unique<IndexBuffer>(gfx, indices,normalIndice)); //auto type =IndexBuffer::IndiceType::NormalIndice; //AddStaticNormalIndexBuffer(std::make_unique<IndexBuffer>(gfx, normalIndice,type)); const std::vector<D3D11_INPUT_ELEMENT_DESC> ied = { {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0}, {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,D3D11_APPEND_ALIGNED_ELEMENT,D3D11_INPUT_PER_VERTEX_DATA, 0} }; AddStaticBind(std::make_unique<InputLayout>(gfx, ied, pvsbc)); AddStaticBind(std::make_unique<Topology>(gfx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST));
IndexBuffer实现代码
IndexBuffer::IndexBuffer(Graphics& gfx, const std::vector<unsigned int>& indices, const std::vector<unsigned int>& normalIndices) : count((UINT)indices.size()+normalIndices.size()) { std::vector<Face> faces(indices.size()); for (int i = 0; i < indices.size(); i++) { faces[i]=(Face(indices[i], normalIndices[i])); } D3D11_BUFFER_DESC ibd = {}; ibd.BindFlags = D3D11_BIND_INDEX_BUFFER; ibd.Usage = D3D11_USAGE_DEFAULT; ibd.CPUAccessFlags = 0u; ibd.MiscFlags = 0u; ibd.ByteWidth = UINT(count * sizeof(unsigned int)); ibd.StructureByteStride = sizeof(unsigned int); D3D11_SUBRESOURCE_DATA isd = {}; isd.pSysMem = faces.data(); GetDevice(gfx)->CreateBuffer(&ibd, &isd, &pIndexBuffer); }
VertexShader代码
cbuffer CBuf { matrix transform; matrix model; matrix modelViewProj; }; struct VSOut { float4 worldPos : Position; float3 normal : Normal; float4 position : SV_Position; }; struct VS_INPUT { float3 position : Position; float3 normal : Normal; }; VSOut main(VS_INPUT input ) { VSOut vso; vso.position = mul(float4(input.position,1.0f), transform); vso.normal = input.normal; return vso; }
问题根源及修复方案
1. OBJ法线索引解析逻辑错误
OBJ的f行格式为v/vt/vn,你在解析法线索引时错误执行了三次erase,导致跳过所有有效内容,解析出的法线索引完全无效。正确解析方式:
std::string linePart = values[i]; // 提取顶点索引 size_t slash1 = linePart.find('/'); unsigned int v_idx = stoi(linePart.substr(0, slash1)) - 1; linePart.erase(0, slash1 + 1); // 跳过纹理索引(即使为空) size_t slash2 = linePart.find('/'); linePart.erase(0, slash2 + 1); // 提取法线索引 unsigned int vn_idx = stoi(linePart) - 1; normalIndice.push_back(vn_idx);
2. IndexBuffer实现完全错误
Direct3D索引缓冲区只能存储单一类型的顶点索引,不能存储Face结构体。你将Face数据传入索引缓冲区,导致GPU读取的索引值混乱,这是模型变形的核心原因。
正确逻辑:OBJ中顶点的唯一标识是(v, vt, vn)的组合,同一位置顶点对应不同法线时,需拆分出独立顶点实例:
- 用
std::map<std::tuple<int, int, int>, unsigned int>记录每个组合对应的顶点索引 - 解析
f行时,检查当前(v_idx, vt_idx, vn_idx)是否已存在:- 存在:直接使用对应顶点索引加入indices
- 不存在:从v和vn列表取数据创建新Vertex,加入vertices并记录映射关系,再将新索引加入indices
- 最终indices仅存储顶点缓冲区的索引,无需单独法线索引缓冲区
修正后的IndexBuffer:
IndexBuffer::IndexBuffer(Graphics& gfx, const std::vector<unsigned int>& indices) : count((UINT)indices.size()) { D3D11_BUFFER_DESC ibd = {}; ibd.BindFlags = D3D11_BIND_INDEX_BUFFER; ibd.Usage = D3D11_USAGE_DEFAULT; ibd.CPUAccessFlags = 0u; ibd.MiscFlags = 0u; ibd.ByteWidth = UINT(count * sizeof(unsigned int)); ibd.StructureByteStride = sizeof(unsigned int); D3D11_SUBRESOURCE_DATA isd = {}; isd.pSysMem = indices.data(); GetDevice(gfx)->CreateBuffer(&ibd, &isd, &pIndexBuffer); }
3. 顶点初始化与输入布局不匹配
- 你固定初始化了14个顶点,OBJ顶点数量可能不符,改为动态添加:
std::vector<Vertex> vertices;,解析v时用vertices.push_back(vertex) - Vertex结构体中Pos是
XMFLOAT4,但输入布局中Position格式为DXGI_FORMAT_R32G32B32_FLOAT,导致数据偏移错误。修正输入布局:
const std::vector<D3D11_INPUT_ELEMENT_DESC> ied = { {"Position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0}, {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,D3D11_APPEND_ALIGNED_ELEMENT,D3D11_INPUT_PER_VERTEX_DATA, 0} };
内容的提问来源于stack exchange,提问作者Marko
相关产品推荐
相关产品推荐

