2011-05-06 66 views
14

Escuché que debería usar normales en lugar de colores, porque los colores están en desuso. (¿Es eso verdad?) Los normales tienen algo que ver con el reflejo de la luz, pero no puedo encontrar una explicación clara e intuitiva. ¿Qué es una normal?¿Qué es una normal en OpenGL?

Respuesta

17

A normal en general es un vector unitario cuya dirección es perpendicular a una superficie en un punto específico. Por lo tanto, te dice en qué dirección se enfrenta una superficie. El caso de uso principal para las personas normales son los cálculos de iluminación, donde debe determinar el ángulo (o prácticamente a menudo su coseno) entre la normal en un punto superficial dado y la dirección hacia una fuente de luz o una cámara.

+0

Gracias por el enlace. ¿Por qué se necesita una normal, si debería ser obvio a qué dirección se enfrenta una superficie? ¿Las superficies a menudo no se ven perpendiculares, o es solo un cálculo que OpenGL no quiere manejar? –

+3

Incluso si es obvio dónde se encuentra la superficie, aún necesita proporcionar esa información para poder hacer cálculos de iluminación, y las normales son la forma estándar de hacerlo.Para un simple sombreado plano de una malla triangular, puede calcular fácilmente las normales a partir de los vértices del triángulo. –

+1

Para obtener sombreados más realistas, las normales suelen variar suavemente. Esto a menudo se logra al interpolar las normales a través de la superficie, ya sea mediante la interpolación entre las normales de la cara o las normales de los vértices. Las normales de cara son fáciles de obtener (como la caja sombreada plana), pero difíciles de interpolar. Las normales de los vértices son fáciles de interpolar (ver [sombreado de Phong] (http://en.wikipedia.org/wiki/Phong_shading)) pero no tan fáciles de calcular para una malla. Es por eso que las normales de vértice a menudo se calculan de antemano y se suministran con la malla. –

3

Muchas cosas ahora están en desuso, incluidas las normales y los colores. Eso solo significa que tienes que implementarlos tú mismo. Con las normales puedes sombrear tus objetos. Depende de usted hacer los cálculos, pero hay muchos tutoriales sobre, por ejemplo, Gouraud/Phong sombreado.

Editar: Hay dos tipos de normales: normales de cara y normales de vértice. Las normales de la cara apuntan lejos del triángulo, las normales de los vértices apuntan hacia el vértice. Con las normales de los vértices puede lograr una mejor calidad, pero también hay muchos usos para los normales de las caras, p. se pueden usar en detección de colisiones y volúmenes ocultos.

+0

Este, al igual que la mayor parte de la información que he podido encontrar, me dice una un poco más acerca de para qué se usan las normales, pero todavía no estoy seguro de qué es * normal *. Me sorprende escuchar que están en desuso, pero no sabría por qué o cómo los implementaría, ya que no estoy seguro de cuáles son. –

+3

Toma una hoja de papel y traza una línea. Luego dibuja la segunda línea que es perpendicular a la primera. La segunda línea es la primera normal. Ahora imagina que la primera línea es la superficie de un triángulo. Los bordes de la primera línea son vértices. – SurvivalMachine

+4

Las normas son vectores de longitud de unidad. Entonces, un normal (1, 0, 0) apunta hacia la derecha. Cuando envía vértices para representar, también puede enviar normales. Luego haces el cálculo de iluminación en el sombreador. – SurvivalMachine

3

glNormal mínima ejemplo

glNormal es un método OpenGL 2 obsoleto, pero es fácil de entender, por lo que vamos a ver en ella. La alternativa del sombreador moderno se analiza a continuación.

Este ejemplo ilustra algunos detalles de cómo glNormal trabaja con un rayo difuso.

Los comentarios de la función display explican el significado de cada triángulo.

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; 
} 

Teoría

En OpenGL 2 cada vértice tiene su propio vector normal asociado.

El vector normal determina cómo brillante el vértice es, que luego se utiliza para determinar cómo brillante del triángulo es.

OpenGL 2 utiliza la Phong reflection model, en el que la luz se separa en tres componentes: ambiente, difusas y especulares. De ellos, los componentes difusas y especulares se ven afectados por la normalidad:

  • si la luz difusa es perpendicular a la superficie, que hace es más brillante, sin importar donde el observador es
  • si la luz especular llega a la superficie , y rebota de la derecha en el ojo del observador, que se convierte en el punto brigher

glNormal establece el vector normal actual, que se utiliza para todos los siguientes vértices.

El valor inicial para la normalidad antes de todos glNormal es 0,0,1.

vectores normales mosto tienen norma 1, o bien cambian de color! glScale ¡también altera la duración de las normales! glEnable(GL_NORMALIZE); hace que OpenGL establezca automáticamente su norma en 1 para nosotros. This GIF ilustra eso maravillosamente.

Por qué es útil tener normales por vértices en lugar de por caras

Ambas esferas a continuación tienen el mismo número de polígonos. El que tiene normales en los vértices se ve mucho más suave.

enter image description here

OpenGL 4 fragment shaders

En la API de OpenGL más nuevos, que pasan los datos normales de dirección para la GPU como un trozo arbitraria de datos: la GPU no sabe que representa las normales .

Luego escribe un sombreador de fragmentos manuscrito, que es un programa arbitrario que se ejecuta en la GPU, que lee los datos normales que le pasa, e implementa el algoritmo de iluminación que desee. Puede implementar Phong de manera eficiente si así lo desea, calculando manualmente algunos productos de puntos.

Esto le da total flexibilidad para cambiar el diseño del algoritmo, que es una de las principales características de las GPU modernas. Ver: https://stackoverflow.com/a/36211337/895245

Ejemplos de esto se pueden encontrar en cualquiera de los tutoriales "modernos" de OpenGL 4, p. https://github.com/opengl-tutorials/ogl/blob/a9fe43fedef827240ce17c1c0f07e83e2680909a/tutorial08_basic_shading/StandardShading.fragmentshader#L42

Bibliografía

+0

Buena explicación, sin embargo glNormal es parte de la implementación anterior de OpenGL y debe evitarse. – Matth

+1

@MatthUnderpants gracias por sus comentarios, ¿cuál es la mejor manera de hacerlo ahora? –

+0

Es mediante el uso del sombreador de vértices y luego pasar los datos al sombreador de fragmentos. Te aconsejo que leas en este fantástico tutorial: www.learnopengl.com – Matth

Cuestiones relacionadas