【发布时间】:2020-07-04 19:59:06
【问题描述】:
我按照this website 的教程制作了一个简单的模型加载器。
所有坐标都可以很好地读取,但它没有涉及的一件事是如何计算索引,但在进一步的教程之一found here 中称为 VBO 索引。虽然这似乎是一个很好的方法,但它计算的索引是错误的。
我在搅拌机中制作了一个立方体并导出为 obj,我使用我之前由大学老师制作的 obj 加载器加载它,索引读取为 0,1,2,3,4,(和依此类推)... 35 代表立方体。使用下面的代码,索引为 0,1,2,3,... 16,17,0,18,3,19,4,6,20,7,9,21,10,12,22,13 ,15,23,16.
这是因为它找到了相似的顶点并将其推回索引,而实际上对于这个立方体不应该有任何相似的索引。
函数索引VBO慢速
void Model::indexVBO_slow(
std::vector<glm::vec3>& in_vertices,
std::vector<glm::vec2>& in_uvs,
std::vector<glm::vec3>& in_normals,
std::vector<unsigned short>& out_indices,
std::vector<glm::vec3>& out_vertices,
std::vector<glm::vec2>& out_uvs,
std::vector<glm::vec3>& out_normals
) {
// For each input vertex
for (unsigned int i = 0; i < in_vertices.size(); i++) {
// Try to find a similar vertex in out_XXXX
unsigned short index;
bool found = getSimilarVertexIndex(in_vertices[i], in_uvs[i], in_normals[i], out_vertices, out_uvs, out_normals, index);
if (found) { // A similar vertex is already in the VBO, use it instead !
out_indices.push_back(index);
}
else { // If not, it needs to be added in the output data.
out_vertices.push_back(in_vertices[i]);
out_uvs.push_back(in_uvs[i]);
out_normals.push_back(in_normals[i]);
out_indices.push_back((unsigned short)out_vertices.size() - 1);
}
}
我将从文件中读取的顶点、uvs 和法线传入 vector3s 和 vector2 的向量中。而其他叫out_的人则作为空容器进入。
函数 getSimilarVertexIndex
// Searches through all already-exported vertices
// for a similar one.
// Similar = same position + same UVs + same normal
bool Model::getSimilarVertexIndex(glm::vec3& in_vertex, glm::vec2& in_uv, glm::vec3& in_normal, std::vector<glm::vec3>& out_vertices, std::vector<glm::vec2>& out_uvs, std::vector<glm::vec3>& out_normals, unsigned short& result)
{
// Lame linear search
for (unsigned int i = 0; i < out_vertices.size(); i++)
{
if (
is_near(in_vertex.x, out_vertices[i].x) &&
is_near(in_vertex.y, out_vertices[i].y) &&
is_near(in_vertex.z, out_vertices[i].z) &&
is_near(in_uv.x, out_uvs[i].x) &&
is_near(in_uv.y, out_uvs[i].y) &&
is_near(in_normal.x, out_normals[i].x) &&
is_near(in_normal.y, out_normals[i].y) &&
is_near(in_normal.z, out_normals[i].z)
)
{
result = i;
return true;
}
}
// No other vertex could be used instead.
// Needs to be added to VBO
return false;
}
函数 is_near
bool Model::is_near(float v1, float v2) {
return fabs(v1 - v2) < 0.01f;
}
填写 EBO
//Fill EBO with indices
m_buffer->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); //gl bind buffer
m_buffer->FillBuffer(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW); //gl buffer data
我 100% 确定这不起作用的原因是我不完全理解从 obj 获取数据并将其转换为 OpenGL 索引之间的逻辑。我理解它检查是否已经找到一些数据的部分,以便它可以重用一些索引数据,但不是 if 语句的细节。也许是因为我将它作为 EBO 发送,而教程正在做 VBO 索引。
解析器代码加上 VBO 索引
std::vector <glm::vec3> m_vertices, m_normals;
std::vector <glm::vec2> m_uvs;
std::vector <unsigned int> vertexIndices, uvIndices, normalIndices;
std::vector<unsigned short> out_indices;
std::vector<glm::vec3> out_vertices;
std::vector<glm::vec2> out_uvs;
std::vector<glm::vec3> out_normals;
std::vector<glm::vec3> temp_vertices, temp_normals;
std::vector<glm::vec2> temp_uvs;
std::string temp_text = "";
FILE* file = fopen(filepath.c_str(), "r");
//Open File
if (!file)
{
TheDebug::Log("Impossible to openfile", ALERT);
return false;
}
//Read file until the end
while (1)
{
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
{
break;
}
//Parse lineheader
int x = strcmp(lineHeader, "v");
if (strcmp(lineHeader, "v") == 0)
{
glm::vec3 vertex;
fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
temp_vertices.push_back(vertex);
}
//scan for uvs
else if (strcmp(lineHeader, "vt") == 0)
{
glm::vec2 uv;
glm::vec2 uv2;
glm::vec2 uv3;
fscanf(file, "%f %f %f %f\n", &uv.x, &uv.y, &uv3.x, &uv2.y);
temp_uvs.push_back(uv);
}
//scan for normals
else if (strcmp(lineHeader, "vn") == 0) {
glm::vec3 normal;
fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
temp_normals.push_back(normal);
}
//Scan for faces
else if (strcmp(lineHeader, "f") == 0)
{
std::string vertex1, vertex2, vertex3;
unsigned int vertexIndex[4], uvIndex[4], normalIndex[4];
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
if (matches != 9)
{
printf("File can't be read : ( Try exporting with other options\n");
fclose(file);
return false;
}
//triangulated
if (matches == 9)
{
//Add 3 total vertices
m_totalVertices += 3;
vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]);
uvIndices.push_back(uvIndex[0]);
uvIndices.push_back(uvIndex[1]);
uvIndices.push_back(uvIndex[2]);
normalIndices.push_back(normalIndex[0]);
normalIndices.push_back(normalIndex[1]);
normalIndices.push_back(normalIndex[2]);
}
}
}
//Go through each vertex of each triangle
for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
unsigned int vertexIndex = vertexIndices[i];
unsigned int uvIndex = uvIndices[i];
unsigned int normalIndex = normalIndices[i];
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
glm::vec2 uv = temp_uvs[uvIndex - 1];
glm::vec3 normal = temp_normals[normalIndex - 1];
m_normals.push_back(normal);
m_vertices.push_back(vertex);
m_uvs.push_back(uv);
}
fclose(file);
unsigned short result;
std::vector<GLuint> indices;
std::vector<glm::vec3> indexed_vertices;
std::vector<glm::vec2> indexed_uvs;
std::vector<glm::vec3> indexed_normals;
indexVBO(m_vertices, m_uvs, m_normals, indices, indexed_vertices, indexed_uvs, indexed_normals);
//在我用这些索引填充缓冲区之后。
加载的Cube图片
从昨天开始,我一直在搜索这些信息的来源,但我能找到的地方似乎非常有限,而且没有什么能真正帮助我找到信息,所以我不得不求助于堆栈溢出。
请记住,问题在于计算索引,因为使用另一个模型加载器,索引是不同的,并且同时从 0-35 变化,而我的则没有。
希望我没有浪费您的时间,并提前感谢您。
【问题讨论】:
-
看起来你是从三角形网格中创建立方体的。首先尝试查看您是否在某处启用了 CULL_FACE,将其删除并查看结果。如果它解决了问题,那么问题在于索引三角形的顶点。
-
@armagedescu 谢谢,但不是这样,我没有启用 CULL_FACE,事实上,当我使用其他模型加载器时,它可以完美地渲染立方体
-
你认为Wavefront OBJ索引从1开始,而数组索引从0开始?
-
@Rabbid76 对不起,我忘了上传这个代码,它现在在那里。我不知道我是否 100% 正确,但我认为当我遍历每个三角形的每个顶点时我会这样做
-
您使用的是 glDrawArray 还是 glDrawElement?我想我在使用 glDrawArray 时也遇到了同样的问题,所以它不会使用索引