为 OpenGL Gouraud 着色提供带有三角形条和扇形的法线

Posted

技术标签:

【中文标题】为 OpenGL Gouraud 着色提供带有三角形条和扇形的法线【英文标题】:Providing normals with triangle strips and fans for OpenGL Gouraud shading 【发布时间】:2014-12-01 17:27:15 【问题描述】:

我是 Open GL 的新手,我对 Gouraud 着色有点困惑。我主要处理共面三角形。 我的理解是,在 Gouraud 着色中,我必须为三个顶点中的每一个找到法线,然后在推动顶点之前,我必须推动它的法线。

glNormal3d(nx1.x, nx1.y, nx1.z); glVertex3d(x1.x, x1.y, x1.z);

法线是通过平均相邻三角形的面法线获得的。 现在,假设我想画一个以 n 边形为底的圆锥。对于基础,我使用 triangle_fan 并且所有三角形都具有相同的法线,对吗?那么,我如何推动法线呢?我只计算一个法线并将其用于所有三角形?或者我无论如何都要计算每个顶点的法线?我也想对锥体的顶部使用 Gouraud 着色(这些三角形也定义为 triangle_fan),但似乎不起作用,为什么?我更改了单个三角形以便能够使用 Gouraud。

现在,我想创建一个圆柱体,将 n 边形作为顶面和底面。对于侧面,我使用 triangle_strips,对于底部,我使用三角形扇形。所以,侧面像:

*--------* |\ | | \ | | \ | | \ | | \ | | \ | | \ | | \| *--------*

现在,我有与上述相同的问题。对于基础中的 Gouraud 着色,使用单个法线是否足够?对于横向三角形的 Gourdaud 着色,两个三角形具有相同的法线,我应该对两者都使用相同的法线吗?

【问题讨论】:

【参考方案1】:

画一个圆锥有点复杂。顶部顶点的每个相邻三角形必须具有不同的法线。那是因为顶点没有它所属的明确定义的“表面”(无限小而平坦)。

将圆锥体想象成圆柱体,但顶部半径为 0。

所以要绘制一个圆锥体,您需要复制顶部顶点n 次,每次使用不同的法线,并且不能为此使用三角形扇形(因为每个三角形都有不同的顶部顶点) .

基本上,如果顶点不是连续曲面的一部分,并且圆锥的顶部不连续,则您需要复制顶点。另一方面,球体的表面是连续的,所以在这里你可以简单地平均面部的法线(就像你解释的那样)。立方体的角不是,每个角都需要用三个不同的法线绘制。 (只是给你一些其他的例子。)

【讨论】:

您好,谢谢您的回答。但是我仍然想知道当我有共面三角形时我必须做什么 把一对三角形想象成一个四边形/平面。他们共享相同的常态。但是您提供的是顶点法线,而不是面法线,因此每个角的法线向量等于两个相邻平面的法线的平均值。顺便说一下,这只是二维顶点位置的归一化向量(即指向远离圆柱轴的向量) 好的,谢谢。但是我可以在三角条上使用平面阴影吗?【参考方案2】:

@leemes 在之前的答案中介绍了特定的圆柱体外壳。但我想从你的问题中解决一个更普遍的问题:

法线是通过平均相邻三角形的面法线获得的。

这通常不是你真正应该做的。如果您的模型基于解析曲面,您需要计算 曲面的法线 以获得法线向量。解析曲面的典型示例包括:

几何形状,如球体、圆锥体、圆环体等。 样条曲面。

您将使用这些表面的三角剖分进行 OpenGL 渲染。这个三角剖分是表面的近似。如果您将法线计算为相邻三角形法线的平均值,则这些法线也是表面法线的近似值。除非您的曲面曲率很小,或者您使用非常精细的细分,否则这些近似法线远不如实际曲面法线。

这不仅仅是理论。以平均值计算的法线表面大多看起来有点粗糙。您可以尝试通过考虑三角形或相邻角度的大小来改进近似值以进行平均。但它始终是真实事物的近似值。

幸运的是,我们可以计算解析曲面的实际曲面法线。很多时候,这很容易。

例如,球体是微不足道的。法向量与从中心到顶点的归一化向量相同。您通常甚至不需要单独的法线向量。对于以原点为中心、半径为 1.0 的球体,顶点和法线具有完全相同的坐标。因此,您可以计算单位球体的顶点,将它们直接用作法线,并通过与所需位置和半径相匹配的平移/缩放来获得最终顶点。

对于您的圆锥示例,您还可以通过几张纸草图直观地得出法线。但是为了说明更通用的计算方式,让我们来计算一下。

许多表面是由基于两个参数的参数方程表示的。这可能就是您计算圆锥顶点的方式了。

取一个底面在 xy 平面上的圆锥,底边的半径为 r,高度为 h,顶端在 z 轴的正方向上。然后计算顶点为:

(a = 0..2*pi, t = 0..h)

          ( r * cos(a) * (h - t) / h )
f(a, t) = ( r * sin(a) * (h - t) / h )
          ( t                        )

对于这样的参数曲面,法线可以计算为两个梯度的叉积。先计算两个梯度向量:

        ( -r * sin(a) * (h - t) / h )
df/da = (  r * cos(a) * (h - t) / h )
        ( 0                         )

        ( -r * cos(a) / h )
df/dt = ( -r * sin(a) / h )
        ( 1               )

现在我们可以计算这两个向量的叉积:

( r * cos(a) * (h - t) / h  )
( r * sin(a) * (h - t) / h  )
( r * r * (h - t) / (h * h) )

由于无论如何我们都必须对这个向量进行归一化,我们可以去掉一些常见的因素,从而将其简化为:

( cos(a) )
( sin(a) )
( r / h  )

这很直观。它从中心向外指向与顶点相同的方向,并且在 z 方向上越多,半径越大,高度越小。

再稍加操作(我们仍然不关心大小),这可以写成:

( h * cos(a) )
( h * sin(a) )
( r          )

现在这需要标准化。长度变为:

sqrt(h * h + r * r)

方便的是所有顶点都相同。所以我们可以计算一次缩放因子为:

s = 1 / sqrt(h * h + r * r)

每个点的法向量计算为:

( s * h * cos(a) )
( s * h * sin(a) )
( s * r          )

这显然比圆柱体需要的复杂得多,但它应该有助于说明可应用于更复杂表面的一般过程。

只有当你只有一个网格,并且没有办法获得更多信息来计算好的法线时,才应该使用平均法线的方法。

【讨论】:

以上是关于为 OpenGL Gouraud 着色提供带有三角形条和扇形的法线的主要内容,如果未能解决你的问题,请参考以下文章

DirectX学习笔记:利用平面着色和Gouraud着色模式绘制具有颜色的三角形

OpenGL几何着色器三角形不会没有错误地绘制

在 gouraud shading 中,啥是 T-junction 问题以及如何用 OpenGL 演示它

带有着色器程序且没有错误的现代 OpenGL 黑屏

如何进行高卢着色? [关闭]

Python之OpenGL笔记(5):OpenGL着色器语言(GLSL)应用画三角形