OpenGL 如何管理负法线?
OpenGL how to manage negative normals?
对于如下代码中定义的立方体,您会看到法线通常在一个轴上为负。 (即使我们计算它们)
OpenGL 使用其固定管道管理它,如果我错了请纠正我,但是使用可编程管道,它会导致像黑脸这样的伪像。 (我之前的提供了代码。)
我设法 运行 我的代码对我的法线进行了操作 (normal = (0.5 + 0.5 * normal); ),但即使结果看起来不错,我想知道我的法线是否仍然有效? (而且这个操作是最好的吗?)
我的意思是,从着色器的角度来看,我仍然可以使用它们来为我的模型着色或增亮吗?你平时怎么样?
提到的法线:
const GLfloat cube_vertices[] = {
1, 1, 1, -1, 1, 1, -1,-1, 1, // v0-v1-v2 (front)
-1,-1, 1, 1,-1, 1, 1, 1, 1, // v2-v3-v0
1, 1, 1, 1,-1, 1, 1,-1,-1, // v0-v3-v4 (right)
1,-1,-1, 1, 1,-1, 1, 1, 1, // v4-v5-v0
1, 1, 1, 1, 1,-1, -1, 1,-1, // v0-v5-v6 (top)
-1, 1,-1, -1, 1, 1, 1, 1, 1, // v6-v1-v0
-1, 1, 1, -1, 1,-1, -1,-1,-1, // v1-v6-v7 (left)
-1,-1,-1, -1,-1, 1, -1, 1, 1, // v7-v2-v1
-1,-1,-1, 1,-1,-1, 1,-1, 1, // v7-v4-v3 (bottom)
1,-1, 1, -1,-1, 1, -1,-1,-1, // v3-v2-v7
1,-1,-1, -1,-1,-1, -1, 1,-1, // v4-v7-v6 (back)
-1, 1,-1, 1, 1,-1, 1,-1,-1 }; // v6-v5-v4
const GLfloat cube_normalsI[] = {
0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2 (front)
0, 0, 1, 0, 0, 1, 0, 0, 1, // v2-v3-v0
1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4 (right)
1, 0, 0, 1, 0, 0, 1, 0, 0, // v4-v5-v0
0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6 (top)
0, 1, 0, 0, 1, 0, 0, 1, 0, // v6-v1-v0
-1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7 (left)
-1, 0, 0, -1, 0, 0, -1, 0, 0, // v7-v2-v1
0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3 (bottom)
0,-1, 0, 0,-1, 0, 0,-1, 0, // v3-v2-v7
0, 0,-1, 0, 0,-1, 0, 0,-1, // v4-v7-v6 (back)
0, 0,-1, 0, 0,-1, 0, 0,-1 }; // v6-v5-v4
不,这根本没有意义。要么你需要更新你的问题,要么你完全错了。
法线可能面向任何方向,法线通常在一个轴上为负值 是完全自然的。他们为什么不呢?根据您的描述,您似乎正在使用照明。一部分照明使用法线来查看光源与表面之间的角度。这里的想法是,当您转动法线时,光线有效地照亮了表面的大部分,从而降低了反射光的密度。通过基础数学,您可以看到相关性为 cos(angle)
,因此平行矢量将产生最高亮度。由于我们使用的是向量,因此我们最好将 cos
替换为点积。
所以在某些时候你有
float factor = dot(normalize(normal), normalize(lightSource-surfacePoint))
让我们在这里举两个例子:
normal = (0, 1, 0)
lightSource = (0, 1, 0)
surfacePoint = (0, 0, 0)
dot((0, 1, 0), (0, 1, 0)) = 0+1+0 = 1
然后转过来:
normal = (-1, 0, 0)
lightSource = (-3, 1, 0)
surfacePoint = (0, 1, 0)
dot((-1, 0, 0), normalize(-3, 0, 0)) = dot((-1, 0, 0), (1, 0, 0)) = 1+0+0 = 1
所以即使位置完全改变并且法线为负,对于相同的角度我们也会得到相同的结果(在这些情况下向量是垂直的)。
这里唯一的问题是当点积为负时怎么办。当法线背对着光线时,就会发生这种情况。在您的情况下,您有一个立方体,所有法线都指向外。如果你需要在一个立方体中并且仍然有照明怎么办?你会得到
normal = (0, 1, 0)
lightSource = (0, 0, 0)
surfacePoint = (0, 1, 0)
dot((0, 1, 0), (0, -1, 0)) = 0-1+0 = -1
由于这种情况,您需要保留值或使用绝对值。夹紧会使立方体的内部变黑(不亮),而绝对值也会使它们变亮:
fragmentColor += lightColor*dotFactor // Do nothing and your light will darken the area
fragmentColor += lightColor*abs(dotFactor) // Use absolute value to lighten even if facing away
fragmentColor += lightColor*max(0.0, dotFactor) // Clamp minimum so there are no negative values.
但其中none与绝对坐标系中面向任何方向的法线无关。它只是与法线、像素位置和光源之间的相对位置有关。
对于如下代码中定义的立方体,您会看到法线通常在一个轴上为负。 (即使我们计算它们)
OpenGL 使用其固定管道管理它,如果我错了请纠正我,但是使用可编程管道,它会导致像黑脸这样的伪像。 (我之前的
我设法 运行 我的代码对我的法线进行了操作 (normal = (0.5 + 0.5 * normal); ),但即使结果看起来不错,我想知道我的法线是否仍然有效? (而且这个操作是最好的吗?)
我的意思是,从着色器的角度来看,我仍然可以使用它们来为我的模型着色或增亮吗?你平时怎么样?
提到的法线:
const GLfloat cube_vertices[] = {
1, 1, 1, -1, 1, 1, -1,-1, 1, // v0-v1-v2 (front)
-1,-1, 1, 1,-1, 1, 1, 1, 1, // v2-v3-v0
1, 1, 1, 1,-1, 1, 1,-1,-1, // v0-v3-v4 (right)
1,-1,-1, 1, 1,-1, 1, 1, 1, // v4-v5-v0
1, 1, 1, 1, 1,-1, -1, 1,-1, // v0-v5-v6 (top)
-1, 1,-1, -1, 1, 1, 1, 1, 1, // v6-v1-v0
-1, 1, 1, -1, 1,-1, -1,-1,-1, // v1-v6-v7 (left)
-1,-1,-1, -1,-1, 1, -1, 1, 1, // v7-v2-v1
-1,-1,-1, 1,-1,-1, 1,-1, 1, // v7-v4-v3 (bottom)
1,-1, 1, -1,-1, 1, -1,-1,-1, // v3-v2-v7
1,-1,-1, -1,-1,-1, -1, 1,-1, // v4-v7-v6 (back)
-1, 1,-1, 1, 1,-1, 1,-1,-1 }; // v6-v5-v4
const GLfloat cube_normalsI[] = {
0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2 (front)
0, 0, 1, 0, 0, 1, 0, 0, 1, // v2-v3-v0
1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4 (right)
1, 0, 0, 1, 0, 0, 1, 0, 0, // v4-v5-v0
0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6 (top)
0, 1, 0, 0, 1, 0, 0, 1, 0, // v6-v1-v0
-1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7 (left)
-1, 0, 0, -1, 0, 0, -1, 0, 0, // v7-v2-v1
0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3 (bottom)
0,-1, 0, 0,-1, 0, 0,-1, 0, // v3-v2-v7
0, 0,-1, 0, 0,-1, 0, 0,-1, // v4-v7-v6 (back)
0, 0,-1, 0, 0,-1, 0, 0,-1 }; // v6-v5-v4
不,这根本没有意义。要么你需要更新你的问题,要么你完全错了。
法线可能面向任何方向,法线通常在一个轴上为负值 是完全自然的。他们为什么不呢?根据您的描述,您似乎正在使用照明。一部分照明使用法线来查看光源与表面之间的角度。这里的想法是,当您转动法线时,光线有效地照亮了表面的大部分,从而降低了反射光的密度。通过基础数学,您可以看到相关性为 cos(angle)
,因此平行矢量将产生最高亮度。由于我们使用的是向量,因此我们最好将 cos
替换为点积。
所以在某些时候你有
float factor = dot(normalize(normal), normalize(lightSource-surfacePoint))
让我们在这里举两个例子:
normal = (0, 1, 0)
lightSource = (0, 1, 0)
surfacePoint = (0, 0, 0)
dot((0, 1, 0), (0, 1, 0)) = 0+1+0 = 1
然后转过来:
normal = (-1, 0, 0)
lightSource = (-3, 1, 0)
surfacePoint = (0, 1, 0)
dot((-1, 0, 0), normalize(-3, 0, 0)) = dot((-1, 0, 0), (1, 0, 0)) = 1+0+0 = 1
所以即使位置完全改变并且法线为负,对于相同的角度我们也会得到相同的结果(在这些情况下向量是垂直的)。
这里唯一的问题是当点积为负时怎么办。当法线背对着光线时,就会发生这种情况。在您的情况下,您有一个立方体,所有法线都指向外。如果你需要在一个立方体中并且仍然有照明怎么办?你会得到
normal = (0, 1, 0)
lightSource = (0, 0, 0)
surfacePoint = (0, 1, 0)
dot((0, 1, 0), (0, -1, 0)) = 0-1+0 = -1
由于这种情况,您需要保留值或使用绝对值。夹紧会使立方体的内部变黑(不亮),而绝对值也会使它们变亮:
fragmentColor += lightColor*dotFactor // Do nothing and your light will darken the area
fragmentColor += lightColor*abs(dotFactor) // Use absolute value to lighten even if facing away
fragmentColor += lightColor*max(0.0, dotFactor) // Clamp minimum so there are no negative values.
但其中none与绝对坐标系中面向任何方向的法线无关。它只是与法线、像素位置和光源之间的相对位置有关。