Reputation: 119
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
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