Что нормального в OpenGL? - PullRequest
       109

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

16 голосов
/ 06 мая 2011

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

Ответы [ 3 ]

21 голосов
/ 06 мая 2011

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

4 голосов

glNormal минимальный пример

glNormal является устаревшим методом OpenGL 2, но его легко понять, поэтому давайте рассмотрим его. Современная альтернатива шейдеров обсуждается ниже.

Этот пример иллюстрирует некоторые детали того, как glNormal работает с рассеянной молнией.

Комментарии функции display объясняют, что означает каждый треугольник.

4 red triangles

#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 использовалась модель отражения Фонга , в которой свет разделяется на три компонента: рассеянный, рассеянный и зеркальный. Из них на диффузные и зеркальные компоненты влияет нормаль:

  • Если рассеянный свет перпендикулярен поверхности, он становится ярче, независимо от того, где находится наблюдатель
  • если зеркальный свет попадает на поверхность и отражается прямо в глаз наблюдателя, эта точка становится более быстрой

glNormal устанавливает текущий вектор нормали, который используется для всех следующих вершин.

Начальное значение для нормали, прежде чем мы все glNormal будет 0,0,1.

Нормальные векторы должны иметь норму 1, иначе цвета меняются! glScale также изменяет длину нормалей! glEnable(GL_NORMALIZE); заставляет OpenGL автоматически устанавливать для них норму 1. Этот GIF прекрасно иллюстрирует это.

Почему полезно иметь нормали для вершин, а не для граней

Обе сферы ниже имеют одинаковое количество полигонов. Тот, у которого нормали на вершинах, выглядит намного плавнее.

enter image description here

Фрагментные шейдеры OpenGL 4

В более новых API OpenGL вы передаете данные о нормальном направлении в графический процессор в виде произвольного фрагмента данных: графический процессор не знает, что он представляет нормали.

Затем вы пишете рукописный фрагментный шейдер, который представляет собой произвольную программу, запускаемую в графическом процессоре, которая считывает обычные данные, которые вы передаете ему, и реализует любой алгоритм молнии, который вы хотите. Вы можете эффективно реализовать Phong, если захотите, вручную вычисляя некоторые точечные продукты.

Это дает вам полную гибкость при изменении структуры алгоритма, которая является основной особенностью современных графических процессоров. Смотри: https://stackoverflow.com/a/36211337/895245

Примеры этого можно найти в любом из "современных" учебных пособий по OpenGL 4, например, https://github.com/opengl-tutorials/ogl/blob/a9fe43fedef827240ce17c1c0f07e83e2680909a/tutorial08_basic_shading/StandardShading.fragmentshader#L42

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

3 голосов
/ 06 мая 2011

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

Редактировать: Есть два типа нормали: нормали лица и нормали вершины.Нормы грани указывают в сторону от треугольника, нормали вершины указывают в сторону от вершины.С вершинными нормалями вы можете добиться лучшего качества, но есть много применений и для нормалей лица, например, они могут использоваться для обнаружения столкновений и теней.

...