Local Diffuse & Specular Lighting Model

2010, Dr. Lawlor, CS 481/681, CS, UAF

For camera motion, you might check out ogl/minicam.h.  It lets you move the camera naturally using keyboard and mouse with just two function calls: oglCameraInit() from main (to set up event listener functions), and oglCameraLookAt(1.0) from your display function (to actually move the camera and set the gluLookAt part of the projection matrix).  You probably want a gluPerspective before the oglCameraLookAt.

GLSL "uniform" Variables: Smuggling Data into GLSL from C++

To do raytracing or specular lighting, we need to know where the camera is.  Only C++ knows this.  Luckily, you can smuggle data from C++ into GLSL using a GLSL "uniform" variable.

Inside your GLSL (vertex or fragment) shader, you declare a uniform variable just like a 'varying' variable:
    uniform vec3 cam; /* camera location, world coordinates */
After compiling, from C++ you can set this uniform variable.  You do this by passing it's string name to "glGetUniformLocationARB", which returns an integer index (into a table of variables somewhere in the guts of your graphics card).  You can then set a "vec3" uniform's value by passing the uniform's location to glUniform3fvARB (or set a vec4 with glUniform4fvARB, etc).  Here I'm passing one (1) float-pointer as the camera location, which I've named "camera" in C++ ogl/minicam.h:
	glUseProgramObjectARB(prog);
glUniform3fvARB( /* set the GLSL uniform variable named "cam" */
glGetUniformLocationARB(prog, "cam"),
1, /* <- number of variables to set (just one vec3) */
camera /* C++ variable new uniform value is read from */
);
Calling "glGetUniformLocation" every frame is somewhat expensive, and it's rather annoying to call.  So I've got a wrapper macro (in ogl/glsl.h) that caches the uniform's location in a static variable, for faster execution and a simpler interface:
	glFastUniform3fv(prog,"cam",1,camera);
Make sure your program is still bound in use before you try to set uniform variables; the "glUseProgramObjectARB" above is only needed once, but it is needed!

Examples of Lighting

There are several kinds of lighting we'll try out here:
To actually render lighting, you first need a whole set of unit-length direction vectors starting at the point on the surface you're shading.  In your GLSL fragment shader:
	vec3 N = normalize(gradient); // Points away from surface
vec3 L1 = normalize(vec3(0.0,0.0,1.0)); // Points toward light 1
vec3 T = normalize(vec3(cam)-P); // Points from intersection Toward camera
vec3 H1 = normalize(T+L1); // Blinn's 'halfway vector' for light 1
You may need to flip the normal vector to point toward the camera, typically this is just a "dot(N,cameraDir)" test followed by an "N=-N;" flip if the normals are backwards. Then you need to compute (clamped) dot products between these vectors:
	float a=0.2; // Ambient illumination
float d=clamp(dot(N,L1),0.0,1.0); // Diffuse light fraction
float s=pow(clamp(dot(N,H1),0.0,1.0),50.0); // Specular fraction. Constant controls highlight size
And finally you just combine these together--you can get lots of different effects by combining them in different ways.  Here I've added in "M" for the object ("Material") color, and "K" for a checkerboard pattern.


a- Ambient, 0.2

d- Diffuse, dot(N,L)

M- Material Color, gl_Color

s- Specular, pow(dot(N,H),...)


M*a
Typical Pure Ambient

M*d
Typical Pure Diffuse

M*(d+a)
Typical Lambertian

M*(d+a)+s
Classic Phong Lighting


K- Checkerboard, using step(fract(position))

K*M*(d+a)+s
Checkerboard controls overall diffuse color.

M*(d+a)+K*s
Checkerboard controls shininess.

M*(d+a)+s+0.5*K
Gently glowing checkerboard.