OpenGL:为什么我必须用glNormal设置正常?

我正在学习一些OpenGL的基础知识,但我想知道为什么有一个调用glNormal来设置顶点的法线

如果我创建这样一个简单的三角形:

 glBegin(GL_TRIANGLES); glVertex3f(0,0,0); glVertex3f(1,0,0); glVertex3f(0,1,0); glEnd(); 

法线不应该由几何原型的types来隐式定义吗? 如果我不设置一个正常的OpenGL会计算出来吗?

glBegin/End之间多次调用glNormal可以让您为每个顶点而不是每个基元设置一个法线。

 glBegin(GL_TRIANGLES); glNormal3f(0,0,1); glVertex3f(0,0,0); glNormal3f(0,0,1); glVertex3f(1,0,0); glNormal3f(0,0,1); glVertex3f(0,1,0); glEnd(); 

对于由多个三角形组成的更复杂的网格,您希望一个顶点的法线依赖于周围几何的其他三角形,而不仅仅是它所属的三角形的法线。

这是一个相同几何的例子:

  • 在左边的每个顶点法线上 – 所以每个顶点的面都有一个不同的法线(对于这个球体来说,这意味着所有的法线从它的中心向外),
  • 每个正面的法线上 – 每个顶点获得相同的正常值(因为您只指定一次,或因为它使用(0,0,1)的默认正常值):

每个顶点法线在左边,按面法线在右边

默认的阴影模型( GL_SMOOTH )会使脸部顶点的法线在脸部上插值。 对于每个顶点的法线,这会产生一个平滑的阴影球体,但是对于每个面的法线,您最终会看到具有特征的面。

不,GL不会为你计算一个规范。 它不知道什么是正常的。 你的原始语言并不意味着栅格化之外的任何东西(以及其他一些地方)。 GL不知道哪个面(三角形)共享一个顶点(在运行时计算它是非常昂贵的,而不使用不同的数据结构)。

您需要预处理网格的“三角汤”以find共同的顶点并计algorithm线。 这应该在游戏运行之前完成。

也有类似硬边的情况,几何上在角上有一个共享的顶点,但是您需要单独的法线,使其正确点亮。 在GL / D3D渲染模型中,为此需要两个单独的顶点(在相同的位置),每个顶点都有一个单独的法线。

你也需要这些UV和纹理映射。

简短的回答是,你根本不必设置一个正常的。 法线向量仅在您执行OpenGL光照时使用(或者可能取决于它们的某些其他计算),但是如果这不适用于您(或者您正在使用某种其他方法来进行光照,例如光照贴图)那么你可以很高兴地忽略所有的glNormal调用。

所以让我们假设你正在做一些指定glNormal有意义的事情,然后再看看它。

glNormal可以被指定为每个基元或每个顶点。 对于每个原始法线,它意味着原始中每个顶点的所有法线都面向相同的方向。 有时候这就是你想要的,但有时却不是这样 – 每个顶点法线的用处在于它允许每个顶点都拥有自己的面。

拿一个球体的经典例子。 如果一个球体中的每个三角形具有相同的法线,那么最终的结果将是相当多面的。 这可能不是你想要的,你需要一个平滑的阴影。 所以你所做的是平均每个共享顶点的三角形的法线,并得到平滑的阴影结果。

您需要glNormal来实现一些与编码器相关的特性。 例如,你可能希望创建法线数据保存硬边缘。

最小的例子 ,说明glNormal如何与弥漫闪电工作的一些细节。

displayfunction的注释解释每个三角形的含义。

在这里输入图像描述

 #include <stdlib.h> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h> /* Triangle on the xy plane. */ static void draw_triangle() { glBegin(GL_TRIANGLES); glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f); glEnd(); } /* A triangle tilted 45 degrees manually. */ static void draw_triangle_45() { glBegin(GL_TRIANGLES); glVertex3f( 0.0f, 1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f); glEnd(); } static void display(void) { glColor3f(1.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); /* Triangle perpendicular to the light. 0,0,1 also happens to be the default normal if we hadn't specified one. */ glNormal3f(0.0f, 0.0f, 1.0f); draw_triangle(); /* This triangle is as bright as the previous one. This is not photorealistic, where it should be less bright. */ glTranslatef(2.0f, 0.0f, 0.0f); draw_triangle_45(); /* Same as previous triangle, but with the normal set to the photorealistic value of 45, making it less bright. Note that the norm of this normal vector is not 1, but we are fine since we are using `glEnable(GL_NORMALIZE)`. */ glTranslatef(2.0f, 0.0f, 0.0f); glNormal3f(0.0f, 1.0f, 1.0f); draw_triangle_45(); /* This triangle is rotated 45 degrees with a glRotate. It should be as bright as the previous one, even though we set the normal to 0,0,1. So glRotate also affects the normal! */ glTranslatef(2.0f, 0.0f, 0.0f); glNormal3f(0.0, 0.0, 1.0); glRotatef(45.0, -1.0, 0.0, 0.0); draw_triangle(); glPopMatrix(); glFlush(); } static void init(void) { GLfloat light0_diffuse[] = {1.0, 1.0, 1.0, 1.0}; /* Plane wave coming from +z infinity. */ GLfloat light0_position[] = {0.0, 0.0, 1.0, 0.0}; glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_SMOOTH); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glColorMaterial(GL_FRONT, GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glEnable(GL_NORMALIZE); } static void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 7.0, -1.0, 1.0, -1.5, 1.5); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(800, 200); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return EXIT_SUCCESS; } 

理论

在OpenGL中,每个顶点都有自己关联的法线向量。

法向vector决定了顶点的亮度,然后用它来确定三角形的亮度。

当一个表面垂直于光线时,它比平行的表面更亮。

glNormal设置当前法向量,用于所有后续顶点。

在我们所有的glNormal之前,正常的初始值是0,0,1

法向量必须有标准1,否则颜色会改变! glScale也改变法线的长度! glEnable(GL_NORMALIZE); 使得OpenGL自动将它们的规范设置为1。 这个GIF很好地说明了这一点。

参考书目: