Что нормально в OpenGL?

Я слышал, что вместо цветов нужно использовать нормали, потому что цвета устарели. (Это правда?) Нормали имеют какое-то отношение к отражению света, но я не могу найти ясного и интуитивного объяснения. Что такое нормальный?

Ответы

Ответ 1

A normal в общем случае является единичным вектором, направление которого перпендикулярно поверхности в определенной точке. Поэтому он сообщает вам, в каком направлении обращена поверхность. Основным прецедентом для нормалей являются подсчеты освещения, где вам необходимо определить угол (или практически часто его косинус) между нормалью в данной точке поверхности и направлением к источнику света или камере.

Ответ 2

Многие вещи теперь устарели, включая нормали и цвета. Это просто означает, что вы должны реализовать их самостоятельно. С нормалями вы можете затенять ваши объекты. Это зависит от вас, чтобы выполнить вычисления, но есть много учебных пособий, например. Gouraud/Phong shading.

Изменить: Существует два типа нормалей: нормали лица и нормали вершин. Линейные нормали указывают от треугольника, нормали вершин указывают в сторону от вершины. С вершинными нормалями вы можете достичь лучшего качества, но есть много применений также для нормалей лица, например. они могут использоваться при обнаружении конфликтов и теневых томах.

Ответ 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

Библиография