Razmode
Razmode

Reputation: 119

OpenGL: Shading/Interpolation not working

The purpose of this code is to generate a 'surface' with random Y variation, and then have a light source shine on it and generate areas of brightness and perform shading on darker areas. The problem is, this isn't really happening. The light either illuminates one side or the other, and those sides are all uniformly bright or dark. What am I missing with this? Keep in mind there is a good bit of code that has yet to be removed, but it is not my priority, I'm just trying to get shading functional at this point.

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifdef MAC
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

//Camera variables
int xangle = -270;
int yangle = 0;

//Control Mode (Rotate mode by default)
int mode = 0;

//Player Position (Y offset so it would not be straddling the grid)
float cubeX = 0;
float cubeY = 0.5;

float cubeZ = 0;

//Vertex arrays for surface
float surfaceX [11][11];
float surfaceY [11][11];
float surfaceZ [11][11];

//Surface Normal arrays
float Nx[11][11];
float Ny[11][11];

float Nz[11][11];

//Color arrays
float R[11][11];
float G[11][11];
float B[11][11];

// Material properties
float Ka = 0.2;
float Kd = 0.4;
float Ks = 0.4;
float Kp = 0.5;

//Random number generator
float RandomNumber(float Min, float Max)
{
    return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min;
}

//---------------------------------------
// Initialize material properties
//---------------------------------------
void init_material(float Ka, float Kd, float Ks, float Kp,
                   float Mr, float Mg, float Mb)
{
   // Material variables
   float ambient[] = { Ka * Mr, Ka * Mg, Ka * Mb, 1.0 };
   float diffuse[] = { Kd * Mr, Kd * Mg, Kd * Mb, 1.0 };
   float specular[] = { Ks * Mr, Ks * Mg, Ks * Mb, 1.0 };

   // Initialize material
   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, Kp);
}

//---------------------------------------
// Initialize light source
//---------------------------------------
void init_light(int light_source, float Lx, float Ly, float Lz,
                float Lr, float Lg, float Lb)
{
   // Light variables
   float light_position[] = { Lx, Ly, Lz, 0.0 };
   float light_color[] = { Lr, Lg, Lb, 1.0 };

   // Initialize light source
   glEnable(GL_LIGHTING);
   glEnable(light_source);
   glLightfv(light_source, GL_POSITION, light_position);
   glLightfv(light_source, GL_AMBIENT, light_color);
   glLightfv(light_source, GL_DIFFUSE, light_color);
   glLightfv(light_source, GL_SPECULAR, light_color);
   glLightf(light_source, GL_CONSTANT_ATTENUATION, 1.0);
   glLightf(light_source, GL_LINEAR_ATTENUATION, 0.0);
   glLightf(light_source, GL_QUADRATIC_ATTENUATION, 0.0);
   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
}

//---------------------------------------
// Initialize surface 
//---------------------------------------
void init_surface()
{
    //Initialize X, select column  
    for (int i = 0; i < 11; i++) 
    {
        //Select row        
        for (int j = 0; j < 11; j++)
        {   
            surfaceX[i][j] = i-5;
            surfaceY[i][j] = RandomNumber(5, 7) - 5;
            surfaceZ[i][j] = j-5;
            //std::cout << "Coordinate "<< i << "," << j << std::endl;
        }

        //std::cout << "Hello world "<< std::endl;
    }
    //std::cout << "Coordinate -5,-5" << surfaceX[-5][-5] << std::endl;

}

void define_normals()
{
    //Define surface normals
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            //Get two tangent vectors
            float Ix = surfaceX[i+1][j] - surfaceX[i][j];
            float Iy = surfaceY[i+1][j] - surfaceY[i][j];
            float Iz = surfaceZ[i+1][j] - surfaceZ[i][j];
            float Jx = surfaceX[i][j+1] - surfaceX[i][j];
            float Jy = surfaceY[i][j+1] - surfaceY[i][j];
            float Jz = surfaceZ[i][j+1] - surfaceZ[i][j];

      //Get two tangent vectors
      //float Ix = Px[i+1][j] - Px[i][j];
      //float Iy = Py[i+1][j] - Py[i][j];
      //float Iz = Pz[i+1][j] - Pz[i][j];
      //float Jx = Px[i][j+1] - Px[i][j];
      //float Jy = Py[i][j+1] - Py[i][j];
      //float Jz = Pz[i][j+1] - Pz[i][j];

            //Do cross product
            Nx[i][j] = Iy * Jz - Iz * Jy;
            Ny[i][j] = Iz * Jx - Ix * Jz;
            Nz[i][j] = Ix * Jy - Iy * Jx;

            //Nx[i][j] = Nx[i][j] * -1;
            //Ny[i][j] = Ny[i][j] * -1;
            //Nz[i][j] = Nz[i][j] * -1;
            float length = sqrt( 
                Nx[i][j] * Nx[i][j] + 
                Ny[i][j] * Ny[i][j] + 
                Nz[i][j] * Nz[i][j]);
            if (length > 0)
            {
                Nx[i][j] /= length;
                Ny[j][j] /= length;
                Nz[i][j] /= length;
            }
        }   
    } 

    //std::cout << "Surface normal for 0,0: "<< Nx[0][0] << "," << Ny[0][0] << "," << Nz[0][0] << std::endl;

}

