Freelancer

01-27-2004, 04:42 AM

Hi everyone!

After days of reading ARB_vertex_program spec, gl lighting spec and tons

of vertex program examples, I've written my own implementation of point

lighting. Full lighting equation, according to the red book, is:

Vertex Color = emission + globalAmbient +

sum(attenuation * [lightAmbient + (max{L.N, 0} * diffuse) +

(max{H.N, 0} ^ shininess) * specular]

Where: emission - is the material's emissive color;

globalAmbient - is the material ambient color * global ambient brightness;

attenuation - is a term causing lights to become dimmer with distance;

lightAmbient - is the light's ambient color * material ambient color;

diffuse - is the light's diffuse color * the material's diffuse color;

shininess - is the specular exponent, which tells us how shiny a surface is;

specular - is the light's specular color * the material's specular color;

L - is the normalized(unit length) vector from the vertex we are lighting

to the light;

N - is the unit length normal to our vertex;

H - is the normalized "half angle" vector, which points half way between

the light and the viewer relative to the vertex position.

Here goes the vp:

--------------------------------

!!ARBvp1.0

#-------------------------------------------------------------------------#

# Lighting Vertex Program - replacement for a fixed function single front #

# -face-illuminating (attenuated?) point light. #

#-------------------------------------------------------------------------#

# Parameters' definition:

# Matrices:

PARAM mv[4] = { state.matrix.modelview }; # modelview matrix

PARAM mvi[4] = { state.matrix.modelview.invtrans }; # inverse transpose of modelview

PARAM mvp[4] = { state.matrix.mvp }; # modelview-projection matrix

# Lighting:

PARAM globalAmbient = state.lightmodel.ambient; # global ambient level

PARAM lightPos = state.light[1].position; # light position

PARAM lightAmbient = state.light[1].ambient; # light ambient color

PARAM lightDiffuse = state.light[1].diffuse; # light diffuse color

PARAM lightSpecular = state.light[1].specular; # light specular color

PARAM lightAttenuation = state.light[1].attenuation; # (const, linear, quadratic, #NA#);

# Materials:

PARAM materialAmbient = state.material.ambient; # material ambient color

PARAM materialEmission = state.material.emission; # material emission color

PARAM materialDiffuse = state.material.diffuse; # material diffuse color

PARAM materialSpecular = state.material.specular; # material specular color

PARAM materialShineExp = state.material.shininess; # material shininess exponent (-128, 128)

# Per vertex inputs:

ATTRIB iPos = vertex.position; # position

ATTRIB iNorm = vertex.normal; # normal

ATTRIB iCol0 = vertex.color; # primary color

ATTRIB iTex0 = vertex.texcoord; # texture coord, unit 0

# Outputs:

OUTPUT oPos = result.position; # position

OUTPUT oCol0 = result.color; # color

OUTPUT oTex0 = result.texcoord; # texture coord, unit 0

# Temporaries:

TEMP eyeVertex; # eye space vertex position

TEMP eyeNormal; # eye space normal position

TEMP vertToLight; # vertex-to-tight vector

TEMP lightAtnFinal; # light attenuation factor

TEMP lightFactors; # light factors, calculated by LIT

TEMP halfVector; # eye-vertex-to-light-vertex half-way vector

TEMP temp;

TEMP temp2;

TEMP ambient; # final ambient color

TEMP diffuse; # final diffuse color

TEMP specular; # final specular color

# Transform vertex position to clip space:

DP4 oPos.x, mvp[0], iPos;

DP4 oPos.y, mvp[1], iPos;

DP4 oPos.z, mvp[2], iPos;

DP4 oPos.w, mvp[3], iPos;

# Just pass on texture coordinates:

MOV oTex0, iTex0;

# Transform eye position to eye space:

DP4 eyeVertex.x, mv[0], iPos;

DP4 eyeVertex.y, mv[1], iPos;

DP4 eyeVertex.z, mv[2], iPos;

DP4 eyeVertex.w, mv[3], iPos;

# Transform normal to eye space:

DP4 eyeNormal.x, mvi[0], iNorm;

DP4 eyeNormal.y, mvi[1], iNorm;

DP4 eyeNormal.z, mvi[2], iNorm;

DP4 eyeNormal.w, mvi[3], iNorm;

# Normalize the eye-space normal:

DP3 eyeNormal.w, eyeNormal, eyeNormal;

RSQ eyeNormal.w, eyeNormal.w;

MUL eyeNormal.xyz, eyeNormal.w, eyeNormal;

# Calculate vector from vertex to light in eye-space:

ADD vertToLight, lightPos, -eyeVertex;

# Compute light attenuation factor (?):

# attenuation = 1/(c + l*d + q*d^2);

DP3 temp.yz, vertToLight, vertToLight;

RSQ temp2.yw, temp.y;

DST temp, temp, temp2;

DP4 lightAtnFinal, temp, lightAttenuation;

RCP lightAtnFinal, lightAtnFinal.w;

# Normalize vertex to light vector:

DP3 vertToLight.w, vertToLight, vertToLight;

RSQ vertToLight.w, vertToLight.w;

MUL vertToLight.xyz, vertToLight.w, vertToLight;

# Normalize eyeVertex to temp:

# temp is then the normalized vertex->camera vector.

DP3 temp.w, eyeVertex, eyeVertex;

RSQ temp.w, temp.w;

MUL temp.xyz, temp.w, eyeVertex;

# Calculate half-way vector:

SUB halfVector, vertToLight, temp;

# Normalize halfVector:

DP3 halfVector.w, halfVector, halfVector;

RSQ halfVector.w, halfVector.w;

MUL halfVector.xyz, halfVector.w, halfVector;

# Prepare to calculate lighting contributions.

# Diffuse lighting:

DP3 lightFactors.x, eyeNormal, vertToLight;

# Specular lighting:

DP3 lightFactors.y, eyeNormal, halfVector;

# Specular exponent:

MOV lightFactors.w, materialShineExp.x;

# Compute the lighting coefficients:

LIT lightFactors, lightFactors;

# Compute final ambient:

MUL ambient, materialAmbient, lightAmbient;

# Next line can be omitted; lightFactors.x is always 1.0 (see glspec.)

#MUL ambient, ambient, lightFactors.x;

# Compute final diffuse:

MUL diffuse, materialDiffuse, lightDiffuse;

MUL diffuse, diffuse, lightFactors.y;

# Compute final specular:

MUL specular, materialSpecular, lightSpecular;

MUL specular, specular, lightFactors.z;

# So far, we have:

#

# ambient = ambient_light_col * ambient_mat;

# diffuse = (max{L.N, 0} * diffuse_light_col * diffuse_mat;

# specular = (max{H.N, 0} ^ shininess) * specular_light_col * specular_mat;

#

# Now we evaluate (in temp):

#

# OutputVertexColor = emission + global_ambient_level*ambient_mat +

# attenuation * [ambient + diffuse + specular];

ADD temp, ambient, diffuse;

ADD temp, temp, specular;

# Note: if you are using this vp in a multipass system, the following code

# is valid ONLY for a final pass light. Otherwise, the previous line must be:

#

# MAD oCol0, temp, lightAtnFinal.w, specular;

#

# and next two lines must be commented (#).

MAD temp, temp, lightAtnFinal.w, materialEmission;

MAD oCol0, globalAmbient, materialAmbient, temp;

# Fill color alpha with diffuse alpha:

MOV oCol0.w, lightDiffuse.w;

END

--------------------------------

Three questions arise:

1) Spot lights.

