No division by zero there.
Here are the shaders:
Object shader:
//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
float4 g_MaterialAmbientColor; // Material's ambient color
float4 g_MaterialDiffuseColor; // Material's diffuse color
bool g_DiffuseTexSet;
bool g_NormalTexSet;
bool g_SpecularTexSet;
float g_farZ = 1024;
int g_materialIdx;
//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
sampler g_DiffuseTexture;
sampler g_NormalTexture;
sampler g_SpecularTexture;
//--------------------------------------------------------------------------------------
// This shader outputs the pixel's color by modulating the texture's
// color with diffuse material color
//--------------------------------------------------------------------------------------
// <psMain>
DEFERRED_PS_OUTPUT main( VS_OUTPUT In )
{
DEFERRED_PS_OUTPUT output = (DEFERRED_PS_OUTPUT)0;
// calculate the color component
output.Color = g_MaterialDiffuseColor;
if ( g_DiffuseTexSet )
{
float4 texColor = tex2D( g_DiffuseTexture, In.TextureUV );
output.Color = texColor + g_MaterialDiffuseColor * ( 1 - texColor.a );
}
// calculate normal
float3 surfaceNormalViewSpace = 0;
if ( g_NormalTexSet )
{
float3 normalVS = normalize( In.Normal );
float3 tangentVS = normalize( In.Tangent );
float3 binormalVS = cross( normalVS, tangentVS );
float3x3 mtx = float3x3( tangentVS, binormalVS, normalVS );
float3 surfaceNormalTanSpace = tex2D( g_NormalTexture, In.TextureUV ) * 2.0 - 1.0;
// I noticed the other day that normal maps exported from Blender have y coordinate flipped.
// So I'm unflipping it here - but keep that fact in mind and pay attention to other
// normal maps - perhaps I got something wrong in the code
surfaceNormalTanSpace.y *= -1.0;
surfaceNormalViewSpace = mul( surfaceNormalTanSpace, mtx );
surfaceNormalViewSpace = normalize( surfaceNormalViewSpace );
}
else
{
surfaceNormalViewSpace = normalize( In.Normal );
}
// Flip the Z direction, so that Z values decrease as they get further away from the camera.
// It's required in order for our normals compression algorithm to work correctly
output.NormalSpec.rgb = surfaceNormalViewSpace * float3( 0.5, 0.5, -0.5 ) + 0.5;
output.DepthNormal = encodeDepthAndNormal( surfaceNormalViewSpace, In.PositionCS.z, g_farZ );
// calculate the specular color ( store info about the specularity in albedo's alpha channel
if ( g_SpecularTexSet )
{
output.NormalSpec.a = tex2D( g_SpecularTexture, In.TextureUV ).r;
}
else
{
// full specularity
output.NormalSpec.a = 1;
}
// store the material index and the UV coordinates
output.MatIdx = storeMaterialIndex( g_materialIdx );
return output;
}
Directional light shader:
// -------------------------------------------------------------------------------
// constants
// -------------------------------------------------------------------------------
float4 g_halfPixel;
float4 g_lightDirVS; // light direction in view space
float4 g_lightColor; // light color
float g_strength; // light strength
bool g_drawShadows; // should we take the shadow map into account while rendering the light?
int g_materialsTexSize;
//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
sampler g_Normals;
sampler g_Specular;
sampler g_SceneColor;
sampler g_ShadowMap;
sampler g_MaterialIndices;
sampler g_MaterialsDescr;
//--------------------------------------------------------------------------------------
// Helper structure that describes light parameters at the sampled point of the screen
//--------------------------------------------------------------------------------------
struct LightValues
{
float3 m_direction;
float m_reflectionFactor;
float3 m_reflectionVector;
float4 m_color;
};
//--------------------------------------------------------------------------------------
// Calculates the color of the pixel lit by the light described by the specified values
//--------------------------------------------------------------------------------------
float4 calcLightContribution( float2 UV, LightValues lightValues, sampler sceneColorTex, sampler materialIndices, sampler materialsDescr, int materialsTexSize )
{
// get the material's ambient color
float4 encodedMaterialIdx = tex2D( materialIndices, UV );
int materialIdx = readMaterialIndex( encodedMaterialIdx );
float4 matDiffuseCol = getMaterialComponent( materialsDescr, materialIdx, DIFFUSE_COLOR, materialsTexSize );
float4 matSpecularCol = getMaterialComponent( materialsDescr, materialIdx, SPECULAR_COLOR, materialsTexSize );
// I don't remember encoding the value of specular power, but for some reason
// it's encoded in the same way as the normals - investigate!
const float matShininess = matSpecularCol.a * 2 - 1;
matSpecularCol.a = 1;
float4 baseColor = tex2D( sceneColorTex, UV );
float4 outputColor = float4( 0, 0, 0, 1 );
if ( lightValues.m_reflectionFactor > 0.0 )
{
// calculate the specular factor
float specularValue = dot( lightValues.m_reflectionVector, float3( 0, 0, 1 ) );
float specularFactor = pow( max( specularValue, 1.0 ), matShininess );
// shininess occurs only where we allow it to be, and in our case that is regulated
// by the contents of the albedo texture alpha channel
specularFactor *= baseColor.a;
// calculate component albedo contributions for both the specular and alpha channels
float3 specularColor = matSpecularCol * specularFactor;
float3 diffuseColor = matDiffuseCol * baseColor.rgb * lightValues.m_reflectionFactor;
// calculate the final color by adding the two albedo colors and modulating them using the light's color
outputColor.rgb += ( diffuseColor + specularColor ) * lightValues.m_color.rgb;
}
return saturate( outputColor );
}
//--------------------------------------------------------------------------------------
// This method calculates light parameters values
//--------------------------------------------------------------------------------------
LightValues calculateDirectionalLightValue( float2 UV )
{
LightValues Out = (LightValues)0;
// set light ray direction
Out.m_direction = g_lightDirVS.xyz * float3( -1, -1, 1 );
// calculate the reflection factor of the light ray hitting the lit surface
float3 pixelNormalVS = normalize( tex2D( g_Normals, UV ) * 2.0 - 1.0 );
Out.m_reflectionFactor = dot( pixelNormalVS, Out.m_direction );
Out.m_reflectionVector = normalize( -reflect( Out.m_direction.xyz, pixelNormalVS ) );
// set the color of the light ( no attenuation requires, since directional lights have the same strength
// at every point in space )
Out.m_color = g_lightColor * g_strength;
return Out;
}
// -------------------------------------------------------------------------------
// main function
// -------------------------------------------------------------------------------
LIGHT_PS_OUT main( float2 UV : TEXCOORD0 )
{
LIGHT_PS_OUT Out = (LIGHT_PS_OUT)0;
// align texture coordinates
UV -= g_halfPixel.xy;
float lightPercentage = 1.0f;
if( g_drawShadows )
{
// use the shadow map to determine if we should render there
lightPercentage = tex2D( g_ShadowMap, UV ).r;
}
// color light and direction are constant across the entire space
if ( lightPercentage > 0.0f )
{
LightValues lightValue = calculateDirectionalLightValue( UV );
// calculate the light contribution to the scene color
Out.vColor = calcLightContribution( UV, lightValue, g_SceneColor, g_MaterialIndices, g_MaterialsDescr, g_materialsTexSize );
// modulate the output color by the light value
Out.vColor *= lightPercentage;
}
else
{
// save precious cycles - if the light doesn't reach the spot, don't calculate its contribution
Out.vColor = 0.0f;
}
return Out;
}