Doom 3 lighting shader

I’m trying to write a glSlang shader that is exactly as the Doom 3 lighting shader(well, a bit different, instead of using a texture lookup for the specular reflection, I would use the pow function) and I added a lightmap too. The problem is that I don’t know ARB_fp so I can’t understand the shaders that come with Doom 3, and with glSlang I can’t get it right. So far my best attempt is

uniform sampler2D Base; //Diffuse texture from Doom 3. 
uniform sampler2D Bump; //Normal map from Doom 3.
uniform samplerCube LightCube; //Cubemap of a projected light.
uniform sampler2D Spec; //Specular map from Doom 3
uniform sampler2D LightMap; //Lightmap.

varying vec2 vTexCoord;
varying vec3 vLightVec;
varying vec3 vViewVec;
varying vec3 vDir;

void main(void)
   vec4 base = texture2D(Base2, vTexCoord);
   vec4 spec = texture2D(Spec2, vTexCoord);
   vec4 lightmap = texture2D(LightMap, vTexCoord);
   vec3 bump = texture2D(Bump2, vTexCoord).xyz * 2.0 - 1.0;
   vec4 light = textureCube(LightCube, vDir );

   bump          = normalize(bump );
   spec          = normalize(spec );
   vec3 lightVec = normalize(vLightVec );
   vec3 viewVec  = normalize(vViewVec );

   float diffuse = clamp(dot(lightVec, bump), 0.0, 1.0);
   float specular = pow(clamp(dot(reflect(-viewVec, bump), lightVec) ,0.0, 1.0), 12.0);

   gl_FragColor = ((lightmap * base) + 0.03) + ((diffuse * base * specular) + spec) * light;

but it doesn’t look anything like the Doom 3 shading + lightmap. I would appreciate any help.



seems like this line is all wrong

l_FragColor = ((lightmap * base) + 0.03) + ((diffuse * base * specular) + spec) * light;

A/ u dont wanna add the lightmap to the diffuse etc but modulate instead ie * not +
B/ spelcular is normally added on eg
base * diffuse + specular * spec

Here’s my solution:

ARB FP Source coded by JC

OPTION ARB_precision_hint_fastest;

# texture 0 is the cube map
# texture 1 is the per-surface bump map
# texture 2 is the light falloff texture
# texture 3 is the light projection texture
# texture 4 is the per-surface diffuse map
# texture 5 is the per-surface specular map
# texture 6 is the specular lookup table

# env[0] is the diffuse modifier
# env[1] is the specular modifier

TEMP	light, color, R1, R2, localNormal, specular;

PARAM	subOne = { -1, -1, -1, -1 };
PARAM	scaleTwo = { 2, 2, 2, 2 };

# load the specular half angle first, because
# the ATI shader gives a "too many indirections" error
# if this is done right before the texture indirection

#TEX	specular, fragment.texcoord[6], texture[0], CUBE;
#MAD	specular, specular, scaleTwo, subOne;

# instead of using the normalization cube map, normalize with math
DP3		specular, fragment.texcoord[6],fragment.texcoord[6];
RSQ		specular, specular.x;
MUL		specular, specular.x, fragment.texcoord[6];

# the amount of light contacting the fragment is the
# product of the two light projections and the surface
# bump mapping

# perform the diffuse bump mapping

TEX	light, fragment.texcoord[0], texture[0], CUBE;
MAD	light, light, scaleTwo, subOne;

# instead of using the normalization cube map, normalize with math
#DP3		light, fragment.texcoord[0],fragment.texcoord[0];
#RSQ		light, light.x;
#MUL		light, light.x, fragment.texcoord[0];

TEX	localNormal, fragment.texcoord[1], texture[1], 2D;
MOV localNormal.x, localNormal.a;
MAD	localNormal, localNormal, scaleTwo, subOne;
DP3	light, light, localNormal;

# modulate by the light projection
TXP	R1, fragment.texcoord[3], texture[3], 2D;
MUL	light, light, R1;

