1

I have setup the lighting to the best of my understanding but all that renders is Ambient lighting. It seems to work on built-in shapes like glutSolidSphere and glutSolidCube, but not my manually coded polygons.

I have spent hours reviewing tutorials and StackOverflow but haven't been able to resolve it on my own.

What am I missing? I am using Netbeans (Windows), C++, FreeGlut.

image: 3D Airplane

#include <GL/glut.h>
#include <GL/glext.h>
​
GLfloat pos0 [] = {0,1,0,0},
        white[] = {1,1,1};
​
void render(){
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glNormal3f(0,1,0);
  glScaled(4,4,4);
  glRotated(30,1,0,0);
  glRotated(60,0,1,0);
  glRotated(10,0,0,-1);
  glTranslated(0,0,-.2);
  glBegin(GL_QUADS);
    #define V glVertex3f
    // body
    glColor3f(.7,.5,.3);
    V(0,   0, 0); V(  1,.06,.05); V(  1,.10,.05); V(0, .15, 0);
    V(0,   0, 0); V(  1,.06,.05); V(  1,.05,.10); V(0,-.03,.1);
    V(0,-.03,.1); V(  1,.05,.10); V(  1,.06,.15); V(0,   0,.2);
    V(0,   0,.2); V(  1,.06,.15); V(  1,.10,.15); V(0, .15,.2);
    V(0, .15,.2); V(  1,.10,.15); V(  1,.11,.10); V(0, .17,.1);
    V(0, .17,.1); V(  1,.11,.10); V(  1,.10,.05); V(0, .15, 0);
    V(0,   0, 0); V(-.5,.03,.07); V(-.5,.06,.07); V(0, .15, 0);
    V(0,   0, 0); V(-.5,.03,.07); V(-.5,.02,.10); V(0,-.03,.1);
    V(0,-.03,.1); V(-.5,.02,.10); V(-.5,.03,.13); V(0,   0,.2);
    V(0,   0,.2); V(-.5,.03,.13); V(-.5,.06,.13); V(0, .15,.2);
    V(0, .15,.2); V(-.5,.06,.13); V(-.5,.07,.10); V(0, .17,.1);
    V(0, .17,.1); V(-.5,.07,.10); V(-.5,.06,.07); V(0, .15, 0);
    // wings
    glColor3f(.5,.5,.5);
    V(-.1,.06, .1); V(.3,.06, .1); V(.4,.10, .7); V( .2,.10, .7);
    V(-.1,.10, .1); V(.3,.10, .1); V(.4,.11, .7); V( .2,.11, .7);
    V(-.1,.06, .1); V(.2,.10, .7); V(.2,.11, .7); V(-.1,.10, .1);
    V( .3,.06, .1); V(.4,.10, .7); V(.3,.10, .1); V( .4,.11, .7);
    V( .4,.10, .7); V(.2,.10, .7); V(.4,.11, .7); V( .2,.11, .7);
    V(-.1,.06, .1); V(.3,.06, .1); V(.4,.10,-.5); V( .2,.10,-.5);
    V(-.1,.10, .1); V(.3,.10, .1); V(.4,.11,-.5); V( .2,.11,-.5);
    V(-.1,.06, .1); V(.2,.10,-.5); V(.2,.11,-.5); V(-.1,.10, .1);
    V( .3,.06, .1); V(.4,.10,-.5); V(.3,.10, .1); V( .4,.11,-.5);
    V( .4,.10,-.5); V(.2,.10,-.5); V(.4,.11,-.5); V( .2,.11,-.5);
    // tail
    V(.6,.06, .10); V(.8,.06, .10); V(.9,.10, .40); V(.8,.10, .40);
    V(.6,.10, .10); V(.8,.10, .10); V(.9,.11, .40); V(.8,.11, .40);
    V(.6,.06, .10); V(.8,.10, .40); V(.8,.11, .40); V(.6,.10, .10);
    V(.8,.06, .10); V(.9,.10, .40); V(.9,.11, .40); V(.8,.10, .10);
    V(.9,.10, .40); V(.8,.10, .40); V(.8,.11, .40); V(.9,.11, .40);
    V(.6,.06, .10); V(.8,.06, .10); V(.9,.10,-.20); V(.8,.10,-.20);
    V(.6,.10, .10); V(.8,.10, .10); V(.9,.11,-.20); V(.8,.11,-.20);
    V(.6,.06, .10); V(.8,.10,-.20); V(.8,.11,-.20); V(.6,.10, .10);
    V(.8,.06, .10); V(.9,.10,-.20); V(.9,.11,-.20); V(.8,.10, .10);
    V(.9,.10,-.20); V(.8,.10,-.20); V(.8,.11,-.20); V(.9,.11,-.20);
    V(.7,.10, .09); V(.9,.10, .09); V( 1,.30, .09); V(.9,.30, .09);
    V(.7,.10, .11); V(.9,.10, .11); V( 1,.30, .11); V(.9,.30, .11);
    V(.7,.10, .09); V(.9,.30, .09); V(.9,.10, .11); V(.7,.10, .11);
    V(.9,.10, .09); V( 1,.30, .09); V( 1,.30, .11); V(.9,.10, .11);
    V( 1,.30, .09); V(.9,.30, .09); V(.9,.30, .11); V( 1,.30, .11);
  glEnd();
  glFlush();
}
int main(int argc,char** argv){
  // setup window
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH|GLUT_MULTISAMPLE);
  glutInitWindowSize(800,600);
  glutCreateWindow("3D Airplane");
  glClearColor(0,0,0,1);

  // setup perspective
  glMatrixMode(GL_PROJECTION);
  glFrustum(-2,2, -1.5,1.5, 3,25);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0,0,4, 0,0,0, 0,1,0);
  glViewport(0,0,800,600);
  glEnable(GL_DEPTH_TEST);

  // setup lighting
  glLightfv(GL_LIGHT0,GL_POSITION,pos0 );
  glLightfv(GL_LIGHT0,GL_DIFFUSE ,white);
  glEnable (GL_LIGHT0);
  glEnable (GL_LIGHTING);
  glEnable (GL_COLOR_MATERIAL);

  glutDisplayFunc(render);
  glutMainLoop();
}

