【问题标题】:Normals acting weird in openGL法线在openGL中表现得很奇怪
【发布时间】:2012-06-29 01:21:09
【问题描述】:

我一直在为 openGl 编写一个 .obj 加载器。几何加载很好,但法线总是搞砸了。我尝试在 2 个不同的程序中导出模型,但似乎没有任何效果。据我所知,这就是您将法线放入 GL_TRIANGLES 的方式

     glNormal3fv(norm1);   
     glVertex3fv(vert1);
     glNormal3fv(norm2);
     glVertex3fv(vert2);
     glNormal3fv(norm3);
     glVertex3fv(vert3);

(法线在其余代码中引用 GLfloats。)

编辑:这是一张法线断裂的等面体图片

这是完整的 obj 加载器代码和文件:

    void loadOBJFromFile(NSString *path,float movex,float movey)
{
    NSString *contentns = [[NSString alloc]initWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];
    NSArray *pieces = [contentns componentsSeparatedByString:@"#"];
    //creating the arrays to read the vertecies and normals from.
    NSArray *normals = [[pieces objectAtIndex:3]componentsSeparatedByString:@"\n"];
    NSArray *vertecies = [[pieces objectAtIndex:2]componentsSeparatedByString:@"\n"];
    //The +1 is to make sure we ignore the texture/ material definition that vomes before the faces.
    int normalCount = [[normals objectAtIndex:0]intValue]+2;
    int faceCount = [[normals objectAtIndex:normalCount]intValue];
    //glTranslatef(movex, 0, movey);

glBegin(GL_TRIANGLES);
{
for (int i = 0; i < faceCount;i++)
{
    //aquires all the numbers in thye current face.
    NSArray *currentFace = [[normals objectAtIndex:normalCount+i+1]componentsSeparatedByString:@" "];
    NSArray *v1 = [[currentFace objectAtIndex:1]componentsSeparatedByString:@"//"];
    NSArray *v2 = [[currentFace objectAtIndex:2]componentsSeparatedByString:@"//"];
    NSArray *v3 = [[currentFace objectAtIndex:3]componentsSeparatedByString:@"//"];

    //crewatres the arrays to contain the vertecies
    NSArray *vertex1 = [[vertecies objectAtIndex:[[v1 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];
    NSArray *vertex2 = [[vertecies objectAtIndex:[[v2 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];
    NSArray *vertex3 = [[vertecies objectAtIndex:[[v3 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];
    //creates all the arrays for the normals
    NSArray *normal1 = [[normals objectAtIndex:[[v1 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];
    NSArray *normal2 = [[normals objectAtIndex:[[v2 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];
    NSArray *normal3 = [[normals objectAtIndex:[[v3 objectAtIndex:1]intValue]]componentsSeparatedByString:@" "];

    //creates the vertecies coordinates
    GLfloat vert1[] = {[[vertex1 objectAtIndex:1]floatValue],[[vertex1 objectAtIndex:2]floatValue],[[vertex1 objectAtIndex:3]floatValue]};
    GLfloat vert2[] = {[[vertex2 objectAtIndex:1]floatValue],[[vertex2 objectAtIndex:2]floatValue],[[vertex2 objectAtIndex:3]floatValue]};
    GLfloat vert3[] = {[[vertex3 objectAtIndex:1]floatValue],[[vertex3 objectAtIndex:2]floatValue],[[vertex3 objectAtIndex:3]floatValue]};
    //creates the normals coordinates

    GLfloat norm1[] = {[[normal1 objectAtIndex:1]floatValue],[[normal1 objectAtIndex:2]floatValue],[[normal1 objectAtIndex:3]floatValue]};
    GLfloat norm2[] = {[[normal2 objectAtIndex:1]floatValue],[[normal2 objectAtIndex:2]floatValue],[[normal2 objectAtIndex:3]floatValue]};
    GLfloat norm3[] = {[[normal3 objectAtIndex:1]floatValue],[[normal3 objectAtIndex:2]floatValue],[[normal3 objectAtIndex:3]floatValue]};

     glNormal3fv(norm1);   
     glVertex3fv(vert1);
     glNormal3fv(norm2);
     glVertex3fv(vert2);
     glNormal3fv(norm3);
     glVertex3fv(vert3);
}}
glEnd();

}

obj 文件:

#Wavefront OBJ file created by Hexagon 2 
mtllib object.mtl
g Form0
usemtl def_surf_mat
# 12 
v -12.533 4.78719 0 
v -12.533 20.2788 0 
v -7.74583 12.533 -12.533 
v -7.74583 12.533 12.533 
v 0 0 -7.74583 
v 0 0 7.74583 
v 0 25.066 -7.74583 
v 0 25.066 7.74583 
v 7.74583 12.533 -12.533 
v 7.74583 12.533 12.533 
v 12.533 4.78719 0 
v 12.533 20.2788 0 
# 12 
vn -0.850651 -0.525731 0 
vn -0.850651 0.525731 1.50014e-08 
vn -0.525731 -1.50014e-08 -0.850651 
vn -0.525731 0 0.850651 
vn 1.50014e-08 -0.850651 -0.525731 
vn -1.50014e-08 -0.850651 0.525731 
vn -1.50014e-08 0.850651 -0.525731 
vn 1.50014e-08 0.850651 0.525731 
vn 0.525731 0 -0.850651 
vn 0.525731 7.5007e-09 0.850651 
vn 0.850651 -0.525731 1.50014e-08 
vn 0.850651 0.525731 -1.50014e-08 
usemtl def_surf_mat
20
f 7//7 8//8 12//12 
f 7//7 2//2 8//8 
f 8//8 4//4 10//10 
f 6//6 10//10 4//4 
f 7//7 9//9 3//3 
f 5//5 3//3 9//9 
f 6//6 5//5 11//11 
f 6//6 1//1 5//5 
f 1//1 4//4 2//2 
f 1//1 2//2 3//3 
f 12//12 11//11 9//9 
f 12//12 10//10 11//11 
f 2//2 4//4 8//8 
f 10//10 12//12 8//8 
f 12//12 9//9 7//7 
f 3//3 2//2 7//7 
f 3//3 5//5 1//1 
f 4//4 1//1 6//6 
f 9//9 11//11 5//5 
f 11//11 10//10 6//6 

【问题讨论】:

  • 如何加载这些法线?您是否已对其进行调试以查看它们是否已正确加载?
  • 还要检查你的法线是否是单位长度。 .OBJ 不保证这一点。
  • 作为旁注,以后尽量避开立即模式,它已经很老了,并且在新版本的 opengl API 中被删除了,查看顶点缓冲区对象和 glew

标签: c++ objective-c opengl normals


【解决方案1】:

有了您在问题中添加的额外信息,我的想法已经不多了。我也无法真正阅读 Objective-C,但我建议仔细检查所有数组索引和行计数代码(也许打印所有内容以查看它们是否真的是您所期望的),因为解析基于信任确切的布局和注释单个出口商创建的看起来很毛茸茸。

原答案:

在没有看到您的加载程序代码或 .obj 文件的情况下,请检查以下几点:

  • 有多乱?完全错误,或者只是例如平面阴影而不是平滑阴影?
  • 那些 norm1、norm2 等是三个 GLfloat 的数组吗?
  • 普通数组是否包含合理的值(因此从文件中读取它们有效)?
  • 您的法线是否标准化为长度 1?
  • 法线索引是否正确,即您是否有正确顶点的正确法线?

【讨论】:

  • 法线完全错误。 norm1.... 是 GLfloats。法线是合理的值。我不知道你的标准化是什么。法线被索引到正确的顶点。
  • @BlueSpud Normalized 表示它们是单位长度,使它们的大小无效并且只保留方向。
  • 感谢您的澄清。我将模型更改为单位长度,但没有帮助。无论如何,谢谢。
【解决方案2】:

tl;dr 但是---检查你的法线符号是否正确。在计算中很容易使法线指向与您预期相反的方向。例如。它们沿着正确的路线,但指向内部而不是外部。您的图像与该种类或错误一致。

据我所知,在计算法线时,您必须采用两条边的向量叉积。您进行计算的顺序决定了法线是指向还是指向。因此,请确保以正确的顺序进行计算。

【讨论】:

  • 我实际上并没有计算法线,我假设 obj 文件中的内容已经计算过了。是吗?因为那肯定是问题所在。
  • 我不熟悉那种文件格式。但是“vn”元素的存在向我表明法线在文件中。但这些法线与特定类型的三角形“缠绕”有关。只有当您通过以正确的顺序将顶点传递给渲染器来渲染它们时,它们才能正常工作。
  • 是的,我相信它们是按正确的顺序呈现的。这就是 f 7//7 8//8 12//12 的意思。它告诉你 ling 7,8,12 顶点和 7,8,12 法线。仔细查看代码后,我认为它们的顺序可能不正确。
  • 嗯,obj 在另一个查看器中渲染正常,默认的 OpenGL 方向是 CCW——就像 objs 一样。
【解决方案3】:

在我看来,OBJ 文件是有效的,并且您的 OBJ 解析器可以正确读取顶点位置和法线。所以,我认为渲染问题来自不同的原因。

图像中的视觉伪影看起来像是不正确的深度测试。 (只画了一半的三角形)我猜你在 IB(Interface Builder)中使用了 NSOpenGLView 的一个子类。确保在 IB Attribute Inspector启用 NSOpenGLView 的深度缓冲区(默认为无)。深度缓冲区的常用值是 24 位,加上 Stencil 缓冲区的 8 位。


还有,这里有一些改进您的程序的建议;

  • 绘图和解析必须在单独的函数中执行。因为你只需要加载一次文件,但你可能会多次绘制模型。

  • 对顶点、法线和面使用 NSMutableArray。

  • 您的 OBJ 加载器不是解析 OBJ 文件的最佳方式,因为它依赖于注释行(以 # 开头)。我会这样做:

    1. 逐行读取并将它们存储到行数组中。
    2. 遍历每一行并测试以下内容:
    3. 如果它以 # 开头,则跳过解析该行。
    4. 如果它以“v”开头,则将以下 3 个数字在同一行添加到顶点数组。
    5. 如果它以“vn”开头,则将该行接下来的 3 个数字添加到普通数组中。
    6. 如果以“f”开头,则在人脸数组中添加 3 个人脸索引。

【讨论】:

  • 实际上,我正在使用 Foundation 命令行工具。我启用了 GL_DEPTH_TEST,但没有启用 GL_DEPTh。即使启用它,它仍然看起来很奇怪。我可以尝试将应用程序放在界面构建器中,因为我打算以后再做。
  • @BlueSpud,是的,请确保在创建 OpenGL 渲染上下文时有深度缓冲区。使用 GLUT,glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)。然后,在绘制每一帧之前清除深度缓冲区:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  • 实际上这样做会使客户端崩溃。我不知道为什么,但我找到了我的解决方案。感谢您的帮助,证实了我对渲染问题的怀疑以及优化代码的方式,因为在加载大约 1000 个多边形后,我需要大量执行此操作。
【解决方案4】:

答案很简单。对象正在从对象文件中正确加载。加载器代码可以进行更多优化。调用后,

glEnable(GL_CULL_FACE | GL_CULL_FACE_MODE);

编辑 我不敢相信我当时犯了这样一个新手错误。剔除的脸有帮助,但只是一点点。真正的问题是我忘记清除深度缓冲区。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

如果其他人也遇到同样的事情,那应该可以解决问题。

一些问题已经解决,我实际上需要清除另一个缓冲区或弹出矩阵或其他东西。现在,照明几何图形完全按照它在编辑器和建模程序中的显示方式加载。谢谢大家的帮助。

【讨论】:

    猜你喜欢
    • 2013-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2014-12-12
    • 2012-11-11
    • 1970-01-01
    相关资源
    最近更新 更多