void calc_color()
{
    for (int i = 0; i < 10; i++)
    {           
        for (int j = 0; j < 10; j++)
        {
            //Calculate light vector
            //Light position, hardcoded for now 0,1,1
            float Lx = -1 - surfaceX[i][j]; 
            float Ly = -1 - surfaceY[i][j];
            float Lz = -1 - surfaceZ[i][j];
            std::cout << "Lx: " << Lx << std::endl; 
            std::cout << "Ly: " << Ly << std::endl;
            std::cout << "Lz: " << Lz << std::endl;

            //Grab surface normals
            //These are Nx,Ny,Nz due to compiler issues
            float Na = Nx[i][j];
            float Nb = Ny[i][j];
            float Nc = Nz[i][j];

            std::cout << "Na: " << Na << std::endl; 
            std::cout << "Nb: " << Nb << std::endl; 
            std::cout << "Nc: " << Nc << std::endl; 
            //Do cross product
            float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);
            std::cout << "Color: " << Color << std::endl;

            //Color = Color * -1;
            R[i][j] = Color;
            G[i][j] = Color;
            B[i][j] = Color;

            //std::cout << "Color Value: " << std::endl;
            ////std::cout << "R: " << R[i][j] << std::endl;
            //std::cout << "G: " << G[i][j] << std::endl;
            //std::cout << "B: " << B[i][j] << std::endl;
        }

    }
}



//---------------------------------------
// Init function for OpenGL
//---------------------------------------
void init()
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //Viewing Window Modified
    glOrtho(-7.0, 7.0, -7.0, 7.0, -7.0, 7.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    //Rotates camera
    //glRotatef(30.0, 1.0, 1.0, 1.0);
    glEnable(GL_DEPTH_TEST);

    //Project 3 code
    init_surface();
    define_normals();

    //Shading code
    glShadeModel(GL_SMOOTH);
    glEnable(GL_NORMALIZE);
    init_light(GL_LIGHT0, 0, 1, 1, 0.5, 0.5, 0.5);
    //init_light(GL_LIGHT1, 0, 0, 1, 0.5, 0.5, 0.5);
    //init_light(GL_LIGHT2, 0, 1, 0, 0.5, 0.5, 0.5);


}

void keyboard(unsigned char key, int x, int y)
{
    //Controls
    //Toggle Mode  
    if (key == 'q')
    {
        if(mode == 0)
        {       
            mode = 1;
            std::cout << "Switched to Move mode (" << mode << ")" << std::endl; 
        }
        else if(mode == 1)
        {   
            mode = 0;
            std::cout << "Switched to Rotate mode (" << mode << ")" << std::endl;
        }
    }

    ////Rotate Camera (mode 0)
    //Up & Down
    else if (key == 's' && mode == 0)
        xangle += 5;
    else if (key == 'w' && mode == 0)
    xangle -= 5;

    //Left & Right
    else if (key == 'a' && mode == 0) 
    yangle -= 5;
    else if (key == 'd' && mode == 0) 
    yangle += 5;

    ////Move Cube (mode 1)
    //Forward & Back
    else if (key == 'w' && mode == 1) 
    {
        if (cubeZ > -5)
        cubeZ = cubeZ - 1;
        else
        std::cout << "You have struck an invisible wall! (Min Z bounds)" << std::endl;
    }

    else if (key == 's' && mode == 1)
    {
        if (cubeZ < 5)
        cubeZ = cubeZ + 1;
        else
        std::cout << "You have struck an invisible wall! (Max Z bounds)" << std::endl;
    }

    //Strafe
    else if (key == 'd' && mode == 1)
    {
        if (cubeX < 5)      
        cubeX = cubeX + 1;
        else
        std::cout << "You have struck an invisible wall! (Max X bounds)" << std::endl;  
    }
    else if (key == 'a' && mode == 1)
    {
        if (cubeX > -5)     
        cubeX = cubeX - 1;
        else
        std::cout << "You have struck an invisible wall! (Min X bounds)" << std::endl;  
    }   

    //Up & Down (Cube offset by +0.5 in Y)
    else if (key == 'z' && mode == 1)
    {
        if (cubeY < 5)
        cubeY = cubeY + 1;
        else
        std::cout << "You've gone too high! Come back! (Max Y bounds)" << std::endl;
    }
    else if (key == 'x' && mode == 1)
    {
        if (cubeY > 0.5)
        cubeY = cubeY - 1;
        else
        std::cout << "You've reached bedrock! (Min Y bounds)" << std::endl;
    }
    //Place/Remove block
    else if (key == 'e' && mode == 1)
    {
        //Occupied(cubeX,cubeY,cubeZ);
    }

    //Redraw objects
    glutPostRedisplay();
}


