Hi,
I have got an interesting problem:
Using a directional source of light will not work correct in combination with specular! (If the angle between light and normalvector of the face is 90 degrees)
The rendered result depends on the point of view of the camera.
Cube Example:
To make it easier to understand I made some Screenshot:
From one to another picture I just changed the rotation of the Camera. (Just 0.1 degree around the Y-axis).
I’ve NOT changed the position of the light/cube!!!
The result should be the same (,because it is just another point of view), but it isn’t.
Sphere Examle:
I’ve tryed it on WinXp with QT and I tryed it on linux with GLUT. The result is the same!
complete (linux) sourcecode (modified Version of Nehe-Lesson07):
// This code was created by Jeff Molofee '99 (ported to Linux/GLUT by Richard Campbell '99)
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe
// (email Richard Campbell at [email]ulmont@bellsouth.net[/email])
//
#include <GL/glut.h> // Header File For The GLUT Library
#include <GL/gl.h> // Header File For The OpenGL32 Library
#include <GL/glu.h> // Header File For The GLu32 Library
#include <unistd.h> // Header file for sleeping.
#include <stdio.h> // Header file for standard file i/o.
#include <stdlib.h> // Header file for malloc/free.
void renderSphere()
{
GLUquadricObj *quadratic;
quadratic=gluNewQuadric(); // Create A Pointer To The Quadric Object ( NEW )
gluQuadricNormals(quadratic, GLU_SMOOTH); // Create Smooth Normals ( NEW )
gluQuadricTexture(quadratic, GL_TRUE);
gluSphere(quadratic,1.0f,32,32);
gluDeleteQuadric(quadratic);
}
void renderCube()
{
glBegin(GL_QUADS); // begin drawing a cube
// Front Face (note that the texture's corners have to match the quad's corners)
glNormal3f( 0.0f, 0.0f, 1.0f); // front face points out of the screen on z.
glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
// Back Face
glNormal3f( 0.0f, 0.0f,-1.0f); // back face points into the screen on z.
glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glVertex3f(-1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
// Top Face
glNormal3f( 0.0f, 1.0f, 0.0f); // top face points up on y.
glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
// Bottom Face
glNormal3f( 0.0f, -1.0f, 0.0f); // bottom face points down on y.
glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad
glVertex3f( 1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad
glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
// Right face
glNormal3f( 1.0f, 0.0f, 0.0f); // right face points right on x.
glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
// Left Face
glNormal3f(-1.0f, 0.0f, 0.0f); // left face points left on x.
glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glEnd(); // done with the polygon.
}
/* ascii codes for various special keys */
#define ESCAPE 27
#define PAGE_UP 73
#define PAGE_DOWN 81
#define UP_ARROW 72
#define DOWN_ARROW 80
#define LEFT_ARROW 75
#define RIGHT_ARROW 77
/* The number of our GLUT window */
int window;
/* lighting on/off (1 = on, 0 = off) */
int light;
/* L pressed (1 = yes, 0 = no) */
int lp;
/* F pressed (1 = yes, 0 = no) */
int fp;
GLfloat xrot; // x rotation
GLfloat yrot; // y rotation
GLfloat xspeed; // x rotation speed
GLfloat yspeed; // y rotation speed
GLfloat z=-5.0f; // depth into the screen.
/* white ambient light at half intensity (rgba) */
GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
/* super bright, full intensity diffuse light. */
GLfloat LightDiffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f };
/* position of light (x, y, z, (position of light)) */
GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f, 1.0f };
GLuint filter; /* Which Filter To Use (nearest/linear/mipmapped) */
GLuint texture[3]; /* Storage for 3 textures. */
/* Image type - contains height, width, and data */
struct Image {
unsigned long sizeX;
unsigned long sizeY;
char *data;
};
typedef struct Image Image;
// quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only.
// See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info.
// if mesa ever gets glaux, let me know.
int ImageLoad(char *filename, Image *image) {
FILE *file;
unsigned long size; // size of the image in bytes.
unsigned long i; // standard counter.
unsigned short int planes; // number of planes in image (must be 1)
unsigned short int bpp; // number of bits per pixel (must be 24)
char temp; // used to convert bgr to rgb color.
// make sure the file is there.
if ((file = fopen(filename, "rb"))==NULL)
{
printf("File Not Found : %s
",filename);
return 0;
}
// seek through the bmp header, up to the width/height:
fseek(file, 18, SEEK_CUR);
// read the width
if ((i = fread(&image->sizeX, 4, 1, file)) != 1) {
printf("Error reading width from %s.
", filename);
return 0;
}
printf("Width of %s: %lu
", filename, image->sizeX);
// read the height
if ((i = fread(&image->sizeY, 4, 1, file)) != 1) {
printf("Error reading height from %s.
", filename);
return 0;
}
printf("Height of %s: %lu
", filename, image->sizeY);
// calculate the size (assuming 24 bits or 3 bytes per pixel).
size = image->sizeX * image->sizeY * 3;
// read the planes
if ((fread(&planes, 2, 1, file)) != 1) {
printf("Error reading planes from %s.
", filename);
return 0;
}
if (planes != 1) {
printf("Planes from %s is not 1: %u
", filename, planes);
return 0;
}
// read the bpp
if ((i = fread(&bpp, 2, 1, file)) != 1) {
printf("Error reading bpp from %s.
", filename);
return 0;
}
if (bpp != 24) {
printf("Bpp from %s is not 24: %u
", filename, bpp);
return 0;
}
// seek past the rest of the bitmap header.
fseek(file, 24, SEEK_CUR);
// read the data.
image->data = (char *) malloc(size);
if (image->data == NULL) {
printf("Error allocating memory for color-corrected image data");
return 0;
}
if ((i = fread(image->data, size, 1, file)) != 1) {
printf("Error reading image data from %s.
", filename);
return 0;
}
for (i=0;i<size;i+=3) { // reverse all of the colors. (bgr -> rgb)
temp = image->data[i];
image->data[i] = image->data[i+2];
image->data[i+2] = temp;
}
// we're done.
return 1;
}
// Load Bitmaps And Convert To Textures
GLvoid LoadGLTextures(GLvoid) {
// Load Texture
Image *image1;
// allocate space for texture
image1 = (Image *) malloc(sizeof(Image));
if (image1 == NULL) {
printf("Error allocating space for image");
exit(0);
}
if (!ImageLoad("Data/lesson7/crate.bmp", image1)) {
exit(1);
}
// Create Textures
glGenTextures(3, &texture[0]);
// texture 1 (poor quality scaling)
glBindTexture(GL_TEXTURE_2D, texture[0]); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); // cheap scaling when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); // cheap scaling when image smalled than texture
// 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image,
// border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
// texture 2 (linear scaling)
glBindTexture(GL_TEXTURE_2D, texture[1]); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
// texture 3 (mipmapped scaling)
glBindTexture(GL_TEXTURE_2D, texture[2]); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); // scale linearly + mipmap when image smalled than texture
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
// 2d texture, 3 colors, width, height, RGB in that order, byte data, and the data.
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image1->sizeX, image1->sizeY, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
};
/* A general OpenGL initialization function. Sets all of the initial parameters. */
GLvoid InitGL(GLsizei Width, GLsizei Height) // We call this right after our OpenGL window is created.
{
LoadGLTextures(); // load the textures.
//glEnable(GL_TEXTURE_2D); // Enable texture mapping.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black
glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); // Reset The Projection Matrix
gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); // Calculate The Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW);
// set up light number 1.
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // add lighting. (ambient)
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // add lighting. (diffuse).
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); // set light position.
glEnable(GL_LIGHT1); // turn light 1 on.
glEnable(GL_LIGHTING);
light = 1;
}
/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
GLvoid ReSizeGLScene(GLsizei Width, GLsizei Height)
{
if (Height==0) // Prevent A Divide By Zero If The Window Is Too Small
Height=1;
glViewport(0, 0, Width, Height); // Reset The Current Viewport And Perspective Transformation
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
}
/* The main drawing function. */
GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(0.0f,0.0f,z); // move z units out from the screen.
glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis
//Setze Lichtquelle
const GLfloat LightDirection[] = { 0.0f, 0.0f, 1.0f, 0.0f };
const GLfloat LightSpecular[] = { 1.0f, 0.0f, 0.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_POSITION, LightDirection);
glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpecular);
const GLfloat specular[] = {1.0f,0.0f,0.0f,1.0f};
glMaterialf(GL_FRONT, GL_SHININESS, 1.0f);
glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
//Sphere:
renderSphere();
//Cube
//renderCube();
xrot+=xspeed; // X Axis Rotation
yrot+=yspeed; // Y Axis Rotation
// since this is double buffered, swap the buffers to display what just got drawn.
glutSwapBuffers();
}
/* The function called whenever a normal key is pressed. */
void keyPressed(unsigned char key, int x, int y)
{
/* avoid thrashing this procedure */
usleep(100);
switch (key) {
case ESCAPE: // kill everything.
/* shut down our window */
glutDestroyWindow(window);
/* exit the program...normal termination. */
exit(1);
break; // redundant.
case 76:
case 108: // switch the lighting.
printf("L/l pressed; light is: %d
", light);
light = light ? 0 : 1; // switch the current value of light, between 0 and 1.
printf("Light is now: %d
", light);
if (!light) {
glDisable(GL_LIGHTING);
} else {
glEnable(GL_LIGHTING);
}
break;
case 70:
case 102: // switch the filter.
printf("F/f pressed; filter is: %d
", filter);
filter+=1;
if (filter>2) {
filter=0;
}
printf("Filter is now: %d
", filter);
break;
default:
break;
}
}
/* The function called whenever a normal key is pressed. */
void specialKeyPressed(int key, int x, int y)
{
/* avoid thrashing this procedure */
usleep(100);
switch (key) {
case GLUT_KEY_PAGE_UP: // move the cube into the distance.
z-=0.02f;
break;
case GLUT_KEY_PAGE_DOWN: // move the cube closer.
z+=0.02f;
break;
case GLUT_KEY_UP: // decrease x rotation speed;
xspeed-=0.01f;
break;
case GLUT_KEY_DOWN: // increase x rotation speed;
xspeed+=0.01f;
break;
case GLUT_KEY_LEFT: // decrease y rotation speed;
yspeed-=0.01f;
break;
case GLUT_KEY_RIGHT: // increase y rotation speed;
yspeed+=0.01f;
break;
default:
break;
}
}
int main(int argc, char **argv)
{
/* Initialize GLUT state - glut will take any command line arguments that pertain to it or
X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */
glutInit(&argc, argv);
/* Select type of Display mode:
Double buffer
RGBA color
Alpha components supported
Depth buffer */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
/* get a 640 x 480 window */
glutInitWindowSize(640, 480);
/* the window starts at the upper left corner of the screen */
glutInitWindowPosition(0, 0);
/* Open a window */
window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99");
/* Register the function to do all our OpenGL drawing. */
glutDisplayFunc(&DrawGLScene);
/* Go fullscreen. This is as soon as possible. */
glutFullScreen();
/* Even if there are no events, redraw our gl scene. */
glutIdleFunc(&DrawGLScene);
/* Register the function called when our window is resized. */
glutReshapeFunc(&ReSizeGLScene);
/* Register the function called when the keyboard is pressed. */
glutKeyboardFunc(&keyPressed);
/* Register the function called when special keys (arrows, page down, etc) are pressed. */
glutSpecialFunc(&specialKeyPressed);
/* Initialize our window. */
InitGL(640, 480);
/* Start Event Processing Engine */
glutMainLoop();
return 1;
}
just the most important part of the code (quite short):
/* The main drawing function. */
GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(0.0f,0.0f,z); // move z units out from the screen.
glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis
//Setze Lichtquelle
const GLfloat LightDirection[] = { 0.0f, 0.0f, 1.0f, 0.0f };
const GLfloat LightSpecular[] = { 1.0f, 0.0f, 0.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_POSITION, LightDirection);
glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpecular);
const GLfloat specular[] = {1.0f,0.0f,0.0f,1.0f};
glMaterialf(GL_FRONT, GL_SHININESS, 1.0f);
glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
//Sphere:
renderSphere();
//Cube
//renderCube();
xrot+=xspeed; // X Axis Rotation
yrot+=yspeed; // Y Axis Rotation
// since this is double buffered, swap the buffers to display what just got drawn.
glutSwapBuffers();
}
By the way: I’ve got an workaround. Changing the directional light to a positional light source far far away, will work fine! (But i don’t like this workaround, because I want to know what’s wrong and ‘What is far far away?’)
Workaround:
const GLfloat LightDirection[] = { 0.0f, 0.0f, 9999.0f, 1.0f };