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

使用rapidobj加载OBJ文件到游戏中模型扭曲的问题排查

问题:使用rapidobj加载OBJ模型出现扭曲异常

我尝试用rapidobj库把OBJ文件加载到游戏里,但每次加载模型都会扭曲显示异常(见附图)。哪怕把索引循环起始值改成OBJ规定的1,还是没法正确读取模型。

加载代码

// public domain, cc by sa is bad
auto objFile = rapidobj::ParseFile(location);
for(unsigned int i = 0; i < objFile.attributes.positions.size(); i+=3)
    this->bufferVertices.push_back(glm::vec3(objFile.attributes.positions[i], objFile.attributes.positions[i + 1], objFile.attributes.positions[i + 2]));

for(unsigned int i = 0; i < objFile.shapes.size(); i++) {
    for(unsigned e = 0; e < objFile.shapes[i].mesh.indices.size(); e++)
        this->indices.push_back(objFile.shapes[i].mesh.indices[e].position_index);
}
for(unsigned i = 0; i < indices.size(); i++)
    this->vertices.push_back(this->bufferVertices[this -> indices [i]]);

我的思路是先把顶点加载到数组,再通过索引把正确顶点复制到主顶点数组,之前这个逻辑是能用的,但第一次用rapidobj就不行。用RenderDoc查看发现模型部分加载正确,但有无效重复值导致模型损坏。

缓冲区代码

// public domain cc by sa is awful

glGenVertexArrays(1, &(this->vao));
glBindVertexArray(this->vao);
glEnableVertexAttribArray(0);
glGenBuffers(1, &(this->vbo[0]));
glBindBuffer(GL_ARRAY_BUFFER, this->vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(this->vertices.data())*sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);


glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);

glEnableVertexAttribArray(1);
glGenBuffers(1, &(this->vbo[1]));
glBindBuffer(GL_ARRAY_BUFFER, this->vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(this->position.data()) * sizeof(glm::vec3), position.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
glVertexAttribDivisor(1, 1);

请问我哪里出错了?


错误分析与修复方案

1. 索引与顶点数组的映射完全错误

rapidobj返回的position_index是指向原始positions数组的单float元素索引(0基),但你把positions每3个元素打包成glm::vec3存入bufferVertices后,直接用position_index作为bufferVertices的索引取值,这会导致顶点完全对应错误。

比如position_index=1属于第一个顶点(对应bufferVertices[0]),但你会取bufferVertices[1],直接导致顶点错乱,这是模型扭曲的核心原因。

修复代码:
把索引处理逻辑改成:

for(unsigned int i = 0; i < objFile.shapes.size(); i++) {
    for(unsigned e = 0; e < objFile.shapes[i].mesh.indices.size(); e++) {
        // 将单float索引转换为vec3顶点数组的索引
        unsigned int vertex_idx = objFile.shapes[i].mesh.indices[e].position_index / 3;
        this->indices.push_back(vertex_idx);
    }
}

2. 缓冲区数据大小计算错误

你用sizeof(this->vertices.data())计算缓冲区大小,而data()返回的是指针,64位系统下指针大小固定为8字节,这会导致实际写入GPU的数据量远小于需求,剩下的内存都是垃圾值,进一步加剧模型显示异常。

修复代码:
将glBufferData的大小参数改为元素个数乘以单个元素大小:

// 修复顶点缓冲区大小
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);

// 修复position缓冲区大小
glBufferData(GL_ARRAY_BUFFER, position.size() * sizeof(glm::vec3), position.data(), GL_DYNAMIC_DRAW);

3. 冗余的顶点复制逻辑(可选优化)

你当前把索引展开为顶点的逻辑完全没必要,直接使用索引绘制(EBO)更高效且不易出错:

// 生成并绑定EBO
unsigned int ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);

// 绘制时使用glDrawElements替代glDrawArrays
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

内容的提问来源于stack exchange,提问作者drk1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:45:39