How to change this program, so spot lights can be computed?

I've made a directional lighting vp, which is VERY easy

(just rip out all the attenuation stuff, MOV 'lightPos' to

'vertToLight' instead of 'lightPos-eyeVertex', haflVector

is 'state.light[1].half'). But while implementing spotlights

I quickly ran out of temporaries....

2) Performance.

Even in the most simple (read: non-fill-rate-consuming-and-

non-vertex-processing-time-consuming) scenes, I've got 2x

fps fall when I switch from conventional lighting to this vp.

Is that ok? May be some optimizations?

3) Attenuation.

In the above vp I'm calculating a "fair" attenuation, based

on GL lighting specification. But I still think that lighting

is not correct, or correct but not realistic - you can see it

if use vp. It simply doesn't look like a distance attenuation,

even for per-vertex lighting.

I wanted to implement per-pixel dist. att. with a single 3D

texture + register combiners + modification of the above vp.

Any ideas?

With respect,

-Dmitry aka Freelancer.

After days of reading ARB_vertex_program spec, gl lighting spec and tons

of vertex program examples, I've written my own implementation of point

lighting. Full lighting equation, according to the red book, is:

Vertex Color = emission + globalAmbient +

sum(attenuation * [lightAmbient + (max{L.N, 0} * diffuse) +

(max{H.N, 0} ^ shininess) * specular]

Where: emission - is the material's emissive color;

globalAmbient - is the material ambient color * global ambient brightness;

attenuation - is a term causing lights to become dimmer with distance;

lightAmbient - is the light's ambient color * material ambient color;

diffuse - is the light's diffuse color * the material's diffuse color;

shininess - is the specular exponent, which tells us how shiny a surface is;

specular - is the light's specular color * the material's specular color;

L - is the normalized(unit length) vector from the vertex we are lighting

to the light;

N - is the unit length normal to our vertex;

H - is the normalized "half angle" vector, which points half way between

the light and the viewer relative to the vertex position.

Here goes the vp:

--------------------------------

!!ARBvp1.0

#-------------------------------------------------------------------------#

# Lighting Vertex Program - replacement for a fixed function single front #

# -face-illuminating (attenuated?) point light. #

#-------------------------------------------------------------------------#

# Parameters' definition:

# Matrices:

PARAM mv[4] = { state.matrix.modelview }; # modelview matrix

PARAM mvi[4] = { state.matrix.modelview.invtrans }; # inverse transpose of modelview

PARAM mvp[4] = { state.matrix.mvp }; # modelview-projection matrix

# Lighting:

PARAM globalAmbient = state.lightmodel.ambient; # global ambient level

PARAM lightPos = state.light[1].position; # light position

PARAM lightAmbient = state.light[1].ambient; # light ambient color

PARAM lightDiffuse = state.light[1].diffuse; # light diffuse color

PARAM lightSpecular = state.light[1].specular; # light specular color

PARAM lightAttenuation = state.light[1].attenuation; # (const, linear, quadratic, #NA#);

# Materials:

PARAM materialAmbient = state.material.ambient; # material ambient color

PARAM materialEmission = state.material.emission; # material emission color

PARAM materialDiffuse = state.material.diffuse; # material diffuse color

PARAM materialSpecular = state.material.specular; # material specular color

PARAM materialShineExp = state.material.shininess; # material shininess exponent (-128, 128)

# Per vertex inputs:

ATTRIB iPos = vertex.position; # position

ATTRIB iNorm = vertex.normal; # normal

ATTRIB iCol0 = vertex.color; # primary color

ATTRIB iTex0 = vertex.texcoord; # texture coord, unit 0

# Outputs:

OUTPUT oPos = result.position; # position

OUTPUT oCol0 = result.color; # color

OUTPUT oTex0 = result.texcoord; # texture coord, unit 0

# Temporaries:

TEMP eyeVertex; # eye space vertex position

TEMP eyeNormal; # eye space normal position

TEMP vertToLight; # vertex-to-tight vector

TEMP lightAtnFinal; # light attenuation factor

TEMP lightFactors; # light factors, calculated by LIT

TEMP halfVector; # eye-vertex-to-light-vertex half-way vector

TEMP temp;

TEMP temp2;

TEMP ambient; # final ambient color

TEMP diffuse; # final diffuse color

TEMP specular; # final specular color

# Transform vertex position to clip space:

DP4 oPos.x, mvp[0], iPos;

DP4 oPos.y, mvp[1], iPos;

DP4 oPos.z, mvp[2], iPos;

DP4 oPos.w, mvp[3], iPos;

# Just pass on texture coordinates:

MOV oTex0, iTex0;

# Transform eye position to eye space:

DP4 eyeVertex.x, mv[0], iPos;

DP4 eyeVertex.y, mv[1], iPos;

DP4 eyeVertex.z, mv[2], iPos;

DP4 eyeVertex.w, mv[3], iPos;

# Transform normal to eye space:

DP4 eyeNormal.x, mvi[0], iNorm;

DP4 eyeNormal.y, mvi[1], iNorm;

DP4 eyeNormal.z, mvi[2], iNorm;

DP4 eyeNormal.w, mvi[3], iNorm;

# Normalize the eye-space normal:

DP3 eyeNormal.w, eyeNormal, eyeNormal;

RSQ eyeNormal.w, eyeNormal.w;

MUL eyeNormal.xyz, eyeNormal.w, eyeNormal;

# Calculate vector from vertex to light in eye-space:

ADD vertToLight, lightPos, -eyeVertex;

# Compute light attenuation factor (?):

# attenuation = 1/(c + l*d + q*d^2);

DP3 temp.yz, vertToLight, vertToLight;

RSQ temp2.yw, temp.y;

DST temp, temp, temp2;

DP4 lightAtnFinal, temp, lightAttenuation;

RCP lightAtnFinal, lightAtnFinal.w;

# Normalize vertex to light vector:

DP3 vertToLight.w, vertToLight, vertToLight;

RSQ vertToLight.w, vertToLight.w;

MUL vertToLight.xyz, vertToLight.w, vertToLight;

# Normalize eyeVertex to temp:

# temp is then the normalized vertex->camera vector.

DP3 temp.w, eyeVertex, eyeVertex;

RSQ temp.w, temp.w;

MUL temp.xyz, temp.w, eyeVertex;

# Calculate half-way vector:

SUB halfVector, vertToLight, temp;

# Normalize halfVector:

DP3 halfVector.w, halfVector, halfVector;

RSQ halfVector.w, halfVector.w;

MUL halfVector.xyz, halfVector.w, halfVector;

# Prepare to calculate lighting contributions.

# Diffuse lighting:

DP3 lightFactors.x, eyeNormal, vertToLight;

# Specular lighting:

DP3 lightFactors.y, eyeNormal, halfVector;

# Specular exponent:

MOV lightFactors.w, materialShineExp.x;

# Compute the lighting coefficients:

LIT lightFactors, lightFactors;

# Compute final ambient:

MUL ambient, materialAmbient, lightAmbient;

# Next line can be omitted; lightFactors.x is always 1.0 (see glspec.)

#MUL ambient, ambient, lightFactors.x;

# Compute final diffuse:

MUL diffuse, materialDiffuse, lightDiffuse;

MUL diffuse, diffuse, lightFactors.y;

# Compute final specular:

MUL specular, materialSpecular, lightSpecular;

MUL specular, specular, lightFactors.z;

# So far, we have:

#

# ambient = ambient_light_col * ambient_mat;

# diffuse = (max{L.N, 0} * diffuse_light_col * diffuse_mat;

# specular = (max{H.N, 0} ^ shininess) * specular_light_col * specular_mat;

#

# Now we evaluate (in temp):

#

# OutputVertexColor = emission + global_ambient_level*ambient_mat +

# attenuation * [ambient + diffuse + specular];

ADD temp, ambient, diffuse;

ADD temp, temp, specular;

# Note: if you are using this vp in a multipass system, the following code

# is valid ONLY for a final pass light. Otherwise, the previous line must be:

#

# MAD oCol0, temp, lightAtnFinal.w, specular;

#

# and next two lines must be commented (#).

MAD temp, temp, lightAtnFinal.w, materialEmission;

MAD oCol0, globalAmbient, materialAmbient, temp;

# Fill color alpha with diffuse alpha:

MOV oCol0.w, lightDiffuse.w;

END

--------------------------------

Three questions arise:

1) Spot lights.

How to change this program, so spot lights can be computed?

I've made a directional lighting vp, which is VERY easy

(just rip out all the attenuation stuff, MOV 'lightPos' to

'vertToLight' instead of 'lightPos-eyeVertex', haflVector

is 'state.light[1].half'). But while implementing spotlights

I quickly ran out of temporaries....

2) Performance.

Even in the most simple (read: non-fill-rate-consuming-and-

non-vertex-processing-time-consuming) scenes, I've got 2x

fps fall when I switch from conventional lighting to this vp.

Is that ok? May be some optimizations?

3) Attenuation.

In the above vp I'm calculating a "fair" attenuation, based

on GL lighting specification. But I still think that lighting

is not correct, or correct but not realistic - you can see it

if use vp. It simply doesn't look like a distance attenuation,

even for per-vertex lighting.

I wanted to implement per-pixel dist. att. with a single 3D

texture + register combiners + modification of the above vp.

Any ideas?

With respect,

-Dmitry aka Freelancer.