我正在学习OpenGL的一些基础知识,但我想知道为什么要调用glNormal
一个设置顶点法线的方法。
如果我这样创建一个简单的三角形:
glBegin(GL_TRIANGLES);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(0,1,0);
glEnd();
法线不应该由几何图元的类型隐式定义吗?如果我不设置法线,OpenGL将对其进行计算吗?
我正在学习OpenGL的一些基础知识,但我想知道为什么要调用glNormal
一个设置顶点法线的方法。
如果我这样创建一个简单的三角形:
glBegin(GL_TRIANGLES);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(0,1,0);
glEnd();
法线不应该由几何图元的类型隐式定义吗?如果我不设置法线,OpenGL将对其进行计算吗?
Answers:
glNormal
两次之间调用glBegin/End
可以使您为每个顶点而不是每个图元设置法线。
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
)使脸部顶点的法线在脸部内插。对于每个顶点法线,这会生成一个平滑的阴影球体,但是对于每个面法线,您最终会得到特征性的多面外观。
简短的回答是,你实际上并不具备设置正常的。仅在执行OpenGL照明(或可能依赖于它们的某些其他计算)时才使用法向矢量,但如果该方法不适用于您(或在使用其他方法进行照明,例如,光照贴图) ),那么您可以很高兴地忽略所有glNormal调用。
因此,让我们假设您正在做一些指定glNormal有意义的事情,然后再进行一些研究。
glNormal可以按每个图元或每个顶点指定。对于每个基本体法线,这意味着基本面中每个顶点的所有法线都朝向同一方向。有时这就是您想要的,但有时却不是您想要的-每个顶点法线有用的地方是它们允许每个顶点都有自己的面对。
以球体的经典示例为例。如果球体中的每个三角形具有相同的法线,则最终结果将是非常多方面的。那可能不是您想要的,而是想要一个平滑的阴影。因此,您要做的是平均每个共享相同顶点的三角形的法线,并获得平滑的阴影结果。
最小的示例阐释了如何glNormal
使用漫反射闪电的一些细节。
该display
函数的注释解释了每个三角形的含义。
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
/* Triangle on the x-y 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中,每个顶点都有自己的关联法线向量。
法线向量确定顶点的亮度,然后将其用于确定三角形的亮度。
当表面垂直于光时,它比平行表面更亮。
glNormal
设置当前法向向量,该向量将用于以下所有顶点。
我们所有人之前的法线初始值glNormal
是0,0,1
。
法线向量必须具有范数1,否则颜色会改变!glScale
也会改变法线的长度!glEnable(GL_NORMALIZE);
使OpenGL自动为我们将其范数设置为1。此GIF很好地说明了这一点。
参考书目:
您需要glNormal来实现某些依赖于编码器的功能。例如,您可能希望创建法线以保留硬边。