//---------------------------------------
// Display callback for OpenGL
//---------------------------------------
void display()
{       
        // Clear the screen
    //std::cout << "xangle: " << xangle << std::endl;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Rotation Code
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(xangle, 1.0, 0.0, 0.0);
    glRotatef(yangle, 0.0, 1.0, 0.0);

    //Light Code
    init_material(Ka, Kd, Ks, 100 * Kp, 0.8, 0.6, 0.4); 

    calc_color();




    //Draw the squares, select column  
    for (int i = 0; i <= 9; i++)
    {
        //Select row        
        for (int j = 0; j <= 9; j++)
        {   
            glBegin(GL_POLYGON); 
            //Surface starts at top left
            //Counter clockwise

// CALCULATE COLOR HERE
// - calculate direction from surface to light
// - calculate dot product of normal and light direction vector
// - call glColor function





            //Calculate light vector
            //Light position, hardcoded for now 0,1,1
            ///float Lx = 0 - surfaceX[i][j]; 
            //float Ly = 1 - surfaceY[i][j];
            //float Lz = 1 - surfaceZ[i][j];

            //Grab surface normals
            //These are Nx,Ny,Nz due to compiler issues
            //float Na = Nx[i][j];
            //float Nb = Ny[i][j];
            //float Nc = Nz[i][j];

            //Do cross product
            //float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);

            //???           
            //glColor3fv(Color);
            //glColor3f(0.5*Color,0.5*Color,0.5*Color);

            glColor3f(R[i][j], G[i][j], B[i][j]);
            glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);

            glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
            glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);

            glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
            glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);

            glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
            glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);

            glEnd();
        }
    }

    //Draw the normals
    for (int i = 0; i <= 10; i++)
    {
        for (int j = 0; j <= 10; j++)
        {

            glBegin(GL_LINES);
            //glColor3f(0.0, 1.0, 1.0);
            float length = 1;
            glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
                glVertex3f(surfaceX[i][j]+length*Nx[i][j], 
                surfaceY[i][j]+length*Ny[i][j], 
                surfaceZ[i][j]+length*Nz[i][j]);
            glEnd();
        }
    }
    glEnd();
    glFlush();


    //Player Cube
    //Cube: midx, midy, midz, size
    //+Z = Moving TOWARD camera in opengl

    //Origin point for reference
    glPointSize(10);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);
    glVertex3f(0, 0, 0);        
    glEnd();

    //Assign Color of Lines
    float R = 1;
    float G = 1;
    float B = 1;
    glBegin(GL_LINES);
    glColor3f(R, G, B);

    ////Drawing the grid
    //Vertical lines
    for (int i = 0; i < 11; i++)
    {
        int b = -5 + i;

        glVertex3f(b, 0, -5);
        glVertex3f(b, 0, 5);
    }

    //Horizontal lines
    for (int i = 0; i < 11; i++)
    {
        int b = -5 + i;
        glVertex3f(-5,0,b);
        glVertex3f(5,0,b);

    }
    glEnd();


    glEnd();
    glFlush();  
}




//---------------------------------------
// Main program
//---------------------------------------
int main(int argc, char *argv[])
{

    srand(time(NULL));

    //Print Instructions
    std::cout << "Project 3 Controls: " << std::endl;
    std::cout << "q switches control mode" << std::endl;
    std::cout << "w,a,s,d for camera rotation" << std::endl;


    //Required
    glutInit(&argc, argv);
    //Window will default to a different size without
    glutInitWindowSize(500, 500);
    //Window will default to a different position without
    glutInitWindowPosition(250, 250);
    //
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
    //Required
    glutCreateWindow("Project 3");
    //Required, calls display function
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    //Required
    init();
    glutMainLoop();



   return 0;
}

Both the surface and normals are being generated as well as the color for the given vertex, I'm just not understanding why it isn't working.

Upvotes: 1

Views: 135

Answers (1)

Rabbid76
Rabbid76

Reputation: 210977

The light or brightness of the surface is a function of the incident light vector the view direction and the normal vector of the surface. You missed to set the normal vector attributes when you render the plane. Set the normal vector attribute by glNormal, before the vertex coordinate is specified:

for (int i = 0; i <= 9; i++)
{
    for (int j = 0; j <= 9; j++)
    {   
        glBegin(GL_POLYGON); 

        glColor3f(R[i][j], G[i][j], B[i][j]);
        glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
        glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);

        glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
        glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
        glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);

        glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
        glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
        glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);

        glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
        glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
        glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);

        glEnd();
    }
}  

But note, that the quality of the light will be low, because of the Gouraud shading of the Legacy OpenGL standard light model.
See also OpenGL Lighting on texture plane is not working.


Further, the normal vectors are inverted. You can change the direction by swapping the vectors in the cross product:

Nx[i][j] = Iz * Jy - Iy * Jz;
Ny[i][j] = Ix * Jz - Iz * Jx;
Nz[i][j] = Iy * Jx - Ix * Jy; 

Side note:

Note, that drawing by glBegin/glEnd sequences, the fixed function matrix stack and fixed function, per vertex light model, is deprecated since decades. See Fixed Function Pipeline and Legacy OpenGL. Read about Vertex Specification and Shader for a state of the art way of rendering.

Upvotes: 1

Related Questions