Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm learning some basics of OpenGL but I'm wondering why there is a call glNormal to set the normal of vertices.

If I create a simple triangle like this:

glBegin(GL_TRIANGLES);
    glVertex3f(0,0,0);
    glVertex3f(1,0,0);
    glVertex3f(0,1,0);
glEnd();

Shouldn't the normals be defined implicitly by the type of the geometric primitive? If I don't set a normal will OpenGL calculate it?

share|improve this question
6  
I would just like to point out that this code is using deprecated fixed-function pipeline, and in modern OpenGL both normals and vertices are just generic vertex attributes. – Bartek Banachewicz Mar 8 '13 at 14:49
4  
Don't use immediate mode. It's deprecated. I highly recommend Learning Modern 3D Graphics Programming. – rightfold Mar 8 '13 at 14:52
2  
I think comment on use of immediate mode is completely irrelevant here. Yes, one shouldn't use it, but the point of the question remains the same irrespective of whether it's immediate mode, vertex arrays, VBO, generic attribs, fixed attribs, or bits of toast. – 21st Century Moose Mar 8 '13 at 15:21
1  
The reason it is not automatically determined is because it would make the lighting very polygony (for lack of a better word). What I mean by this is, all verticies on a triangle would have the same normal value. For flat surfaces this is what we would expect, however if you had a model of a persons face, each triangle on the face would have the same lighting value (making it very easy to see the polygons). By specifying your own normal values you can make things look much smoother (normally you find a normal by averaging the normal values of all triangles containing the current vertex). – Benjamin Danger Johnson Mar 8 '13 at 17:40
up vote 26 down vote accepted

Calling glNormal multiple times between glBegin/End allows you to set a normal per vertex instead of per primitive.

glBegin(GL_TRIANGLES);
    glNormal3f(0,0,1);
    glVertex3f(0,0,0);
    glNormal3f(0,0,1);
    glVertex3f(1,0,0);
    glNormal3f(0,0,1);
    glVertex3f(0,1,0);
glEnd();

For a more complex mesh consisting of multiple triangles you would want the normal at one vertex depend on the other triangles of the surrounding geometry and not just the normal of the triangle it belongs to.

Here is an example of the same geometry with:

  • on the left per-vertex normals - so each vertex of a face has a different normal (for this sphere it means all normals point outward from its center), and
  • on the right per-face normals - each vertex gets the same normal value (since you specify it only once, or because it uses the default normal value of (0,0,1)):

per-vertex normals on the left, per-face normals on the right

The default shade model (GL_SMOOTH) causes the normals of the face's vertices to be interpolated across the face. For the per-vertex normals this results in a smooth shaded sphere, but for the per-face normals you end up with the characteristic faceted look.

share|improve this answer

The short answer is that you don't actually have to set a normal at all. Normal vectors are only used if you're performing OpenGL lighting (or perhaps some other calculation that may depend on them), but if that doesn't apply to you (or if you're using some other method to do lighting, e.g. lightmaps) then you can quite happily omit all glNormal calls.

So let's assume that you are doing something where specifying glNormal makes sense and look at it some more.

glNormal can be specified per-primitive or per-vertex. With per-primitive normals all that it means is that all of the normals for each vertex in the primitive face in the same direction. Sometimes this is what you want but sometimes it isn't - where per-vertex normals come in useful is that they allow each vertex to have it's own facing.

Take the classic example of a sphere. If each triangle in a sphere had the same normal then the end result would be quite faceted. That's probably not what you want, you want a smooth shading instead. So what you do is average the normals for each triangle that shares a common vertex, and get a smooth shaded result.

share|improve this answer

No, GL does not calculate a norm for you. It cant't know what the normal should be. Your primitives don't mean anything outside of rasterization (and a few other places). GL doesn't know which faces (triangles) share a vertex (calculating this at runtime is very expensive without using different data structures).

You need to preprocess a mesh's "triangle soup" to find common vertices and calculate normals. This should be done before the game runs for speed.

There are also cases like hard edges where geometrically there is a shared vertex at the corner, but you want separate normals so it lights correctly. In the GL/D3D rendering model, you need two separate vertices (at the same position) for this, each with a separate normal.

You'll need all this for UVs and texture mapping, too.

share|improve this answer

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

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

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

Theory

In OpenGL each vertex has it's own associated normal vector.

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

When a surface is perpendicular to light, it is brighter than a parallel surface.

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.

Bibliography:

share|improve this answer
    
Nice answer, but why focusing on an old question and an old technology? – Alexandre Vaillancourt Mar 18 at 15:45
    
@AlexandreVaillancourt thanks! Old question: why not :-) Old technology: what's the better way today? – Ciro Santilli 巴拿馬文件 六四事件 法轮功 Mar 18 at 15:46
    
I think the modern OpenGL uses buffer objects instead of specifying glNormal. – Alexandre Vaillancourt Mar 18 at 15:49
1  
Indeed, glNormal and glVertex and the like are gone in modern OpenGL, and were deprecated some time before that... they were in fact deprecated even when this question was originally asked. – Josh Petrie Mar 18 at 16:38

You need the glNormal to implement some features coder-dependant. For instance you might wish to create normals dat preserve hard edges.

share|improve this answer
4  
Perhaps you'll want to improve your answer a bit. – Bartek Banachewicz Mar 8 '13 at 14:50
    
If you want me to improve my answer you would need to point out the weak spots. I can elaborate on it though. As xblax used to think, each normal "should" be calculated in the same way. Even despite the difference between interpolated normals for each pixel vs each face. Let's dwelve into the topic a bit more. – AB. Mar 11 '13 at 14:05
    
Sometimes you might wish to maintain the hard edge and make it more distinctive. Here are some sample images : titan.cs.ukzn.ac.za/opengl/opengl-d5/trant.sgi.com/opengl/… PS. Sorry for double posting. I am new to the SE interface – AB. Mar 11 '13 at 14:14
    
there's an "edit" button. – Bartek Banachewicz Mar 11 '13 at 15:04
    
You cannot edit after 5 minutes – AB. Mar 11 '13 at 22:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.