EDIT: I have written a normal calculation function called calcNormal, and converted my glVertex calls to a quadrilateral array of vertices...

GLfloat* calcNormal(GLfloat* a,GLfloat* b,GLfloat* c){
  // define target plane
  GLfloat u[] = {b[0]-a[0], b[1]-a[1], b[2]-a[2]};
  GLfloat v[] = {c[0]-a[0], c[1]-a[1], c[2]-a[2]};
  // calculate cross product
  static GLfloat cp[] = { u[1]*v[2] - u[2]*v[1], 
                          u[2]*v[0] - u[0]*v[2], 
                          u[0]*v[1] - u[1]*v[0] };
  // normalize
  GLfloat length = sqrt(cp[0]*cp[0] + cp[1]*cp[1] + cp[2]*cp[2]);
  for(int i=0;i<3;i++) cp[i] /= length;
  return cp;
}
render(){
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glColor3f(.7,.5,.3);
  glPushMatrix();
  glScaled(3,3,3);
  glRotated(30,1,0,0);
  glRotated(60,0,1,0);
  glRotated(10,0,0,-1);
  glTranslatef(x,y,z);

  for(GLint* quad:planeQuads){
    #define PV planeVertices
    glNormal3fv(calcNormal(PV[quad[0]],PV[quad[1]],PV[quad[2]]));
    glBegin(GL_QUADS);
      glVertex3fv(PV[quad[0]]);
      glVertex3fv(PV[quad[1]]);
      glVertex3fv(PV[quad[2]]);
      glVertex3fv(PV[quad[3]]);
    glEnd();
  }
  glPopMatrix();
  glFlush();
}

...but it still isn't rendering diffuse light, even though I am now setting glNormal for each poly.

My poly/vertex structure now looks like this:

// polygon arrays
GLint planeQuads[37][4] = {
  { 9,15,17,11}, { 9,15,14, 8}, { 8,14,16,10}, {10,16,18,12}, {12,18,19,13}, {13,19,17,11}, // rear body
  { 9, 1, 3,11}, { 9, 1, 0, 8}, { 8, 0, 2,10}, {10, 2, 4,12}, {12, 4, 5,13}, {13, 5, 3,11}, // front body
  { 6,17,20,14}, { 7,19,22,16}, { 6,14,16, 7}, {17,20,19,22}, {20,14,22,16}, // left wing
  { 6,17,23,18}, { 7,19,21,15}, { 6,18,17, 7}, {17,23,19,21}, {23,18,21,15}, // right wing
  {24,28,37,31}, {25,30,39,33}, {24,31,33,25}, {28,37,39,30}, {37,31,33,39}, // left fin
  {24,28,34,29}, {25,30,38,32}, {24,29,32,25}, {28,34,38,30}, {34,29,32,38}, // right fin
  {26,35,48,40}, {27,36,49,41}, {26,40,36,27}, {35,48,49,36}, {48,40,41,49}  // tail
};

