I think this is interesting for people who are working on their own Q3A/Doom3 compatible engine and looking for some performance tricks.
I can render a Doom3 level using a Doom3 style lighting system with light shaders and everything but shadows. On the other hand I can use per pixel lighting using deluxemaps and light vectors (deluxels) that are stored in every BSP vertex. The lighting is generated by a modified q3map2 compiler. The new way does not need to render world surfaces multiple times for every static light. There is only 1 lighting pass needed for every world surface using deluxemaps and deluxel vectors. It allows all N dot L based lighting techniques even Specular Parallax Bumpmapping.
The advantage is clear. You need less lighting passes and this will result in more speed. The disadvantage is that you can use this technique only for static lights. But think about that Doom3 renders a surface 1 time more for really every simple static and non-exiting light just because to have a unified lighting model. I think it is better to use the GPU power for something else. Use deluxe mapping for all static lights in your map and Doom3 style dynamic lighting for all lights that require it like flickering or rotating lights.
I made some comparison shots to demonstrate how deluxel based lighting looks compared to the Doom3 style lighting system. Keep in mind that q3map2 uses a different light attenuation model than Doom3 and I haven’t written light shader support for q3map2 so it will look a bit different.
http://xreal.sourceforge.net/screenshots/qrazor-fxIII050.jpg <-- dynamic Doom3 style lighting
http://xreal.sourceforge.net/screenshots/qrazor-fxIII051.jpg <-- static Deluxel based lighting precomputed by q3map2
There are more shots till http://xreal.sourceforge.net/screenshots/qrazor-fxIII065.jpg
This technique works with Doom3 materials which allow to add lightmap and deluxemap shader stages to the shader without any special blend modes.
Material sample shader
textures/ind/tig_bluholes
{
qer_editorimage textures/ind_ed/tig_bluholes.tga
{
stage diffusemap
map textures/ind/tig_bluholes.png
}
{
// heightmap for parallax mapping can be saved in the alpha channel
stage bumpmap
map textures/ind_x/tig_bluholes_n8.png
bumpScale 1.1
heightBias -0.02
heightScale 0.04
}
{
stage specularmap
map textures/ind_x/tig_bluholes_s.png
specularExponent 24
}
{
// you don't need to specify this stage
stage lightmap
// apply tcMod commands if needed like scale
}
{
// you don't need to specify this stage
stage deluxemap
// apply tcMod commands if needed
}
// add addional color stages if needed like this one
// {
// stage colormap
// map textures/ind_x/tr3b_blueholes_decal.png
// }
}
OpenGL GLSL code for vertex deluxel based lighting
attribute vec4 attr_TexCoord0;
attribute vec3 attr_Tangent;
attribute vec3 attr_Binormal;
attribute vec3 attr_Light;
varying vec3 var_vertex;
varying vec4 var_tex_diffuse_bump;
varying vec2 var_tex_specular;
varying mat3 var_mat_os2ts;
varying vec3 var_light;
varying vec3 var_color;
void main()
{
// transform vertex position into homogenous clip-space
gl_Position = ftransform();
// assign position in object space
var_vertex = gl_Vertex.xyz;
// transform texcoords into diffusemap texture space
var_tex_diffuse_bump.st = (gl_TextureMatrix[0] * attr_TexCoord0).st;
// transform texcoords into bumpmap texture space
var_tex_diffuse_bump.pq = (gl_TextureMatrix[1] * attr_TexCoord0).st;
// transform texcoords into specularmap texture space
var_tex_specular = (gl_TextureMatrix[2] * attr_TexCoord0).st;
// construct object-space-to-tangent-space 3x3 matrix
var_mat_os2ts = mat3( attr_Tangent.x, attr_Binormal.x, gl_Normal.x,
attr_Tangent.y, attr_Binormal.y, gl_Normal.y,
attr_Tangent.z, attr_Binormal.z, gl_Normal.z );
// assign vertex to light vector in object space
var_light = attr_Light;
// assign color
var_color = gl_Color.rgb;
}
uniform sampler2D u_diffusemap;
uniform sampler2D u_bumpmap;
uniform sampler2D u_specularmap;
uniform vec3 u_view_origin;
uniform float u_bump_scale;
uniform float u_height_scale;
uniform float u_height_bias;
uniform float u_specular_exponent;
void main()
{
// compute view direction in tangent space
vec3 V = normalize(var_mat_os2ts * (u_view_origin - var_vertex));
// compute height
float height = texture2D(u_bumpmap, var_tex_diffuse_bump.pq).a;
// compute texcoords offset
vec2 tex_offset = (height * u_height_scale + u_height_bias) * V.xy;
// compute light direction in tangent space
vec3 L = normalize(var_mat_os2ts * var_light);
// compute half angle in tangent space
vec3 H = normalize(L + V);
// compute normal in tangent space from bumpmap
vec3 N = 2.0 * (texture2D(u_bumpmap, var_tex_diffuse_bump.pq + tex_offset).xyz - 0.5);
N.z *= u_bump_scale;
N = normalize(N);
// compute the diffuse term
vec4 diffuse = texture2D(u_diffusemap, var_tex_diffuse_bump.st + tex_offset);
diffuse.rgb *= var_color * clamp(dot(N, L), 0.0, 1.0);
// compute the specular term
vec3 specular = texture2D(u_specularmap, var_tex_specular + tex_offset).rgb * var_color * pow(clamp(dot(N, H), 0.0, 1.0), u_specular_exponent);
// compute final color
gl_FragColor.rgba = diffuse;
gl_FragColor.rgb += specular;
}
OpenGL GLSL code for lightmap and deluxmap based lighting
attribute vec4 attr_TexCoord0;
attribute vec4 attr_TexCoord1;
attribute vec3 attr_Tangent;
attribute vec3 attr_Binormal;
varying vec3 var_vertex;
varying vec4 var_tex_diffuse_bump;
varying vec2 var_tex_specular;
varying vec4 var_tex_light_deluxe;
varying mat3 var_mat_os2ts;
void main()
{
// transform vertex position into homogenous clip-space
gl_Position = ftransform();
// assign vertex in object space
var_vertex = gl_Vertex.xyz;
// transform texcoords into diffusemap texture space
var_tex_diffuse_bump.st = (gl_TextureMatrix[0] * attr_TexCoord0).st;
// transform texcoords into bumpmap texture space
var_tex_diffuse_bump.pq = (gl_TextureMatrix[1] * attr_TexCoord0).st;
// transform texcoords into specularmap texture space
var_tex_specular = (gl_TextureMatrix[2] * attr_TexCoord0).st;
// transform texcoords_lm into lightmap texture space
var_tex_light_deluxe.st = (gl_TextureMatrix[3] * attr_TexCoord1).st;
// transform texcoords_lm into deluxemap texture space
var_tex_light_deluxe.pq = (gl_TextureMatrix[4] * attr_TexCoord1).st;
// construct object-space-to-tangent-space 3x3 matrix
var_mat_os2ts = mat3( attr_Tangent.x, attr_Binormal.x, gl_Normal.x,
attr_Tangent.y, attr_Binormal.y, gl_Normal.y,
attr_Tangent.z, attr_Binormal.z, gl_Normal.z );
}
uniform sampler2D u_diffusemap;
uniform sampler2D u_bumpmap;
uniform sampler2D u_specularmap;
uniform sampler2D u_lightmap;
uniform sampler2D u_deluxemap;
uniform vec3 u_view_origin;
uniform float u_bump_scale;
uniform float u_height_scale;
uniform float u_height_bias;
uniform float u_specular_exponent;
void main()
{
// compute view direction in tangent space
vec3 V = normalize(var_mat_os2ts * (u_view_origin - var_vertex));
// compute height
float height = texture2D(u_bumpmap, var_tex_diffuse_bump.pq).a;
// compute texcoords offset
vec2 tex_offset = (height * u_height_scale + u_height_bias) * V.xy;
// compute light direction in tangent space from deluxemap
vec3 L = normalize(var_mat_os2ts * (2.0 * (texture2D(u_deluxemap, var_tex_light_deluxe.pq).xyz - 0.5)));
// compute half angle in tangent space
vec3 H = normalize(L + V);
// compute normal in tangent space from bumpmap
vec3 N = 2.0 * (texture2D(u_bumpmap, var_tex_diffuse_bump.pq + tex_offset).xyz - 0.5);
N.z *= u_bump_scale;
N = normalize(N);
// compute light color from object space lightmap
vec3 C = texture2D(u_lightmap, var_tex_light_deluxe.st).xyz;
// compute the diffuse term
vec4 diffuse = texture2D(u_diffusemap, var_tex_diffuse_bump.st + tex_offset);
diffuse.rgb *= C * clamp(dot(N, L), 0.0, 1.0);
// compute the specular term
vec3 specular = texture2D(u_specularmap, var_tex_specular + tex_offset).rgb * C * pow(clamp(dot(N, H), 0.0, 1.0), u_specular_exponent);
// compute final color
gl_FragColor = diffuse;
gl_FragColor.rgb += specular;
}
I hope this helps to illustrate how things work in the GPU.