# modulate by the light falloff
TXP	R1, fragment.texcoord[2], texture[2], 2D;
MUL	light, light, R1;

# the light will be modulated by the diffuse and
# specular surface characteristics

# modulate by the diffuse map and constant diffuse factor
TEX	R1, fragment.texcoord[4], texture[4], 2D;
MUL	color, R1, program.env[0];

# perform the specular bump mapping
DP3	specular, specular, localNormal;

# perform a dependent table read for the specular falloff
TEX	R1, specular, texture[6], 2D;

# modulate by the constant specular factor
MUL	R1, R1, program.env[1];

# modulate by the specular map * 2
TEX	R2, fragment.texcoord[5], texture[5], 2D;
ADD	R2, R2, R2;
MAD	color, R1, R2, color;

MUL	color, light, color;

# modify by the vertex color

MUL result.color, color, fragment.color;

# this should be better on future hardware, but current drivers make it slower
#MUL, color, fragment.color;

My GLSL source

varying vec3 Specular_Light_Vector; //fragment.texcoord[6]
varying vec3 Diffuse_Light_Vector; //fragment.texcoord[0]
varying vec2 Normal_Texture_Coordinate; //fragment.texcoord[1]
varying vec2 Light_Projection_Vector; //fragment.texcoord[3]
varying vec2 Light_Falloff_Vector; //fragment.texcoord[2]
varying vec2 Diffuse_Texture_Coordinate; //fragment.texcoord[4]
varying vec2 Sepcular_Texture_Coordinate; //fragment.texcoord[5]

uniform samplerCube Normalization_Cube_Map;
uniform sampler2D Bump_Map;
uniform sampler2D Diffuse_Map;
uniform sampler2D Specular_Map;
uniform sampler2D Light_Falloff_Map;
uniform sampler2D Light_Projection_Map;
uniform sampler2D Specular_Lockup;

uniform vec4 Diffuse_Modifier; //env[0]
uniform vec4 Specular_Modifier; //env[1]

void main()
	vec3 specular_vec = normalize(Specular_Light_Vector);

	vec3 diffuse_vec = textureCube(Normalization_Cube_Map, Diffuse_Light_Vector) * 2.0 - 1.0;

	vec3 local_normal = texture2D(Bump_Map, Normal_Texture_Coordinate) * 2.0 - 1.0;
	local_normal.x = local_normal.a;
	vec4 diffuse = dot(diffuse_vec, local_normal) * texture2DProj(Light_Projection_Map, Light_Projection_Vector) * texture2DProj(Light_Falloff_Map, Light_Falloff_Vector);

	vec4 diffuse_color = texture2D(Diffuse_Map, Diffuse_Texture_Coordinate) * Diffue_Modifier;
	vec4 specular_color = texture2D(Specular_Map, Specular_Texture_Coordinate) * 2;

	vec4 specular = texture2D(vec2(dot(specular_vec, local_normal)), Sepcular_Texture_Coordinate) * Specular_Modifier;

	gl_FragColor = (specular * specular_color + diffuse_color) * diffuse * gl_Color;

First of all, thanks both of you, you are really helpful.

Now, I made two modifications to my shader, first I added

uniform float specValue; //Float for the Pow function

Since I’m using RenderMonkey I just change the value in real time, one of the reasons is because I don’t know the actual value that Doom 3 uses, it seems close to 16.0f. Also because the value should change depending on the surface, some surfaces have broader specular reflections, some have tighter specular reflections. So now to generate the specular value I use

float specular = pow(clamp(dot(reflect(-viewVec, bump), lightVec) ,0.0, 1.0), specValue);

The other change is the equation, is there anything wrong with this?

gl_FragColor = (((base + specular * (spec * 2.0)) * diffuse) * light) + lightmap;

It seems to look right, when everything is dark there is no specular reflection, and when the cubemap goes over the shadowed area(shadowed by the lightmap) the lighting is correct too, but maybe I’m doing something wrong.


This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.