Current version: 0.45.1
>GeeXLab homepage

Current version: 1.30.0
>FurMark homepage

GPU Caps Viewer
Current version:
>GPU Caps Viewer homepage

GPU Shark
Current version:
>GPU Shark homepage

>JeGX's HackLab

Geeks3D's Articles
>GPU Memory Speed Demystified

>Multi-Threading Programming Resources

>GeForce and Radeon OpenCL Overview

>How to Get your Multi-core CPU Busy at 100%

>How To Make a VGA Dummy Plug

>Night Vision Post Processing Filter

PhysX FluidMark
Current version: 1.5.4
>FluidMark homepage

Current version: 0.3.0
>TessMark homepage

Current version: 0.3.0
>ShaderToyMark homepage
>ShaderToyMark Scores

Current Version: 1.23.0
>Libraries and Plugins
>Online Help - Reference Guide
>Codes Samples
Lighting with GLSL
Phong Model

By Jérôme GUINOT aka 'JeGX' - jegx [at] ozone3d [dot] net

Initial draft: February 19, 2006
Update: March 8, 2006

[ Index ]

Page 1 | Page 2 | Page 3 | Page 4

4 - Lighting Attenuation

Lighting equation coefficients that are dependent on a specific source of light can be optionally multiplied by an attenuation factor. This attenuation factor allows to simulate the light fading with distance.

If = (As*Am) + ((Al*Am) + Id + Is) * att

There are several ways to calculate the attenuation factor. We're going to see two of them.

4.1 - The standard method

This method exploits OpenGL coefficients of each light in GLSL:
- gl_LightSource[0].constantAttenuation or Kc
- gl_LightSource[0].linearAttenuation or Kl
- gl_LightSource[0].quadraticAttenuation or Kq

From these coefficients, the attenuation factor can be obtained by:

att = 1.0 / (Kc + Kl*d + Kq*d2)

where d stands for the distance between the position of the light and the currenlty processed vertex:

lightDir = vec3(gl_LightSource[0] - vVertex);
	float d = lenght(lightDir);

In OpenGL terms, attenuation factors are defined by:


And in Demoniak3D, these factors are defined in the light node:

<light name="Light_01" 
	constant_att="0.0" linear_att="0.0" quadratic_att="0.0002" />

Here is now our attenuated point light vertex shader:

varying vec3 normal, lightDir, eyeVec;
varying float att;

void main()
	normal = gl_NormalMatrix * gl_Normal;

	vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
	lightDir = vec3(gl_LightSource[0] - vVertex);
	eyeVec = -vVertex;
	float d = length(lightDir);
	att = 1.0 / ( gl_LightSource[0].constantAttenuation + 
	(gl_LightSource[0].linearAttenuation*d) + 
	(gl_LightSource[0].quadraticAttenuation*d*d) );

	gl_Position = ftransform();		

And here is our attenuated point light pixel shader:

varying vec3 normal, lightDir, eyeVec;
varying float att;

void main (void)
	vec4 final_color = 
	(gl_FrontLightModelProduct.sceneColor * gl_FrontMaterial.ambient) + 
	(gl_LightSource[0].ambient * gl_FrontMaterial.ambient)*att;
	vec3 N = normalize(normal);
	vec3 L = normalize(lightDir);
	float lambertTerm = dot(N,L);
	if(lambertTerm > 0.0)
		final_color += gl_LightSource[0].diffuse * 
		gl_FrontMaterial.diffuse * 
		lambertTerm * att;	
		vec3 E = normalize(eyeVec);
		vec3 R = reflect(-L, N);
		float specular = pow( max(dot(R, E), 0.0), 
		gl_FrontMaterial.shininess );
		final_color += gl_LightSource[0].specular * 
		gl_FrontMaterial.specular * specular * att;	

	gl_FragColor = final_color;			

The attenuation factor is computed in the vertex shader and then is passed to the pixel shader through the varying float att; variable.

Fig.5 - The point_light_att.xml demo.

4.2 - Light Radius

This method uses an imaginary sphere in which the light lies in. This sphere represents the light's influence area (i.e the farthest distance an object can be influenced by the light). The attenuation factor decreases along the radius, and becomes equal to zero on the edge of and outside the sphere. This factor is calculated in the pixel shader and assumes that a variable holding the inverse of the radius of the sphere is defined (in order to avoid a division):

float distSqr = dot(lightDir, lightDir);
float att = clamp(1.0 - invRadius * sqrt(distSqr), 0.0, 1.0);
vec3 L = lightDir * inversesqrt(distSqr);

This piece of code needs to be explained. The norm of the lightDir vector, is given by:

dist = sqrt(lightDir.x2 + lightDir.y2 + lightDir.z2)

This norm can be written using a dot product:

dist = sqrt( dot(lightDir, lightDir) )
distSqr = dot(lightDir, lightDir)

distSqr and invRadius allow to calculate the attenuation factor. The L vector, that is the normalized direction vector, can be obtenaid by the usual normalization formula:

magnitude: |r| = sqrt( x2 + y2 + z2 )
magnitude: |r| = sqrt( distSqr )

unit vector u: u = (1/|r|) * U
unit vector u: u = inversesqrt(distSqr) * U

A value of invRadius=0.001 (i.e 1000.0 units radius) will allow you to start your tests.

The final word: all vertex / pixel shaders previously seen are for all-purposes (i.e they are not bound to Demoniak3D), do not take input parameters (i.e no uniform variables) and so they can be effortless used in any OpenGL application. That said, have a good coding!

5 - Further Resources

6 - Downloads

Demoniak3D Phong Lighting Source Codes
Update: February 19, 2006

All projects have been successfully tested on a nVidia Geforce 7800GT (with Forceware 81.98) and on an ATI Radeon X700 Pro (with Catalyst 6.1).

All projects have been successfully tested on the flollowing configurations:

  • nVidia Geforce 7800GT - Forceware 81.98
  • ATI Radeon X700 Pro - Catalyst 6.1
  • ATI Radeon 9800 Pro - Catalyst 6.2

*** These source codes require Demoniak3D to work properly.
Demoniak3D is available here: [:: Demoniak3D Demo-System ::].

[ Index ]

Page 1 | Page 2 | Page 3 | Page 4

GeeXLab demos

GLSL - Mesh exploder

PhysX 3 cloth demo

Normal visualizer with GS

Compute Shaders test on Radeon

Raymarching in GLSL

>Texture DataPack #1
>Asus Silent Knight CPU Cooler
Page generated in 0.003242015838623 seconds.