PDA

View Full Version : Point light/spotlight attenuation in glsl 1.3+?

omgzor
04-02-2011, 07:41 PM
I've been working OpenGL's Superbible (5th Edition) point light examples.

I found them missing the constant, linear and quadratic attenuation values integrated into the old lighting model, so I went and wrote a point light shader with attenuation values based on Point Light Attenuation (http://www.ogre3d.org/tikiwiki/-Point+Light+Attenuation) this ogre guide.

The results have been completely bizarre. What can be done to get a sensible light attenuation on the new glsl? Is there a glsl table for attenuation constants?

Sample vertex and fragment programs:
Vertex program:

//point light per pixel vertex program
#version 130

// Incoming per vertex... position and normal
in vec4 vVertex;
in vec3 vNormal;

uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;

// Color to fragment program
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;

out float dist;
out float constantAttenuation;
out float linearAttenuation;

void main(void)
{
// Get surface normal in eye coordinates
vVaryingNormal = normalMatrix * vNormal;

// Get vertex position in eye coordinates
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

//get distance to light source
dist=length(vLightPosition-vPosition3);

//write proper attenuation values

if (dist<7.0){
constantAttenuation=1.0;
linearAttenuation=0.7;
}

else if (dist<13.0){
constantAttenuation=1.0;
linearAttenuation=0.35;
}

else if (dist<20.0){
constantAttenuation=1.0;
linearAttenuation=0.22;
}

if (dist<32.0){
constantAttenuation=1.0;
linearAttenuation=0.14;
}
if (dist<50.0){
constantAttenuation=1.0;
linearAttenuation=0.09;
}

if (dist<65.0){
constantAttenuation=1.0;
linearAttenuation=0.07;
}

if (dist<100.0){
constantAttenuation=1.0;
linearAttenuation=0.045;
}

// Get vector to light source
vVaryingLightDir = normalize(vLightPosition - vPosition3);

// Don't forget to transform the geometry!
gl_Position = mvpMatrix * vVertex;
}

Fragment program:

//point light per pixel fragment program

#version 130

out vec4 vFragColor;

uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;

smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;

in float dist;
in float constantAttenuation;
in float linearAttenuation;

void main(void){

float att;

att = 1.0 / constantAttenuation + linearAttenuation*dist +quadraticAttenuation*dist*dist;

// Dot product gives us diffuse intensity
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));

// Multiply intensity by diffuse color, force alpha to 1.0
vFragColor = att*(diff * diffuseColor +ambientColor); // attenuation affects the diffuse component

// Specular Light
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));

float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if(diff != 0) {
float fSpec = pow(spec, 128.0);
vFragColor.rgb += (att*vec3 (fSpec, fSpec, fSpec)); // attenuation affects the specular component
}
}

Alfonse Reinheart
04-02-2011, 08:51 PM
The results have been completely bizarre.

That's because you misunderstood the Ogre document.

The "range" represents the maximum distance of effect for the light. That is, the distance at which you no longer want that light to affect objects.

This is a property of the light, just like it's intensity (ie: color). Your code has a hard-coded light intensity (namely, 1.0. Since you don't multiply the dot(N, L) by a color, you effectively have lights with unit intensity). The range is not something that should be output from a vertex shader.

Furthermore, you've misunderstood what the table means. The table is saying that, "If you want a light to have an effective range of X, here are the attenuation parameters that give this effect." The attenuation parameters are, like the light intensity and effective range, properties of the light. Not output values from the vertex shader. They should be uniforms.

That is, you pick one set of values from the table, based on the effective range you want.

Also:

att = 1.0 / constantAttenuation + linearAttenuation*dist +quadraticAttenuation*dist*dist;

You need more parentheses here. It's 1.0 divided by all of those things, not just "constantAttenuation".