// vertex arrays
GLfloat planeVertices[50][3] = {
  {-.5,0.02,0.10}, {-.5,0.03,0.07}, {-.5,0.03,0.13}, {-.5,0.06,0.07}, {-.5,0.06,0.13},
  {-.5,0.07,0.10}, {-.1,0.06,0.10}, {-.1,0.10,0.10}, {0.0,-.03,0.10}, {0.0,0.00,0.00},
  {0.0,0.00,0.20}, {0.0,0.15,0.00}, {0.0,0.15,0.20}, {0.0,0.17,0.10}, {0.2,0.10,-.50},
  {0.2,0.10,0.70}, {0.2,0.11,-.50}, {0.2,0.11,0.70}, {0.3,0.06,0.10}, {0.3,0.10,0.10},
  {0.4,0.10,-.50}, {0.4,0.10,0.70}, {0.4,0.11,-.50}, {0.4,0.11,0.70}, {0.6,0.06,0.10},
  {0.6,0.10,0.10}, {0.7,0.10,0.09}, {0.7,0.10,0.11}, {0.8,0.06,0.10}, {0.8,0.10,-.20},
  {0.8,0.10,0.10}, {0.8,0.10,0.40}, {0.8,0.11,-.20}, {0.8,0.11,0.40}, {0.9,0.10,-.20},
  {0.9,0.10,0.09}, {0.9,0.10,0.11}, {0.9,0.10,0.40}, {0.9,0.11,-.20}, {0.9,0.11,0.40},
  {0.9,0.30,0.09}, {0.9,0.30,0.11}, {1.0,0.05,0.10}, {1.0,0.06,0.05}, {1.0,0.06,0.15},
  {1.0,0.10,0.05}, {1.0,0.10,0.15}, {1.0,0.11,0.10}, {1.0,0.30,0.09}, {1.0,0.30,0.11}
};
Jonah M
  • 300
  • 2
  • 13
  • 1
    You've to set normal vectors, which are perpendicular to the faces (or even vertices) of the geometry. So you've to set a normal vector for each face or vertex coordinate. – Rabbid76 Sep 12 '19 at 04:56

1 Answers1

1

You can see that your setup works in general, if you change the one and only normal vector form glNormal3f(0,1,0) to glNormal3f(0,-1,0), which makes the scene darker.

But, you've to set normal vectors, which are perpendicular to the faces (or even vertices) of the geometry. So you've to set a normal vector for each face or vertex coordinate.

The diffuse light is calculated by the the Dot product of the light vector and the normal vector attribute of the mesh. For a directional light, the light vector is the same for the entire scene. If the normal vector is constant for the entire geometry, then the result of the dot product is constant for the entire geometry and thus the light, too.
See also GLSL fixed function fragment program replacement


In your code there is a basic mistake. A static variable is initialized only once:

static GLfloat cp[] = { u[1]*v[2] - u[2]*v[1], 
                       u[2]*v[0] - u[0]*v[2], 
                       u[0]*v[1] - u[1]*v[0] };

You've to assign the variable every time when you call the function:

static GLfloat cp[3];
cp[0] = u[1]*v[2] - u[2]*v[1]; 
cp[1] = u[2]*v[0] - u[0]*v[2]; 
cp[2] = u[0]*v[1] - u[1]*v[0];  

When you calculate the normal vector, then you've to ensure, that the normal vector points out of the mesh. Invert the normal vector to see how the direction of the vector effects the light.

GLfloat* calcNormal(GLfloat* a,GLfloat* b,GLfloat* c){
    // define target plane
    GLfloat u[] = {b[0]-a[0], b[1]-a[1], b[2]-a[2]};
    GLfloat v[] = {c[0]-a[0], c[1]-a[1], c[2]-a[2]};
    // calculate cross product
    static GLfloat cp[3];
    cp[0] = u[1]*v[2] - u[2]*v[1]; 
    cp[1] = u[2]*v[0] - u[0]*v[2]; 
    cp[2] = u[0]*v[1] - u[1]*v[0];
    // normalize
    GLfloat length = sqrt(cp[0]*cp[0] + cp[1]*cp[1] + cp[2]*cp[2]);
    for(int i=0;i<3;i++) cp[i] /= -length; // <- invert the normal vector
    return cp;
}

Since the normal vectors are calculated by the cross product of 2 sides of the triangular primitives, the direction of the normal vector depends on the winding order of the primitives. This means the winding order of all the primitives has to be the same.


If the winding order varies, this means some primitives are clockwise and others are counter-clockwise, then you can try to compensate this by setting (see glLightModel)

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

In this case the material parameters for front and back faces have to be equal. (See glMaterial).

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • I just reworked my program to set normal vectors for each face, but it still isn't solving the problem – Jonah M Sep 13 '19 at 07:32
  • @JonahM You probably expect more from the legacy OpenGL gouraud shading than it can afford. Again see [GLSL fixed function fragment program replacement](https://stackoverflow.com/questions/8421778/glsl-fixed-function-fragment-program-replacement/45716107#45716107) respectively [Per Vertex Diffuse and Specular Shader](https://stackoverflow.com/questions/48523723/per-vertex-diffuse-and-specular-shader/48528327#48528327). – Rabbid76 Sep 13 '19 at 07:42
  • I read your posts but I still don't understand what the problem is. – Jonah M Sep 13 '19 at 15:27
  • @JonahM The normal vector has to point out of the mesh. See the last part of the answer. Further there is a mistake related to the static variable (but that's basic c respectively c++) – Rabbid76 Sep 13 '19 at 17:37
  • Thank you! I made cp[] static to avoid returning the address of a local variable forgetting it could not be reinitialized. I realize some of the polygons are facing the wrong direction but it was hard to address that with flat colours. – Jonah M Sep 13 '19 at 19:02