chris : such an optimization is very handy for per-fragment low-level extensions such as register combiners (and certainly with fragment shaders too).
I’ve already seen such approximation in Mark Kilgard’s “Practical and Robust Bump-mapping for Today’s GPUs” with the formula : (0.4 * ((H.N) - 0.75))^2
wis mak : the piece of code that I wrote merely does :
{
draw decal in RGB buffer => so that RGB buffer contains ‘decal’
multiply RGB buffer by diffuse => so that RGB buffer contains ‘decaldiffuse’
draw specular in Alpha buffer => so that Alpha buffer contains ‘specular intensity’
modulate Alpha buffer with specular color and add it to RGB buffer => so that RGB buffer contains 'decaldiiffuse+specular intensityspecular color’
}
because 'specular intensityspecular color’ represents the total specular contribution, the final RGB buffer contains ‘decal*diffuse+specular’
to do it with multiple lights, add specular contributions of each light separately by clearing the alpha buffer when you need a new specular contribution.
First, add the diffuse contributions of each light. Render this contribution in RGB buffer.
Then modulate the total diffuse contribution with decal. Render this into RGB buffer.
Then, for each light :
{
draw the specular intensity in Alpha buffer.
modulate this Alpha buffer with specular color and add it to RGB buffer
}
The would look like that (still, without optimizations) :
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // deactivate Alpha writing
glDisable(GL_BLEND);
glColor3f(0,0,0); // black color to clear the buffer
draw_geometry(); // draw geometry without texture. it clears the RGB buffer
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
for (i=0; i<number_of_lights(); i++) // in this loop, all diffuse contributions will be rendered
{
draw_diffuse(i); // add decal contribution of light i, in RGB buffer
// At this point, RGB buffer contains : (diffuse_0 + … + diffuse_i) where i is the index of the current light in the loop
}
// At this point, RGB buffer contains : (diffuse_0 + diffuse_1 + … + diffuse_n) where n is the number of lights
glBlendFunc(GL_DST_COLOR, GL_ZERO);
draw_decal(); // modulate the total diffuse contribution with the decal, in RGB buffer
// At this point, RGB buffer contains : (diffuse_0 + diffuse_1 + … + diffuse_n)*decal
for (i=0; i<number_of_lights(); i++) // in this loop, all specular contributions will be rendered
{
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); // activate Alpha writing and deactivate RGB writing
glDisable(GL_BLEND);
glColor4f(0,0,0,1); // full alpha to clear the buffer
draw_geometry(); // draw geometry without texture. it clears the Alpha buffer
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
for (int j=0; j<shininess(); j++) draw_specular(i); // render specular_intensity^n for light i, in Alpha buffer
glBlendFunc(GL_DST_ALPHA, GL_ONE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // activate RGB writing to add specular contribution of light i
glColor3fv(specular_color()); // note that this specular color may be dependant to the light (eg one light may have a white specular color but a second light may have a yellow specular color)
draw_geometry(); // draw geometry without texture. it adds specular contribution of light i, in RGB buffer
// At this point, RGB buffer contains : (diffuse_0 + diffuse_1 + … + diffuse_n)*decal + (specular_0 + … + specular_i)
}
// At this point, RGB buffer contains : (diffuse_0 + diffuse_1 + … + diffuse_n)*decal + (specular_0 + specular_1 + … + specular_n)