Heres the code...
#include <windows.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#if defined(__cplusplus)
extern "C" {
#endif
int _fltused;
#if defined(__cplusplus)
};
#endif
//max number is 7, we must reserve one light for plane
// opengl supports 8 lights
#define NLIGHTS 7
static const GLfloat white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const GLfloat black[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //doubles as position of lights
static const GLfloat darklight[] = { -2.0f, -2.0f, -2.0f, 2.0f };
static GLUquadricObj *q;
long startTime;
float time;
void setup(){
int i;
q = gluNewQuadric ();
gluQuadricNormals (q,GLU_SMOOTH);
glColorMaterial (GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
// everything in the scene will receive white ambient light
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, white);
//setup all opengl lights to be "ambient occlusion" lights.
for (i=0;i<8; i++) {
glLightfv(GL_LIGHT0+i,GL_AMBIENT,black);
glLightfv(GL_LIGHT0+i,GL_SPECULAR,black);
// yo the secret sauce...
// lights are set with NEGATIVE colour!
glLightfv(GL_LIGHT0+i,GL_DIFFUSE,darklight);
// a quadratic falloff of light power is used
// attenuation is 1/(constant + linear*dist + quadratic*dist)
glLightf(GL_LIGHT0+i, GL_CONSTANT_ATTENUATION, 1.0f);//so intersecting spheres look right
glLightf(GL_LIGHT0+i, GL_LINEAR_ATTENUATION, 0.0f);
glLightf(GL_LIGHT0+i, GL_QUADRATIC_ATTENUATION, 2.0f); //physically correct would be 1.0, but it looks bad :-)
}
// enable all sphere lights
for (i=0;i<NLIGHTS;i++) glEnable (GL_LIGHT0+i);
// now the plane light needs differnt attenuation factors...
glLightf(GL_LIGHT7, GL_CONSTANT_ATTENUATION, 0.0f);//touch the plane and its black
glLightf(GL_LIGHT7, GL_LINEAR_ATTENUATION, 1.0f);
glLightf(GL_LIGHT7, GL_QUADRATIC_ATTENUATION, 0.0f);
// enable plane light
glEnable (GL_LIGHT7);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f,1.0f,0.1f,100.0f);
gluLookAt(0,0,-10.0f, 0,0,0, 0,1,0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
time =0;
}
void moveSpheres(int val){
glRotatef(time*0.4f,1,1,1);
glRotatef(val*time,val,5,3);
glTranslatef(sinf(val+time*0.1f)*4.0f,0,0);
}
void moveGroundLight(int val){
glTranslatef(0,-6.0f,0);
glScalef(1.0f,0.0f,1.0f);
moveSpheres(val);
}
void drawGround(){
// draw GROUND plane
glLoadIdentity();
glTranslatef(0.0f,-5.0f,0.0f);
glRotatef (-90,1,0,0);
glColor4f (1.0f,1.0f,0.9f,1.0f);
// disable the ground light as it should not occlude itself
glDisable (GL_LIGHT7);
gluDisk(q,0.0f,20.0f,60,60);
glEnable (GL_LIGHT7);
}
void drawAOSpheres(){
int i;
// set the lights in position
for (i=0; i<NLIGHTS; i++) {
glLoadIdentity();
moveSpheres(i);
// black is 0,0,0,1, which are the values we need for
// a point light at the origin.
glLightfv (GL_LIGHT0+i, GL_POSITION, black);
}
// draw the spheres being careful not to draw a sphere occluding itself
for (i=0;i<NLIGHTS; i++) {
// simulate a plane by always placing a light under the sphere on the plane
glLoadIdentity();
moveGroundLight(i);
glLightfv (GL_LIGHT7, GL_POSITION, black);
// a sphere should not occlude itself so switch off its own light
glDisable (GL_LIGHT0+i);
glColor4f (1.0f-(i/20.0f),0.7f+(i/20.0f),1.0f,1.0f);
glLoadIdentity();
moveSpheres(i);
gluSphere (q,1,30,30);
glEnable (GL_LIGHT0+i);
}
}
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
PIXELFORMATDESCRIPTOR pfd;
HDC hDC;
pfd.cColorBits = pfd.cDepthBits = 32;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
hDC = GetDC ( CreateWindow("edit", 0,
WS_POPUP|WS_VISIBLE,
100, 100, 640 , 480, 0, 0, 0, 0) );
SetPixelFormat ( hDC, ChoosePixelFormat ( hDC, &pfd) , &pfd );
wglMakeCurrent ( hDC, wglCreateContext(hDC) );
ShowCursor(FALSE);
glClearColor(1.0f,1.0f,1.0f,1.0f);
setup();
startTime = timeGetTime();
do {
glClear ( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
time = (timeGetTime() - startTime) *0.006f;
drawAOSpheres();
drawGround();
SwapBuffers ( hDC );
} while ( !GetAsyncKeyState(VK_ESCAPE) );
ExitProcess(0);
}