-
Notifications
You must be signed in to change notification settings - Fork 63
Wrath Shading
Exterior lighting in WotLK is driven by something known as 'area lights'. These lights are defined by entries in Light.dbc
, and are selected by the client based on proximity to the lights' position. Note that later versions of WoW also make use of something called 'zone lights'. WotLK does not make use of zone lights.
Area lights effectively act like spheres, with a center point (the light's position), and two radii: an inner radius (aka falloffStart
), and an outer radius (aka falloffEnd
).
When the camera position is in the inner radius of an area light, that light is the exclusive source for area lighting values. Occasionally, a camera position may be in the inner radius of two lights. TODO: Determine what, exactly, the client does in this case. Theory: the client opts to use only the closest area light of the set of area lights that the camera position is within the inner radius of.
When the camera position is in the outer radius of two or more lights, a blending factor is used to combine the values of all contributing lights. TODO: Determine how the blending logic works; determine if more than two lights can be blended together.
When the camera position does not fall within the radius of any light, a default area light entry is used. This is the first light entry in Light.dbc
. TODO: Confirm the use of the default; determine if default light is also used when no inner radius light is within range, but an outer radius light is within range.
Area light data is stored in other DBCs: static values are stored in LightParams.dbc
, lerped color values are stored in LightIntBand.dbc
, and lerped floating point values are stored in LightFloatBand.dbc
.
The colors and values for area lights are linearly interpolated using a factor based on the time of day in half minutes. There are 2880 half minutes in a day. Half minute 0 is midnight, as is half minute 2880.
Note that the values in LightIntBand.dbc
and LightFloatBand.dbc
often are sparse; that is, the number of 'stops' on the gradient being interpolated might be as few as 2. TODO: Are there ever entries with single stops?
Day progression is offset to match server time, rather than following the time of day on the local computer.
The following information was partially determined by reversing the client, and its accuracy has been checked against the actual WotLK client.
In the WotLK client, when using D3D9 mode, fog is computed using two uniforms:
-
fogParams
- avec4
on the vertex shader -
fogColor
- avec4
on the pixel shader
fogParams
is broken down like so:
fogParams.x = -(1.0 / (fogEnd - fogStart));
fogParams.y = (1.0 / (fogEnd - fogStart)) * fogEnd;
fogParams.z = <unknown; often 1.0; fed to pow() in VS, so maybe for special effects / weather>;
fogParams.w = <likely unused; often 0.0>;
The values for fogEnd
and fogStart
come from area lights. Area lights are the lighting values and colors found in Light.dbc
, LightParams.dbc
, LightIntBand.dbc
, and LightFloatBand.dbc
.
-
fogEnd
is found inLightFloatBand.dbc
-
fogScalar
is found inLightFloatBand.dbc
-
fogStart
isfogEnd * fogScalar
Note that fogScalar
can be negative. Indeed, it often is negative in the LightFloatBand.dbc
entries used by the WotLK client. Negative fogScalar
values seem to increase the rate the fog factor increases toward the end of the fog range.
For details on how the DBC values backing fogEnd
and fogScalar
are read, see the area lights section above.
fogColor
is broken down like so:
fogColor.r;
fogColor.g;
fogColor.b;
fogColor.a;
The value of fogColor
is found in LightIntBand.dbc. See the area lights section above for more details.
In WotLK, the vertex shader is responsible for computing the fog factor, a value between 0.0 and 1.0 representing the amount of fog that should be added to the final color in the pixel shader.
The fog vertex shading logic looks like this:
// Distance between vertex and camera
float cameraDistance = length(modelViewMatrix * vec4(position, 1.0));
float f1 = (cameraDistance * fogParams.x) + fogParams.y;
float f2 = max(f1, 0.0);
float f3 = pow(f2, fogParams.z);
float f4 = min(f3, 1.0);
float fogFactor = 1.0 - f4;
In WotLK, the pixel shader is responsible for mixing the fog color with the shaded pixel color. It does so by using the fog factor computed in the vertex shader.
The fog pixel shading logic looks like this:
result.rgb = mix(result.rgb, fogColor.rgb, fogFactor);
-
c6
- avec4
containing something related to texture UV transforms -
c7
- avec4
containing something related to texture UV transforms -
c10
- avec4
containingsunDiffuse
-
c11
- avec4
containingsunAmbient
-
c12
- avec4
containingsunDir
-
c28
- unknown (involved in lighting) - c28.w is texture weight from the texture weight animation tracks -
c29
- avec4
that, at least in some cases, comes out of the color animation tracks in the M2 -
c30
- avec4
containingfogParams
(see fog section)
-
c2
- avec4
containingfogColor
(see fog section)
-
sunDir
- avec4
sent to the vertex shader; can be overridden, for ex:s_interiorSunDir
-
sunAmbient
- avec4
sent to the vertex shader; can be overridden, for ex:MODD.color
orMOHD.color
-
sunDiffuse
- avec4
sent to the vertex shader; can be overridden, for ex:MODD.color
uniform vec3 lightDiffuse; // c10
uniform vec3 lightAmbient; // c11
uniform vec3 lightDir; // c12
uniform vec4 animatedColor; // c29 ?? Is there more in here?
uniform vec4 fogParams; // c30
vec4 light;
if (useLighting) {
vec3 objectNormal = normalize(modelMatrix * vec4(normal, 0.0)).xyz;
float lightFactor = saturate(dot(-lightDir, objectNormal));
light.rgb = saturate((lightFactor * lightDiffuse) + lightAmbient);
} else {
light.rgb = vec3(1.0);
}
// TODO:
// c28??
// light = saturate((light * c28) + c29)
light = saturate((light * c28) + animatedColor);
colors[0] = light;
// colors[0] = output of vertex shader (see above)
// fog.rgb = fog color (see fog section)
// fogFactor = fog factor (see fog section)
vec4 result;
vec4 sampled0 = texture2D(textures[0], coords[0]);
vec4 color0 = sampled0.rgb * colors[0].rgb;
result.rgb = mix(color0.rgb, fog.rgb, fogFactor);
result.a = colors[0].a;