Что такое нормальный в OpenGL?

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

3 ответов


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


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

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


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

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

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

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

enter image description here

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

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

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

начальное значение для нормального до нас все!--1--> is 0,0,1.

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

почему полезно иметь нормали на вершины, а не на лица

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

enter image description here

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

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

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

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

примеры этого можно найти в любом из" современных " учебников OpenGL 4, например