What is a normal in OpenGL?

23,994

Solution 1

A normal in general is a unit vector whose direction is perpendicular to a surface at a specific point. Therefore it tells you in which direction a surface is facing. The main use case for normals are lighting calculations, where you have to determine the angle (or practically often its cosine) between the normal at a given surface point and the direction towards a lightsource or a camera.

Solution 2

glNormal minimal example

glNormal is a deprecated OpenGL 2 method, but it is simple to understand, so let's look into it. The modern shader alternative is discussed below.

This example illustrates some details of how glNormal works with diffuse lightning.

The comments of the display function explain what each triangle means.

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

Theory

In OpenGL 2 each vertex has its own associated normal vector.

The normal vector determines how bright the vertex is, which is then used to determine how bright the triangle is.

OpenGL 2 used the Phong reflection model, in which light is separated into three components: ambient, diffuse and specular. Of those, diffuse and specular components are affected by the normal:

  • if the diffuse light is perpendicular to the surface, it makes is brighter, no matter where the observer is
  • if the specular light hits the surface, and bounces off right into the eye of the observer, that point becomes brigher

glNormal sets the current normal vector, which is used for all following vertexes.

The initial value for the normal before we all glNormal is 0,0,1.

Normal vectors must have norm 1, or else colors change! glScale also alters the length of normals! glEnable(GL_NORMALIZE); makes OpenGL automatically set their norm to 1 for us. This GIF illustrates that beautifully.

Why it is useful to have normals per vertexes instead of per faces

Both spheres below have the same number of polygons. The one with normals on the vertexes looks much smoother.

enter image description here

OpenGL 4 fragment shaders

In newer OpenGL API's, you pass the normal direction data to the GPU as an arbitrary chunk of data: the GPU does not know that it represents the normals.

Then you write a hand-written fragment shader, which is an arbitrary program that runs in the GPU, which reads the normal data you pass to it, and implements whatever lightning algorithm you want. You can implement Phong efficiently if you feel like it, by manually calculating some dot products.

This gives you full flexibility to change the algorithm design, which is a major features of modern GPUs. See: https://stackoverflow.com/a/36211337/895245

Examples of this can be found in any of the "modern" OpenGL 4 tutorials, e.g. https://github.com/opengl-tutorials/ogl/blob/a9fe43fedef827240ce17c1c0f07e83e2680909a/tutorial08_basic_shading/StandardShading.fragmentshader#L42

Bibliography

Solution 3

Many things are now deprecated, including normals and colors. That just means that you have to implement them yourself. With normals you can shade your objects. It's up to you to make the calculations but there are a lot of tutorials on e.g. Gouraud/Phong shading.

Edit: There are two types of normals: face normals and vertex normals. Face normals point away from the triangle, vertex normals point away from the vertex. With vertex normals you can achieve better quality, but there are many uses also for face normals, e.g. they can be used in collision detection and shadow volumes.

Share:
23,994

Related videos on Youtube

mk.
Author by

mk.

Updated on July 09, 2022

Comments

  • mk.
    mk. almost 2 years

    I heard that I should use normals instead of colors, because colors are deprecated. (Is that true?) Normals have something to do with the reflection of light, but I can't find a clear and intuitive explanation. What is a normal?

  • mk.
    mk. almost 13 years
    This, like most of the information I've been able to find, tells me a bit more about what normals are used for, but I'm still not sure what a normal is. I'm surprised to hear that they're deprecated, but I wouldn't have any idea why, or how I'd implement them, since I'm not sure what they are.
  • Fil Karnicki
    Fil Karnicki almost 13 years
    Take a piece of paper and draw a line. Then draw second line that's perpendicular to the first. The second line is the first's normal. Now imagine that the first line is a triangle's surface. The first line's edges are vertices.
  • Fil Karnicki
    Fil Karnicki almost 13 years
    Normals are unit-length vectors. So a normal (1, 0, 0) points to the right. When you send vertices to be rendered, you can also send normals. Then you do the lighting calculation in the shader.
  • mk.
    mk. almost 13 years
    Thanks for the link. Why is a normal needed, if it should be obvious which direction a surface is facing? Do surfaces often not face perpendicular, or is this just a calculation that OpenGL doesn't want to handle?
  • Thies Heidecke
    Thies Heidecke almost 13 years
    Even if it's obvious where the surface is facing you still need to supply that information to be able to do lighting calculations, and normals are just the standard way of doing that. For simple flat shading of a triangular mesh you can easily compute the normals from the triangle vertices.
  • Thies Heidecke
    Thies Heidecke almost 13 years
    For more realistic shading, normals most often vary smoothly. This is often achieved by interpolating the normals across the surface, either by interpolating between face normals or vertex normals. Face normals are easy to obtain (like the flat shaded case), but hard to interpolate. Vertex normals are easy to interpolate (see Phong shading) but not so easy to calculate for a mesh. That's why vertex normals often are calculated beforehand and supplied with the mesh.
  • Andon M. Coleman
    Andon M. Coleman about 10 years
    Actually, there is no requirement that a normal vector be unit length. It is a handy property to have, but by the strictest definition a vector is a normal so long as it is perpendicular to a surface's tangent at a point. When a normal is unit length, it is a special form known as the "unit normal".
  • Andon M. Coleman
    Andon M. Coleman about 10 years
    @SurvivalMachine: No they're not. Unit normals are unit-length vectors, but normals are merely vectors that are perpendicular to a surface. Now, since the majority of applications of normals work best when they are unit-length, most of the time you encounter a normal it will be the unit normal.
  • Matth
    Matth about 8 years
    Nice explanation, however glNormal is part of the old OpenGL implementation and should be avoided.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com about 8 years
    @MatthUnderpants thanks for feedback, what is the better way of doing it now?
  • Matth
    Matth about 8 years
    It's by using the vertex shader and then passing the data to the fragment shader. I advice you to have a read at this awesome tutorial:www.learnopengl.com