【问题标题】:Automatically calculate normals in GLKit/OpenGL-ES在 GLKit/OpenGL-ES 中自动计算法线
【发布时间】:2012-01-13 01:12:57
【问题描述】:

我正在根据 Apple 的示例代码在 OpenGL-ES 中制作一些相当基本的形状。他们使用了一个点数组,在第一个数组中有一个索引数组,每组三个索引创建一个多边形。太好了,我可以做出我想要的形状。为了正确着色形状,我相信我需要计算每个多边形上每个顶点的法线。起初形状是长方体的,所以很容易,但现在我正在制作(稍微)更高级的形状,我想自动创建这些法线。如果我得到一个多边形的两条边的向量(这里所有的多边形都是三角形)并将它们的叉积用于该多边形上的每个顶点,这似乎很容易。之后我使用下面的代码来绘制形状。

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, triangleVertices);

glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, 0, triangleColours);

glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 0, triangleNormals);

glDrawArrays(GL_TRIANGLES, 0, 48);

glDisableVertexAttribArray(GLKVertexAttribPosition);
glDisableVertexAttribArray(GLKVertexAttribColor);
glDisableVertexAttribArray(GLKVertexAttribNormal);

我无法理解的是为什么我必须手动执行此操作。我敢肯定在某些情况下,您需要的不仅仅是垂直于表面的矢量,但我也确信这是迄今为止最流行的用例,所以不应该有更简单的方法吗?我错过了什么明显的东西吗? glCalculateNormals() 会很棒。

//这是一个答案 传入一个您希望用法线填充的 GLKVector3[],另一个带有顶点(每三个被分组为多边形),然后是顶点的数量。

- (void) calculateSurfaceNormals: (GLKVector3 *) normals forVertices: (GLKVector3 *)incomingVertices count:(int) numOfVertices
{

    for(int i = 0; i < numOfVertices; i+=3)
    {
        GLKVector3 vector1 = GLKVector3Subtract(incomingVertices[i+1],incomingVertices[i]);
        GLKVector3 vector2 = GLKVector3Subtract(incomingVertices[i+2],incomingVertices[i]);        
        GLKVector3 normal = GLKVector3Normalize(GLKVector3CrossProduct(vector1, vector2));
        normals[i] = normal;
        normals[i+1] = normal;
        normals[i+2] = normal;
    }
}

【问题讨论】:

    标签: ios opengl-es ios5 geometry glkit


    【解决方案1】:

    答案又是:OpenGL 既不是场景管理库也不是几何库,而只是一个绘图 API,它可以在屏幕上绘制漂亮的图片。对于照明,它需要法线,你给它法线。就这样。如果这只能由用户完成,与实际绘图无关,为什么还要计算法线?

    通常您不会在运行时计算它们,而是从文件中加载它们。计算法线的方法有很多种。你想要每面法线还是每顶点法线?您是否需要任何特定的硬边或任何特定的平滑补丁?如果你想平均面法线得到顶点法线,你想如何平均这些?

    随着着色器的出现以及在较新的 OpenGL 版本中移除内置法线属性和光照计算,这整个问题无论如何都变得过时了,因为您可以以任何方式进行光照,并且不再需要传统的法线。

    顺便说一句,听起来您正在使用逐面法线,这意味着面的每个顶点都具有相同的法线。这会创建一个带有硬边的非常多面的模型,并且也不能很好地与索引一起使用。如果你想要一个平滑的模型(我不知道,也许你真的想要一个多面的外观),你应该平均每个顶点的相邻面的面法线来计算每个顶点的法线。这实际上是更常见的用例,而不是每个面的法线。

    所以你可以做这样的伪代码:

    for each vertex normal:
        intialize to zero vector
    
    for each face:
        compute face normal using cross product
        add face normal to each vertex normal of this face
    
    for each vertex normal:
        normalize
    

    生成平滑的逐顶点法线。即使在实际代码中,这也会导致 10 到 20 行代码,这并不复杂。

    【讨论】:

    • 我原以为我的情况是大多数情况,但如果不是通常的做法,那么我可以理解这是一项手工工作。我确实主要想要块,所以我可能会坚持使用我的系统。使用平均平均顶点法线方法,您描述了如何告诉 OpenGL 虽然我有 12 个多边形和 36 个顶点,但我只计算了 8 个法线?我是否需要一个包含 36 条法线的数组来匹配顶点并且以相同的顺序放置法线?这意味着当两个多边形共享一个顶点时,有些会重复,或者有没有办法链接法线和顶点?
    • @Craig 每个顶点总是有一个法线。如果您以您的方式(每个面)计算法线,则必须为面的每个顶点重复每个面法线。如果您计算每个顶点法线,则每个顶点法线都会针对顶点所属的每个面重复,就像实际上的顶点一样。从概念上讲,顶点是其所有属性的整体,而不仅仅是位置,不幸的是,在 OpenGL 中也称为顶点。
    • @Craig 但是每个顶点法线的优势(关于存储效率)是,您可以使用索引数组(和glDrawElements)并完全减少重复,因为每个唯一顶点都有一样正常。对于每个面的法线,索引不会给您带来任何好处,因为无论如何您都需要复制顶点,因为每个具有唯一位置的顶点可能有多个不同的法线。
    • @Craig 所以基本上你所说的就是这样做的方法,因为如果你有 36 个顶点,你也有 36 个顶点法线,而不仅仅是 8 个。怎么可能是 8 个,你甚至没有事先知道您只有 8 个独特的法线,因为您还没有计算任何法线。所以是的,普通数组(以及所有其他属性数组)总是匹配顶点数组,无论它包含哪些值。
    • 8 个法线用于立方体,其中有 8 个顶点和一个索引数组。在该索引数组中,它类似于 {0,1,2, 0,2,3) 表示每个多边形使用哪些顶点,一次三个。然后我会使用相同的索引数组来排序(并重复)八个法线,每个多边形上的每个顶点一个。那是如果我希望他们平均,我不希望。叉积非常适合我的积木。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 2013-11-05
    • 1970-01-01
    相关资源
    最近更新 更多