我听说我应该使用法线而不是颜色,因为颜色已被弃用。 (这是真的吗?)法线与光的反射有关,但我找不到清晰直观的解释。什么是正常的?
一般来说,normal是一个单位向量,其方向垂直于特定点的表面。因此,它会告诉您表面朝向哪个方向。法线的主要用例是光照计算,您必须确定给定曲面点处的法线与朝向光源或摄像机的方向之间的角度(或实际上通常是其余弦)。
glNormal
最小的例子
glNormal
是一个弃用的OpenGL 2方法,但它很容易理解,所以让我们来看看它。下面讨论现代着色器替代方案。
这个例子说明了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 2中,每个顶点都有自己的关联法向量。
法向量确定顶点的亮度,然后用于确定三角形的亮度。
OpenGL 2使用了Phong reflection model,其中光被分成三个部分:环境光,漫反射光和镜面光。其中,漫反射和镜面反射分量受法线影响:
qazxsw poi设置当前法线向量,用于所有后续顶点。
在我们所有glNormal
之前的正常的初始值是glNormal
。
法向量必须具有范数1,否则颜色会发生变化! 0,0,1
也改变了法线的长度! glScale
让OpenGL自动将他们的标准设置为1。 glEnable(GL_NORMALIZE);
说明了这一点。
为什么每个顶点而不是每个面都有法线是有用的
下面的两个球体都具有相同数量的多边形。顶点上具有法线的那个看起来更平滑。
OpenGL 4片段着色器
在较新的OpenGL API中,您将法线方向数据作为任意数据块传递给GPU:GPU不知道它代表法线。
然后你编写一个手写的片段着色器,这是一个在GPU中运行的任意程序,它读取你传递给它的普通数据,并实现你想要的任何闪电算法。如果您愿意,可以通过手动计算某些点积来有效地实现Phong。
这使您可以完全灵活地更改算法设计,这是现代GPU的主要功能。见:
这种情况的例子可以在任何“现代”OpenGL 4教程中找到,例如https://stackoverflow.com/a/36211337/895245
参考书目
现在很多东西都被弃用了,包括法线和颜色。这只意味着你必须自己实现它们。使用法线可以遮挡物体。这取决于你进行计算,但是有许多教程可以用来进行计算。 Gouraud / Phong阴影。
编辑:有两种类型的法线:面法线和顶点法线。面法线指向远离三角形的点,顶点法线指向远离顶点的点。使用顶点法线可以获得更好的质量,但是对于面法线也有很多用途,例如:它们可用于碰撞检测和阴影卷。