Я слышал, что вместо цветов нужно использовать нормали, потому что цвета устарели. (Это правда?) Нормали имеют какое-то отношение к отражению света, но я не могу найти ясного и интуитивного объяснения. Что такое нормальный?
Ответ 3
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, в которой свет разделен на три компонента: окружающий, диффузный и зеркальный. Из них на диффузные и зеркальные компоненты влияет нормальная:
- Если диффузный свет перпендикулярен поверхности, он становится ярче, независимо от того, где наблюдатель
- если зеркальный свет попадает на поверхность и отскакивает прямо в глаз наблюдателя, эта точка становится более сильной.
glNormal
устанавливает текущий вектор нормалей, который используется для всех следующих вершин.
Начальное значение для нормали до того, как все glNormal
будет 0,0,1
.
Нормальные векторы должны иметь норма 1, иначе цвета меняются! glScale
также изменяет длину нормалей! glEnable(GL_NORMALIZE);
делает OpenGL автоматически устанавливает для них норму 1. Этот GIF прекрасно иллюстрирует это.
Почему полезно иметь нормали на вершины вместо граней
Обе сферы внизу имеют одинаковое количество полигонов. Тот, у которого нормали на вершинах выглядят намного более гладкими.
![введите описание изображения здесь]()
шейдеры OpenGL 4
В новых API OpenGL вы передаете данные нормального направления на графический процессор как произвольный фрагмент данных: графический процессор не знает, что он представляет нормали.
Затем вы пишете рукописный фрагмент-шейдер, который представляет собой произвольную программу, которая запускается в графическом процессоре, которая считывает обычные данные, которые вы передаете ей, и реализует любой алгоритм молнии, который вы хотите. Вы можете эффективно использовать Phong, если вам так хочется, вручную вычислив некоторые точечные продукты.
Это дает вам полную гибкость для изменения дизайна алгоритма, который является основным элементом современных графических процессоров. См.: fooobar.com/info/84613/...
Примеры этого можно найти в любом из "современных" руководств OpenGL 4, например. https://github.com/opengl-tutorials/ogl/blob/a9fe43fedef827240ce17c1c0f07e83e2680909a/tutorial08_basic_shading/StandardShading.fragmentshader#L42
Библиография