Per pixel lighting

Today we will talk about per pixel lighting, or fragment lighting as the correct term is today.
Specifically we are going to add diffuse, specular and Fresnel reflection, now it’s not normal to add the Fresnel term, but i think it’s interesting to learn and here it looks kinda good to.
This is mostly a shader tutorial since the only c++ code change from 2b is that i now load a normal texture instead of the ambocc texture (that one is baked into the diffuse texture map using photoshop), and the other thing i changed was that i now push the light position to the shader using the sendUniform3f command (lpos in the shaders), you can do it any way you want, but it is preferable to use the built in constants available, i didn’t, but that’s because i didn’t want to write more code.

Now to do fragment lighting you need some stuff from the vertex shader, specifically the position of the fragment, the vector to the viewer and the vector to the light, i have included a normal to, but since we are going to use normal mapping that will not be needed.

uniform sampler2D texunit0;
uniform sampler2D texunit1;
uniform vec3 lpos;

varying vec4 pos;
varying vec3 normal;
varying vec3 lightVec;
varying vec3 viewVec;

void main( void )
{
pos= gl_ModelViewProjectionMatrix * gl_Vertex;
normal = normalize(gl_NormalMatrix * gl_Normal);
lightVec = normalize(lpos - pos.xyz);
viewVec = vec3 (normalize(- (gl_ModelViewProjectionMatrix *gl_Vertex)));

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
}

Now to the fragment shader, it’s a bit complex and has many parts so i will split it up a little.
First the start bit, it’s identical to the vertex shader start bit.

uniform sampler2D texunit0;
uniform sampler2D texunit1;
uniform vec3 lpos;

varying vec4 pos;
varying vec3 normal;
varying vec3 lightVec;
varying vec3 viewVec;

void main( void )
{

next we read from the textures, please note what we are doing to the normal texture, this is because, the normal texture needs to have negative values to become a normal so just multiply by two and subtract by one, simple.

vec4 base = texture2D(texunit0, gl_TexCoord[0].xy);
vec3 norm = texture2D(texunit1, gl_TexCoord[0].xy).xyz*2.0-1.0;

Next on we need to treat the normal the same way as we did the normal in the vertex shader so that as the tank rotates so does the normal.

norm = normalize(gl_NormalMatrix * norm);

After this we can begin to compute the different lighting components, first we do the Fresnel term, the Fresnel term is a value that basically controls diffuse ambient reflections at a low incandescent angle, and it looks great on round shiny objects.

float fresnel =max((norm.z-0.4)*-1.0,0);

Diffuse term is simple, it is just the dot product of the light vector and the normal.

float diffuse = dot(-lightVec, norm);

Specular is a bit more complicated, and there in no real correct way of doing this, specular highlights are basically a diffuse reflection of the light source.

float specular = max(dot(reflect(-lightVec,norm), viewVec), 0.0)*1.1;
specular=pow(specular,8.0);

and finally we bring it all together

gl_FragColor = (base * diffuse) +(vec4(0.0,0.5,1,0.0)*fresnel) +(vec4(1.0,1.0,0.8,0.0)*specular);

}

Download this tutorial for MSVCPP 6.0


1 Comment

  • By Peter Wallström, May 4, 2010 @ 17:00

    -old comments-

    Comment by eXile on 2007:08:30 10:37
    In glsl.frag replace the line:
    float fresnel =max((norm.z-0.4)*-1.0,0);

    With this one:
    float fresnel =max((norm.z-0.4)*-1.0,0.0);

    This should resolve a issue with Ati-cards (“no overloaded function max”). :)

Other Links to this Post

RSS feed for comments on this post. TrackBack URI

Leave a comment


WordPress Themes