gpt4 book ai didi

c++ - 从 .obj 文件在 OpenGL 中绘制四边形

转载 作者:搜寻专家 更新时间:2023-10-31 01:42:11 25 4
gpt4 key购买 nike

我正在将一个 .obj(在 Blender 中创建,如果有帮助的话)解析为 OpenGL 中的 C++ 程序,它似乎正在接近。它似乎正确地找到了这里需要的顶点。我正在尝试创建一个四处移动的大写字母 I。这是我的解析代码:

bool loadObjectFile(const char * filepath, std::vector < glm::vec3 > & out_vertices, std::vector < glm::vec3 > & out_normals)
{
std::vector< unsigned int > vertexIndices;
std::vector< glm::vec3 > temp_vertices;
std::vector< glm::vec3 > temp_normals;
glm::vec3 tempFaceNormal;

FILE * file = fopen(filepath, "r");
if (file == NULL)
{
printf("Impossible to open the file !\n");
return false;
}

while (1)
{
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // EOF = End Of File. Quit the loop.

// else : parse lineHeader
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);
glm::vec3 zeroface;
zeroface.x = 0, zeroface.y = 0, zeroface.z = 0;
temp_normals.push_back(zeroface);
}
else if (strcmp(lineHeader, "f") == 0)
{
unsigned int vertexIndex[3];
int matches = fscanf(file, "%d %d %d\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]);

if (matches != 3)
{
printf("File can't be read by our simple parser : ( Try exporting with other options\n");
return false;
}

vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]);

tempFaceNormal = computeNormal(temp_vertices[vertexIndex[0] - 1], temp_vertices[vertexIndex[1] - 1], temp_vertices[vertexIndex[2] - 1]);

temp_normals[vertexIndex[0] - 1] += tempFaceNormal;
temp_normals[vertexIndex[1] - 1] += tempFaceNormal;
temp_normals[vertexIndex[2] - 1] += tempFaceNormal;
}
}

// For each vertex of each triangle
for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
unsigned int vertexIndex = vertexIndices[i];
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
out_vertices.push_back(vertex);
}

//For each vertex normal
for (unsigned int i = 0; i < temp_normals.size(); i++)
{
out_normals.push_back(glm::normalize(temp_normals[i]));
}
}

glm::vec3 computeNormal(glm::vec3 const & a, glm::vec3 const & b, glm::vec3 const & c)
{
//Returns the (not normalized) face normal of each triangle
return glm::cross(c - a, b - a);
}

这是我用来绘制解析顶点的代码:

glBegin(GL_QUADS);
glColor3f(0.2, 0.2, 0);
for (int i = 0; i < vertices.size(); i++)
{
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
glTexCoord3f(vertices[i].x, vertices[i].y, vertices[i].z);
}
for (int i = 0; i < normals.size(); i++)
{
glNormal3f(normals[i].x, normals[i].y, normals[i].z);
}
glEnd();

这会产生以下内容(屏幕截图):

https://drive.google.com/file/d/0B6qCowcn51DnQk1mT0hSUjZWc00/view?usp=sharing

https://drive.google.com/file/d/0B6qCowcn51DndTBJRFctV1lOQlk/view?usp=sharing

在这里,顶点似乎是正确的,但面似乎画错了。

最佳答案

此代码中存在许多问题。我不会详细介绍其中的大部分内容,但至少想指出主要问题。

OBJ解析

只要您自己生成 OBJ 文件并准确控制它们包含的内容,您正在做的主要部分就可能没问题。如果您希望能够解析更通用的 OBJ 文件,您将需要更多。例如:

  • “v”和“f”以外的其他记录类型。 OBJ 文件可以包含其他数据。其中的一部分,如法线(“vn”)和纹理坐标(“vt”)通常对 OpenGL 渲染至关重要。除此之外,还有很多可能有用也可能没用的东西,这取决于你想要什么。但至少您必须能够跳过您不解析的记录。
  • 索引值可以是负数,使它们相对于当前位置。
  • 面可以有任意数量的顶点。
  • 面的格式每个顶点最多有 3 个索引(顶点/纹理坐标/法线),用斜线分隔。

原始类型不匹配

这可能是最大的问题。您的解析代码读取每个面 3 个顶点,这意味着它期望所有面都是三角形:

int matches = fscanf(file, "%d %d %d\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]);
if (matches != 3)
{
printf("File can't be read by our simple parser : ( Try exporting with other options\n");
return false;
}

但是随后渲染代码将面渲染为四边形:

glBegin(GL_QUADS);

如果文件包含三角形,则需要将其用作渲染的基元类型:

glBegin(GL_TRIANGLES);

如果它包含四边形,则需要为每个面解析 4 个顶点索引,并相应地更新其余的解析代码。

普通数组

代码包含根据面中顶点索引的顺序重新排列顶点的逻辑:

for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
unsigned int vertexIndex = vertexIndices[i];
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
out_vertices.push_back(vertex);
}

但它只是按照顶点的原始顺序复制法线:

for (unsigned int i = 0; i < temp_normals.size(); i++)
{
out_normals.push_back(glm::normalize(temp_normals[i]));
}

相反,它需要以与顶点相同的方式重新排列法线。这可以作为顶点循环的一部分完成:

for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
unsigned int vertexIndex = vertexIndices[i];
out_vertices.push_back(temp_vertices[vertexIndex - 1]);
out_normals.push_back(glm::normalize(temp_normals[vertexIndex - 1]));
}

渲染过程中的法线规范

渲染代码在遍历顶点之后包含一个单独的法线循环。以这种方式指定法线确实值得注意。 glNormal3fv() 需要在 glVertex3fv() 之前调用以指定该特定顶点的法线。

渲染循环应该如下所示,而不是有两个单独的循环:

for (int i = 0; i < vertices.size(); i++)
{
glNormal3f(normals[i].x, normals[i].y, normals[i].z);
glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
}

glTexCoord3f() 调用也会在 glVertex3f() 之前进行,但您首先没有纹理坐标。

过时的 OpenGL

您正在使用的渲染调用 glBegin()/glEnd()glVertex3f() 等是遗留功能。这通常称为立即模式,已弃用,并且在现代版本的 OpenGL(OpenGL 3.x/4.x 核心配置文件、OpenGL ES 2.0/3.x)中不再可用。

对于现代渲染,首先要了解顶点缓冲区对象 (VBO) 和顶点数组对象 (VAO) 以指定您的顶点。然后,您可以使用 glDrawElements()glDrawArrays() 通过一次调用绘制整个对象。

关于c++ - 从 .obj 文件在 OpenGL 中绘制四边形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27340741/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com