加载 OBJ 文件,如何使用法线 (#vertices < #normals)

Posted

技术标签:

【中文标题】加载 OBJ 文件,如何使用法线 (#vertices < #normals)【英文标题】:Loading an OBJ file, how to use normals (#vertices < #normals) 【发布时间】:2013-01-16 05:54:08 【问题描述】:

我有一个 obj 文件,并且在没有使用给定法线的情况下成功地将对象加载到 opengl。 这是它的外观: 文件格式为:

v x y z
vn x y z
f x//x' y//y' z//z'

网格的显示功能是这样的:

glBegin(GL_TRIANGLES);
for all faces

    glVertex3f(.., .., ..);
    glVertex3f(.., .., ..);
    glVertex3f(.., .., ..);

glEnd();

结果是这样的:

我了解到,由于 OpenGL 用于照明方程的默认法线向量,该对象可能看起来很平坦。 这可以用法线解决。给定的法线是 780,顶点是 155。

我尝试在每次调用 glVertex3f 之前使用 glNorm,但 obj 看起来完全奇怪(线条等)。

我该怎么办?

编辑#1: 这就是我读取文件的方式:

void loader(class mesh &tree)

int vx1, vx2, vx3, vn1, vn2, vn3;
ifstream file;
string line;
point vec, norm;
face tempface;

file.open("tree.obj"); 
if (file.is_open() == true)

    while(getline(file, line) )
    
        if (line.substr(0,2) == "") continue;
        else if (line.substr(0, 2) == "v ")
        
            istringstream numbers(line.substr(2));
            numbers >> vec.x >> vec.y >> vec.z;
            tree.vectors.push_back(vec);
        
        else if (line.substr(0,2) == "vn")
        
            istringstream numbers(line.substr(2));
            numbers >> norm.x >> norm.y >> norm.z;
            tree.normals.push_back(norm);
        
        else if (line.substr(0,2) == "f ")
        
            face f;
            line = line.substr(2,line.length());
            for (string::iterator it = line.begin(); it != line.end(); ++it)
            
                if (*it == '/')
                
                    //erase both of the "//"
                    line.erase(it);
                    line.erase(it);
                    //add a space between the numbers
                    line.insert(it, ' ');
                
            
            istringstream inp(line);
            inp >> f.vert_indices[0] >> f.norm_indices[0] >> f.vert_indices[1] >> f.norm_indices[1] >> f.vert_indices[2] >> f.norm_indices[2];
            tree.faces.push_back(f);
        
        else 
            continue;
    
    file.close();


这就是我展示它的方式:

    void mesh::displayMesh()

    glPushMatrix();
    glBegin(GL_TRIANGLES);
    for(vector<face>::const_iterator it = faces.begin();
        it != faces.end(); ++it)
    
        //glVertex3f(normals[it->norm_indices[0] -1 ].x, normals[it->norm_indices[0] -1 ].y, normals[it->norm_indices[0] -1 ].z);
        glVertex3f(vectors[it->vert_indices[0] -1 ].x, vectors[it->vert_indices[0] -1 ].y, vectors[it->vert_indices[0] -1 ].z);
        //glVertex3f(normals[it->norm_indices[1] -1 ].x, normals[it->norm_indices[1] -1 ].y, normals[it->norm_indices[1] -1 ].z);
        glVertex3f(vectors[it->vert_indices[1] -1 ].x, vectors[it->vert_indices[1] -1 ].y, vectors[it->vert_indices[1] -1 ].z);
        //glVertex3f(normals[it->norm_indices[2] -1 ].x, normals[it->norm_indices[2] -1 ].y, normals[it->norm_indices[2] -1 ].z);
        glVertex3f(vectors[it->vert_indices[2] -1 ].x, vectors[it->vert_indices[2] -1 ].y, vectors[it->vert_indices[2] -1 ].z);
    
    glEnd();
    glPopMatrix();

函数displayMesh在openGL的Render函数中调用,loader函数在Setup OpenGL函数中调用,网格树对象是一个全局变量。

编辑#2: 这是树在法线下的样子: 还有this is the codeOpenGL的设置函数。

【问题讨论】:

如果您使用 Blender(或其他 3D 编辑器/查看器)打开您的 OBJ,它会正确显示吗? glVertex3f 函数之前只添加glNormal3f 调用不会有效果,除非您同时启用OpenGL 照明glEnable(GL_LIGHTING) 和至少一个照明,例如glEnable(GL_LIGHT0)。照明将使用材质、灯光颜色等的所有默认照明设置进行计算。 @ViníciusGobboA.deOliveira:是的,我在 Blender 中打开了它,当我用 opengl 加载它时,它看起来好多了。 @radical7:另外,我在我的代码glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); 中启用了照明,但仍然没有。我更新了问题并添加了两张图片。 在我看来,您正在错误地读取某些顶点。您确定文件中的每一行都具有您正在阅读的格式吗? obj 文件中的任何行都没有异常? 【参考方案1】:

您的绘图代码中不应该有以下内容吗?

glNormal3f(normals[it->norm_indices[0] -1 ].x, normals[it->norm_indices[0] -1 ].y, normals[it->norm_indices[0] -1 ].z);
glVertex3f(vectors[it->vert_indices[0] -1 ].x, vectors[it->vert_indices[0] -1 ].y, vectors[it->vert_indices[0] -1 ].z);
glNormal3f(normals[it->norm_indices[1] -1 ].x, normals[it->norm_indices[1] -1 ].y, normals[it->norm_indices[1] -1 ].z);
glVertex3f(vectors[it->vert_indices[1] -1 ].x, vectors[it->vert_indices[1] -1 ].y, vectors[it->vert_indices[1] -1 ].z);
glNormal3f(normals[it->norm_indices[2] -1 ].x, normals[it->norm_indices[2] -1 ].y, normals[it->norm_indices[2] -1 ].z);
glVertex3f(vectors[it->vert_indices[2] -1 ].x, vectors[it->vert_indices[2] -1 ].y, vectors[it->vert_indices[2] -1 ].z);

编辑:哎呀,你已经意识到了

如果要检查法线是否正确,请根据法线为每个顶点着色。使用 glColor3f 但提供法线。

【讨论】:

如果我这样做,期望的输出是什么?很多颜色? 面将根据法线着色。例如,向上的面将是绿色的,因为法线 y 分量是正的。【参考方案2】:

当您使用擦除时,它会使迭代器无效

//erase both of the "//"
it = line.erase(it);
it = line.erase(it);
//add a space between the numbers
it = line.insert(it, ' ');

但是你可以擦除一次然后替换

//erase both of the "//"
it = line.erase(it);
//add a space between the numbers
*it = ' ';

【讨论】:

以上是关于加载 OBJ 文件,如何使用法线 (#vertices < #normals)的主要内容,如果未能解决你的问题,请参考以下文章

计算与加载 OpenGL 的法线

three.js 加载 obj模型

镜面高光故障 OpenGL

每个顶点超过 1 个法线

为啥maya绑定的模型导出fbx会丢失顶点法线信息?

OBJ Loader 不会“创建”或“渲染”对象