Friday 14 June 2013

Friday Shader Frenzy

My Brain Has Melted

Now that was a long day! There should be a law to working so many hours, especially when there is not a pot of lovely gold at the end of the day. Alas after many burned candles, I cannot show you a glorious cascade shaded rendered scene.  This is what I can show you:


Impressive huh?!  Believe it or not I have made amazing progress, it's just the last two bits that let me show you the shadows are the two bits I need to research more, namely attaching a depth surface to a shader so I can read it in DirectX 9 and using a "sample comparison state" similar to the DX11 method used in the example I have been mining.

To explain the above scene, the bottom part that shows red, green, yellow and blue highlights are some quick cascade divisions (hacked in), but the real divisions are in the shader ready to be used and modified from the app. The upper rectangles are four cascade shadow renders at different zoomed in distances to the area to be shadowed. As I am not rendering the depth as a color in the render target you cannot see anything but a solid color, but hopefully the 'depth' data has been preserved and that is the data I want (need) to use for the main render scene. I use them right now for debugging purposes.

If anyone knows a good tool that allows me to view a DirectX 9 depth/stencil surface in real-time, please do send the link :)

Code Attack

I usually don't assault you with code, but today I wanted to give you a glimpse of the shader Code that I am working on.  It's a Mark Blosser shader with my shadow mapping code added here and there, and some hacks so I could show you that nice screen shot.

SHADER CODE (in progress):

/********************************************************************************************
Shader Model 3 Rendering System for FPS Creator
by Mark Blosser  
email: mjblosser@gmail.com
website: www.mjblosser.com
-------------------------------------------------------------------
Description: 
Shader for segments. Uses lightmapping and normal/spec.
Performs parallax occlusion mapping using the cone-step method.
INVERTED heightmap stored in normal map alpha channel.
Conemap stored in normal map blue channel.
Requires D2/I/N textures.  All textures must be in texturebank folder.

*********************************************************************************************/

/**************MATRICES & UNTWEAKABLES *****************************************************/

// shadow mapping
matrix m_mShadow;
float4 m_vCascadeOffset[8];
float4 m_vCascadeScale[8];
int m_nCascadeLevels;
int             m_iPCFBlurForLoopStart; // For loop begin value. For a 5x5 Kernal this would be -2.
int             m_iPCFBlurForLoopEnd; // For loop end value. For a 5x5 kernel this would be 3.
float           m_fMinBorderPadding;     
float           m_fMaxBorderPadding;
float           m_fShadowBiasFromGUI;  // A shadow map offset to deal with self shadow artifacts.  
float           m_fShadowPartitionSize; 
float           m_fCascadeBlendArea; // Amount to overlap when blending between cascades.
float           m_fTexelSize; 
float           m_fNativeTexelSizeInX;
//float4          m_fCascadeFrustumsEyeSpaceDepthsFloat[2];  // The values along Z that seperate the cascades.
//float4          m_fCascadeFrustumsEyeSpaceDepthsFloat4[8];  // the values along Z that separte the cascades.  
float           m_fCascadeFrustumsEyeSpaceDepths[8];
float3          m_vLightDir;


float4x4 World : World;
float4x4 WorldInverse : WorldInverse;
float4x4 WorldIT : WorldInverseTranspose;
float4x4 WorldView : WorldView;
float4x4 WorldViewProjection : WorldViewProjection;
float4x4 View : View;
float4x4 ViewInverse : ViewInverse;
float4x4 ViewIT : ViewInverseTranspose;
float4x4 ViewProjection : ViewProjection;
float4x4 Projection : Projection;

float4x4 boneMatrix[60] : BoneMatrixPalette;
float4 eyePos : CameraPosition;
float time : Time;
float sintime : SinTime;

/**************VALUES PROVIDED FROM FPSC - NON TWEAKABLE**************************************/

float4 clipPlane : ClipPlane;  //cliplane for water plane

float4 LightSource // SUN LOCATION 
<   string UIType = "Fixed Light Source";
> = {2500.0f,2500.0f,50000.0f, 1.0f};

float4 AmbiColor : Ambient
<    string UIName =  "AmbiColor";    
> = {0.2f, 0.2f, 0.3f, 0.0f};

float4 AmbiColorOverride  //need to divide by 255 in shader
<    string UIName =  "AmbiColorOverride";    
> = {255.0f, 255.0f, 255.0f, 255.0f};

float4 SurfColor : Diffuse
<    string UIName =  "SurfColor";    
> = {1.0f, 1.0f, 1.0f, 1.0f};

//Supports dynamic lights (using CalcLighting function)
float4 g_lights_pos0;
float4 g_lights_pos1;
float4 g_lights_pos2;
float4 g_lights_pos3;
float4 g_lights_atten0;
float4 g_lights_atten1;
float4 g_lights_atten2;
float4 g_lights_atten3;
float4 g_lights_diffuse0;
float4 g_lights_diffuse1;
float4 g_lights_diffuse2;
float4 g_lights_diffuse3;

//SpotFlash Values from FPSC
float4 SpotFlashPos;  //SpotFlashPos.w is carrying the spotflash fadeout value
float4 SpotFlashColor; //remember this has not been divided by 255 before being sent from FPSC (I should fix this)
float SpotFlashRange   //fixed value that FPSC uses
<   string UIName =  "SpotFlash Range";    
> = {600.00};

//WATER Fog Color
float4 FogColor : Diffuse
<   string UIName =  "Fog Color";    
> = {0.0f, 0.0f, 0.0f, 0.0000001f};

//HUD Fog Color
float4 HudFogColor : Diffuse
<   string UIName =  "Hud Fog Color";    
> = {0.0f, 0.0f, 0.0f, 0.0000001f};

//HUD Fog Distances (near,far,0,0)
float4 HudFogDist : Diffuse
<   string UIName =  "Hud Fog Dist";    
> = {1.0f, 0.0f, 0.0f, 0.0000001f};


//Shader Variables pulled from FPI scripting 
float4 ShaderVariables : ShaderVariables
<    string UIName =  "Shader Variables";    
> = {1.0f, 1.0f, 1.0f, 1.0f};


/***************TEXTURES AND SAMPLERS***************************************************/
//For lightmapped ojbects (Lightmap, D, I, N)

texture LightMap : DiffuseMap
<
    string Name = "LM.tga";
    string type = "2D";
>;

texture DiffuseMap : DiffuseMap
<
    string Name = "D.tga";
    string type = "2D";
>;

texture NormalMap : DiffuseMap
<
    string Name = "N.tga";
    string type = "2D";
>;

texture DepthMapTX1 : DiffuseMap
<
    string Name = "DEPTH1.tga";
    string type = "2D";
>;
texture DepthMapTX2 : DiffuseMap
<
    string Name = "DEPTH1.tga";
    string type = "2D";
>;
texture DepthMapTX3 : DiffuseMap
<
    string Name = "DEPTH1.tga";
    string type = "2D";
>;
texture DepthMapTX4 : DiffuseMap
<
    string Name = "DEPTH1.tga";
    string type = "2D";
>;

//Lightmap texture
sampler2D LightmapSampler = sampler_state
{
    Texture   = <LightMap>;
    MipFilter = LINEAR;
    MinFilter = ANISOTROPIC;
    MagFilter = LINEAR;
};

//Diffuse Texture _D
sampler2D DiffuseSampler = sampler_state
{
    Texture   = <DiffuseMap>;
    MipFilter = LINEAR;
    MinFilter = ANISOTROPIC;
    MagFilter = LINEAR;
};

//Effect Texture _N (could be anything here - ill,spec,normal)
sampler2D NormalSampler = sampler_state
{
    Texture   = <NormalMap>;
    MipFilter = LINEAR;
    MinFilter = ANISOTROPIC;
    MagFilter = LINEAR;
};

//DepthMapForShadow (generated in app)
sampler2D DepthMap1 = sampler_state
{
Texture = <DepthMapTX1>;
};
sampler2D DepthMap2 = sampler_state
{
Texture = <DepthMapTX2>;
};
sampler2D DepthMap3 = sampler_state
{
Texture = <DepthMapTX3>;
};
sampler2D DepthMap4 = sampler_state
{
Texture = <DepthMapTX4>;
};

/************* DATA STRUCTS **************/

// structures for shadow map depth pass
struct IN_Depth
{
float4 Pos:POSITION; 
};
struct OUT_Depth
{
float4 OPos:POSITION; 
};

// structures for final render
struct appdata
 {
    float4 Position : POSITION;
    float2 UV0 : TEXCOORD0;
    float2 UV1 : TEXCOORD1;
    float4 Normal : NORMAL;    
};

/*data passed to pixel shader*/
struct vertexOutput
{
    float4 Position     : POSITION;
    float2 TexCoord     : TEXCOORD0;
    float2 TexCoordLM   : TEXCOORD1;
    float3 LightVec    : TEXCOORD2;
    float3 WorldNormal : TEXCOORD3;
    float4 WPos         : TEXCOORD4;
    float  WaterFog     : TEXCOORD5;  
    float  clip         : TEXCOORD6;
    float4 vTexShadow : TEXCOORD7;
    float4 vInterpPos   : TEXCOORD8; 
    float  vDepth       : TEXCOORD9;
};

/****** helper functions for shadow mapping*****/

void ComputeCoordinatesTransform( in int iCascadeIndex,
                                      in float4 InterpolatedPosition ,
                                      in out float4 vShadowTexCoord , 
                                      in out float4 vShadowTexCoordViewSpace ) 
{
    // Now that we know the correct map, we can transform the world space position of the current fragment                
    vShadowTexCoord = vShadowTexCoordViewSpace * m_vCascadeScale[iCascadeIndex];
    vShadowTexCoord += m_vCascadeOffset[iCascadeIndex];
    vShadowTexCoord.x *= m_fShadowPartitionSize;  // precomputed (float)iCascadeIndex / (float)CASCADE_CNT
    vShadowTexCoord.x += (m_fShadowPartitionSize * (float)iCascadeIndex ); 

void CalculateRightAndUpTexelDepthDeltas ( in float3 vShadowTexDDX,
                                           in float3 vShadowTexDDY,
                                           out float fUpTextDepthWeight,
                                           out float fRightTextDepthWeight )
{
// This function calculates the screen space depth for shadow space texels    
    // We use the derivatives in X and Y to create a transformation matrix.  Because these derivives give us the 
    // transformation from screen space to shadow space, we need the inverse matrix to take us from shadow space 
    // to screen space.  This new matrix will allow us to map shadow map texels to screen space.  This will allow 
    // us to find the screen space depth of a corresponding depth pixel.
    // This is not a perfect solution as it assumes the underlying geometry of the scene is a plane.  A more 
    // accureate way of finding the actual depth would be to do a deferred rendering approach and actually 
    // sample the depth (ca-ching dudes!)
    // Using an offset, or using variance shadow maps is a better approach to reducing these artifacts in most cases.
    
    float2x2 matScreentoShadow = float2x2( vShadowTexDDX.xy, vShadowTexDDY.xy );
    float fDeterminant = determinant ( matScreentoShadow );
    
    float fInvDeterminant = 1.0f / fDeterminant;
    
    float2x2 matShadowToScreen = float2x2 (
        matScreentoShadow._22 * fInvDeterminant, matScreentoShadow._12 * -fInvDeterminant, 
        matScreentoShadow._21 * -fInvDeterminant, matScreentoShadow._11 * fInvDeterminant );

    float2 vRightShadowTexelLocation = float2( m_fTexelSize, 0.0f );
    float2 vUpShadowTexelLocation = float2( 0.0f, m_fTexelSize );  
    
    // Transform the right pixel by the shadow space to screen space matrix.
    float2 vRightTexelDepthRatio = mul( vRightShadowTexelLocation,  matShadowToScreen );
    float2 vUpTexelDepthRatio = mul( vUpShadowTexelLocation,  matShadowToScreen );

    // We can now caculate how much depth changes when you move up or right in the shadow map.
    // We use the ratio of change in x and y times the dervivite in X and Y of the screen space 
    // depth to calculate this change.
    fUpTextDepthWeight = 
        vUpTexelDepthRatio.x * vShadowTexDDX.z 
        + vUpTexelDepthRatio.y * vShadowTexDDY.z;
    fRightTextDepthWeight = 
        vRightTexelDepthRatio.x * vShadowTexDDX.z 
        + vRightTexelDepthRatio.y * vShadowTexDDY.z;  
}

void CalculatePCFPercentLit ( in float4 vShadowTexCoord, 
                              in float fRightTexelDepthDelta, 
                              in float fUpTexelDepthDelta, 
                              in float fBlurRowSize,
                              out float fPercentLit ) 
{
// Use PCF to sample the depth map and return a percent lit value.
    fPercentLit = 0.0f;
/*
    // This loop could be unrolled, and texture immediate offsets could be used if the kernel size were fixed.
    // This would be performance improvment.
    //for( int x = m_iPCFBlurForLoopStart; x < m_iPCFBlurForLoopEnd; ++x ) 
    {
        //for( int y = m_iPCFBlurForLoopStart; y < m_iPCFBlurForLoopEnd; ++y ) 
        {
            float depthcompare = vShadowTexCoord.z;
            // A very simple solution to the depth bias problems of PCF is to use an offset.
            // Unfortunately, too much offset can lead to Peter-panning (shadows near the base of object disappear )
            // Too little offset can lead to shadow acne ( objects that should not be in shadow are partially self shadowed ).
            depthcompare -= m_fShadowBiasFromGUI;
            // Compare the transformed pixel depth to the depth read from the map.
            //fPercentLit += g_txShadow.SampleCmpLevelZero( g_samShadow, 
            //    float2( 
            //        vShadowTexCoord.x + ( ( (float) x ) * m_fNativeTexelSizeInX ) , 
            //        vShadowTexCoord.y + ( ( (float) y ) * m_fTexelSize ) 
            //        ), 
            //    depthcompare );
// LEE, Research sampler comparison coommand for DX9 : SampleCmpLevelZero
            //fPercentLit += g_txShadow.SampleCmpLevelZero( g_samShadow, 
            //    float2( vShadowTexCoord.x, vShadowTexCoord.y ), 
            //    depthcompare );
// 
//fPercentLit += tex2D(DepthMap1,float2(vShadowTexCoord.x, vShadowTexCoord.y));// < Depth ? 0.5f:0.0f);
        //}
    //}
    //fPercentLit /= (float)fBlurRowSize;
*/
}

void CalculateBlendAmountForInterval ( in int iCurrentCascadeIndex, 
                                       in out float fPixelDepth, 
                                       in out float fCurrentPixelsBlendBandLocation,
                                       out float fBlendBetweenCascadesAmount ) 
{
// Calculate amount to blend between two cascades and the band where blending will occure.
    // We need to calculate the band of the current shadow map where it will fade into the next cascade.
    // We can then early out of the expensive PCF for loop. 
    float fBlendInterval = m_fCascadeFrustumsEyeSpaceDepths[ iCurrentCascadeIndex  ];
    //if( iNextCascadeIndex > 1 ) 
    int fBlendIntervalbelowIndex = min(0, iCurrentCascadeIndex-1);
    fPixelDepth -= m_fCascadeFrustumsEyeSpaceDepths[ fBlendIntervalbelowIndex ];
    fBlendInterval -= m_fCascadeFrustumsEyeSpaceDepths[ fBlendIntervalbelowIndex ];
    
    // The current pixel's blend band location will be used to determine when we need to blend and by how much.
    fCurrentPixelsBlendBandLocation = fPixelDepth / fBlendInterval;
    fCurrentPixelsBlendBandLocation = 1.0f - fCurrentPixelsBlendBandLocation;
    // The fBlendBetweenCascadesAmount is our location in the blend band.
    fBlendBetweenCascadesAmount = fCurrentPixelsBlendBandLocation / m_fCascadeBlendArea;
}



/*******Vertex Shader***************************/

OUT_Depth VS_Depth(IN_Depth IN)
{
   OUT_Depth OUT;
   OUT.OPos = mul(IN.Pos,WorldViewProjection); 
   return OUT;
}

vertexOutput mainVS(appdata IN)   
{
vertexOutput OUT;
    
    //float4 tempPos = float4(IN.Position, 1);
    float4 worldSpacePos = mul(IN.Position, World);
    OUT.WPos =   worldSpacePos;  
    
    OUT.WorldNormal = normalize(mul(IN.Normal, WorldIT).xyz);
    
    float3 eyeoffset = float3 (10,100,0);
    //OUT.LightVec = normalize (eyePos+eyeoffset - worldSpacePos );
    OUT.LightVec = normalize (LightSource - worldSpacePos );
   
    OUT.Position = mul(IN.Position, WorldViewProjection);
    OUT.TexCoord  = IN.UV0 ; 
    OUT.TexCoordLM  = IN.UV1; 
  
    // calculate Water FOG colour
    float4 cameraPos = mul( worldSpacePos, View );
    float fogstrength = cameraPos.z * FogColor.w;
    OUT.WaterFog = min(fogstrength,1.0);
   
    // all shaders should send the clip value to the pixel shader (for refr/refl)                                                                     
    OUT.clip = dot(worldSpacePos, clipPlane);                                                                      
    
    OUT.vTexShadow = float4(0,0,0,0);
    OUT.vInterpPos = float4(0,0,0,0);
    OUT.vDepth = 0.0f;
    // SHADOW MAPPING - transform the shadow texture coordinates for all the cascades.
    OUT.vInterpPos = IN.Position;   
    OUT.vDepth = mul( IN.Position, WorldView ).z; 
    OUT.vTexShadow = mul( IN.Position, m_mShadow );
    return OUT;
}

/****************Framgent Shader*****************/

float4 PS_Depth(OUT_Depth IN) : COLOR
{
return float4(1,1,0,1);//(IN.Depth/LightRange)+0.01f;
}

float4 CalcSpotFlash( float3 worldNormal, float3 worldPos )
{
    float4 output = (float4)0.0;
    float3 toLight = (SpotFlashPos.xyz - worldPos.xyz);
    float3 lightDir = normalize( toLight );
    float lightDist = length( toLight );
    
    float MinFalloff = 100;  //falloff start distance
    float LinearFalloff = 1;
    float ExpFalloff = .005;  // 1/200
    SpotFlashPos.w = clamp(0,1,SpotFlashPos.w -.2);
    
    //classic attenuation - but never actually reaches zero
    //float fAtten = 1.0/(MinFalloff + (LinearFalloff*lightDist)); //bit faster linear atten only
    float fAtten = 1.0/(MinFalloff + (LinearFalloff*lightDist)+(ExpFalloff*lightDist*lightDist));
    //output += max(0,dot( lightDir, worldNormal ) * (SpotFlashColor) *fAtten * (SpotFlashPos.w) );
    output += (SpotFlashColor) *fAtten * (SpotFlashPos.w); //don't use normal, faster
    
    //faster attenuation function based on min and max falloff distances
    //float fAtten = saturate((500-lightDist)/(500-50));  //50 is minimum distance, 500 is end distance
    //output=fAtten*SpotFlashPos.w*SpotFlashColor*.01;  //fastest technique - doesn't use Normal
    //output = max(0,dot( lightDir, worldNormal )) * fAtten*SpotFlashPos.w*(SpotFlashColor/100);
        
    return output;
}

float4 CalcLighting(float3 Nb, float3 worldPos, float3 Vn, float4 diffusemap,float4 specmap)
{
    float4 output = (float4)0.0;
    
    // light 0
    float3 toLight = g_lights_pos0.xyz - worldPos;
    float lightDist = length( toLight );
    //float fAtten = saturate((200-lightDist)/(200-0)); //faster distance-based atten but needs lightrange from FPSC
    float fAtten = 1.0/dot( g_lights_atten0, float4(1,lightDist,lightDist*lightDist,0) );
    float3 lightDir = normalize( toLight );
    //output += max(0,dot( lightDir, Nb ) * g_lights_diffuse0 * fAtten * 1.7); //cheaper-no spec
    float3 halfvec = normalize(Vn + lightDir);
    float4 lit0 = lit(dot(lightDir,Nb),dot(halfvec,Nb),24); 
    output+= (lit0.y *g_lights_diffuse0 * fAtten * 1.7*diffusemap) + (lit0.z * g_lights_diffuse0 * fAtten *specmap);   
    // light 1
    toLight = g_lights_pos1.xyz - worldPos;
    lightDist = length( toLight );
    fAtten = 1.0/dot( g_lights_atten1, float4(1,lightDist,lightDist*lightDist,0) );
    lightDir = normalize( toLight );
    //output += max(0,dot( lightDir, Nb ) * g_lights_diffuse1 * fAtten * 1.7); //cheaper-no spec
    halfvec = normalize(Vn + lightDir);
    float4 lit1 = lit(dot(lightDir,Nb),dot(halfvec,Nb),24); 
    output+= (lit1.y *g_lights_diffuse1 * fAtten * 1.7*diffusemap) + (lit1.z * g_lights_diffuse1 * fAtten *specmap);
    
    // light 2 -maybe optimize by not calculating spec?
    toLight = g_lights_pos2.xyz - worldPos;
    lightDist = length( toLight );
    fAtten = 1.0/dot( g_lights_atten2, float4(1,lightDist,lightDist*lightDist,0) );
    lightDir = normalize( toLight );
    //output += max(0,dot( lightDir, Nb ) * g_lights_diffuse1 * fAtten * 1.7); //cheaper-no spec
    halfvec = normalize(Vn + lightDir);
    float4 lit2 = lit(dot(lightDir,Nb),dot(halfvec,Nb),24); 
    output+= (lit2.y *g_lights_diffuse2 * fAtten * 1.7*diffusemap) + (lit2.z * g_lights_diffuse2 * fAtten *specmap);    

// (pixel shader 2.0 runs out of space here)
//    // light 2 
//    toLight = g_lights_pos2.xyz - worldPos;
//    lightDist = length( toLight );
//    fAtten = 1.0/dot( g_lights_atten2, float4(1,lightDist,lightDist*lightDist,0) );
//    lightDir = normalize( toLight );
//    output += max(0,dot( lightDir, worldNormal ) * g_lights_diffuse2 * fAtten * 1.7);
//    // light 3
//    toLight = g_lights_pos3.xyz - worldPos;
//    lightDist = length( toLight );
//    fAtten = 1.0/dot( g_lights_atten3, float4(1,lightDist,lightDist*lightDist,0) );
//    lightDir = normalize( toLight );
//    output += max(0,dot( lightDir, worldNormal ) * g_lights_diffuse3 * fAtten * 1.7);

    return output;
}


float4 mainPS(vertexOutput IN) : COLOR
{
    float4 finalcolor;
    
    clip(IN.clip); // all shaders should receive the clip value
    
    float3 V  = (eyePos - IN.WPos);  
    float3 Vn  = normalize(V); 
    
    ////////TANGENT BASIS CONSTRUCTION FOR CONEMAPPING
    // Optimisation 3:
// assume M is orthogonal
float3 p = (IN.WPos);

    // get edge vectors of the pixel triangle
    float3 dp1 = ddx(p );
    float3 dp2 = ddy(p );
    float2 duv1 = ddx( IN.TexCoord.xy );
    float2 duv2 = ddy( IN.TexCoord.xy );

    // solve the linear system
    // (not much solving is left going here)
    float3x3 M = float3x3( dp1, dp2, cross(dp1,dp2) );
    float2x3 invTM = float2x3(cross(M[1],M[2]),cross(M[2],M[0]));
    float3 T = mul( float2( duv1.x, duv2.x ), invTM );
    float3 B = mul( float2( duv1.y, duv2.y ), invTM );
    float3 Nn = normalize(IN.WorldNormal);
    // construct tangent frame basis matrix 
    float3x3 tangentbasis = float3x3( 1*normalize(T), 1*normalize(B),Nn );
    //////////////////////////////////////////////////////////////////////////////// 
    
    ///////////////CREATE NEW UV COORDS///////////////////////////////////////
    float3 newUVs = float3(IN.TexCoord, 0.0);
    
    float3 rayVec = (IN.WPos- ViewInverse[3]);
    rayVec = mul(tangentbasis,rayVec);
    rayVec = normalize(rayVec);
rayVec.z = abs(rayVec.z);
float depth = 0.03;
rayVec.xy *= depth;
float dist = length(rayVec.xy);
    
    for( int i=0;i<16; i++ )
{
float4 tex = tex2D(NormalSampler, newUVs.xy);
float height = saturate(tex.w - newUVs.z);

float cone_ratio = tex.z*tex.z;
float stepDist = height * cone_ratio / (cone_ratio + dist);
newUVs += rayVec * stepDist;
}
    
    float4 lightmap = tex2D(LightmapSampler,IN.TexCoordLM); //sample lightmap texture    
    float4 diffusemap = tex2D(DiffuseSampler,newUVs.xy);  //sample D2 texture
    float4 specmap =diffusemap.w*2;
    
    float3 Nb = tex2D(NormalSampler,newUVs.xy);
Nb.xy = Nb.xy * 2.0 - 1.0;
//Nb.y = -Nb.y;
Nb.z = sqrt(1.0 - dot(Nb.xy, Nb.xy));
Nb = mul(Nb,tangentbasis);
Nb = normalize(Nb);
    
    float3 Ln = normalize(IN.LightVec);   //light vector
    float3 Hn = normalize(Vn+Ln);         //half-vector
    
    //main lighting equation
    float4 lighting = lit((dot(Ln,Nb)),dot(Hn,Nb),24); 

    float4 dynamiclighting = CalcLighting (Nb, IN.WPos.xyz, Vn, diffusemap,specmap);
    float4 spotflashlighting = CalcSpotFlash (IN.WorldNormal, IN.WPos.xyz);
    
    AmbiColor*= (AmbiColorOverride/255); //remember to account for ambient COLOR script changes

    //diffuse and ambient contribution
    float4 diffuseContrib = diffusemap *lighting.y *lightmap;
    
    //specular light contribution
    float4 specContrib = specmap *lighting.z *lightmap  ;
    
    //Ambient, Dynamic, and Spotflash contributions + half-strength lightmap    
    //float4 ambContrib = ((0.5*lightmap) + AmbiColor + dynamiclighting + spotflashlighting) * diffusemap;
    float4 ambContrib = (((0.5*lightmap) + AmbiColor + spotflashlighting) * diffusemap) + dynamiclighting;
    //float4 ambContrib = (lightmap+AmbiColor+dynamiclighting+spotflashlighting) *diffusemap; //full lightmap overly bright
    //float4 ambContrib = (AmbiColor + dynamiclighting + spotflashlighting) * diffusemap; //no extra lightmap (too dark)
    float4 result =  ambContrib + diffuseContrib;
/*
// per pixel shadow projection (for correct split)
float4 Proj1 = mul(ViewMat,mul(IN.WPos,LightProjMatrix1));
float4 Proj2 = mul(ViewMat,mul(IN.WPos,LightProjMatrix2));
float4 Proj3 = mul(ViewMat,mul(IN.WPos,LightProjMatrix3));
float4 Proj4 = mul(ViewMat,mul(IN.WPos,LightProjMatrix4));
//float Depth=(IN.Proj.z/LightRange);
//float shadowmap=1-(tex2Dproj(DepthMap,IN.Proj+float4(-0.8,-0.8,0,0)) < Depth ? 0.05111f:0.0f); // quick PCF softening
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0,-0.8,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0.8,-0.8,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(-0.8,0,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0,0,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0.8,0,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(-0.8,0.8,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0,0.8,0,0)) < Depth ? 0.05111f:0.0f);
//shadowmap=shadowmap-(tex2Dproj(DepthMap,IN.Proj+float4(0.8,0.8,0,0)) < Depth ? 0.05111f:0.0f);
//affect result by any shadows cast on this pixel
//float3 shadowProj1 = Proj1.xyz / Proj1.w;
//float3 shadowProj2 = Proj2.xyz / Proj2.w;
//float3 shadowProj3 = Proj3.xyz / Proj3.w;
//float3 shadowProj4 = Proj4.xyz / Proj4.w;
//bool s1 = all(abs(shadowProj1 - 0.5f) < 0.5f);
//bool s2 = all(abs(shadowProj2 - 0.5f) < 0.5f);
//bool s3 = all(abs(shadowProj3 - 0.5f) < 0.5f);
//bool s4 = all(abs(shadowProj4 - 0.5f) < 0.5f);
result = float4(0,0,0,1);
float Depth;
float shadowmap;
float fPixelDepth = IN.vDepth / 250.0f;
if ( fPixelDepth>0.75f )
{
Depth=(Proj4.z/LightRange);
shadowmap=1-(tex2Dproj(DepthMap4,Proj4+float4(0.0,0.0,0,0)) < Depth ? 0.5f:0.0f);
result = float4(0,1,0,1) * shadowmap;
}
if ( fPixelDepth>=0.5f && fPixelDepth<0.75f )
{
Depth=(Proj3.z/LightRange);
shadowmap=1-(tex2Dproj(DepthMap3,Proj3+float4(0.0,0.0,0,0)) < Depth ? 0.5f:0.0f);
result = float4(1,0,0,1) * shadowmap;
}
if ( fPixelDepth>=0.25f && fPixelDepth<0.5f )
{
Depth=(Proj2.z/LightRange);
shadowmap=1-(tex2Dproj(DepthMap2,Proj2+float4(0.0,0.0,0,0)) < Depth ? 0.5f:0.0f);
result = float4(1,1,0,1) * shadowmap;
}
if ( fPixelDepth>=0.0f && fPixelDepth<0.25f )
{
Depth=(Proj1.z/LightRange);
shadowmap=1-(tex2Dproj(DepthMap1,Proj1+float4(0.0,0.0,0,0)) < Depth ? 0.5f:0.0f);
result = float4(1,1,1,1) * shadowmap;
}
//result = float4(1,1,1,1) * shadowmap;
//result = result * shadowmap;
    */
    float4 vShadowMapTextureCoord = 0.0f;
    float4 vShadowMapTextureCoord_blend = 0.0f;
    float fPercentLit = 0.0f;
    float fPercentLit_blend = 0.0f;
    float fUpTextDepthWeight=0;
    float fRightTextDepthWeight=0;
    float fUpTextDepthWeight_blend=0;
    float fRightTextDepthWeight_blend=0;
    int iBlurRowSize = m_iPCFBlurForLoopEnd - m_iPCFBlurForLoopStart;
    iBlurRowSize *= iBlurRowSize;
    float fBlurRowSize = (float)iBlurRowSize;
    int iCascadeFound = 0;

    // The interval based selection technique compares the pixel's depth against the frustum's cascade divisions.
    float fCurrentPixelDepth;
    fCurrentPixelDepth = IN.vDepth;
    
    // This for loop is not necessary when the frustum is uniformaly divided and interval based selection is used.
    // In this case fCurrentPixelDepth could be used as an array lookup into the correct frustum. 
    int iCurrentCascadeIndex;
    float4 vShadowMapTextureCoordViewSpace = IN.vTexShadow;
iCurrentCascadeIndex = 3;
//if ( fCurrentPixelDepth < m_fCascadeFrustumsEyeSpaceDepths[2] ) iCurrentCascadeIndex = 2;
//if ( fCurrentPixelDepth < m_fCascadeFrustumsEyeSpaceDepths[1] ) iCurrentCascadeIndex = 1;
//if ( fCurrentPixelDepth < m_fCascadeFrustumsEyeSpaceDepths[0] ) iCurrentCascadeIndex = 0;
if ( fCurrentPixelDepth < 300 ) iCurrentCascadeIndex = 2;
if ( fCurrentPixelDepth < 80 ) iCurrentCascadeIndex = 1;
if ( fCurrentPixelDepth < 40 ) iCurrentCascadeIndex = 0;
   
    float4 color = 0;   

// Repeat text coord calculations for the next cascade. 
// The next cascade index is used for blurring between maps.
    int iNextCascadeIndex = 1;
iNextCascadeIndex = min ( 8 - 1, iCurrentCascadeIndex + 1 ); 

    float fBlendBetweenCascadesAmount = 1.0f;
    float fCurrentPixelsBlendBandLocation = 1.0f;
    CalculateBlendAmountForInterval ( iCurrentCascadeIndex, fCurrentPixelDepth, 
fCurrentPixelsBlendBandLocation, fBlendBetweenCascadesAmount );
    
    ComputeCoordinatesTransform( iCurrentCascadeIndex, 
                                 IN.vInterpPos, 
                                 vShadowMapTextureCoord, 
                                 vShadowMapTextureCoordViewSpace );    
    
// hack in for now 4AM!!
if ( iCurrentCascadeIndex==0 )
{
result += float4(0.5,0,0,1);//tex2D(DepthMap1,float2(vShadowTexCoord.x, vShadowTexCoord.y));
}
if ( iCurrentCascadeIndex==1 )
{
result += float4(0,0.5,0,1);//tex2D(DepthMap2,float2(vShadowTexCoord.x, vShadowTexCoord.y));
}
if ( iCurrentCascadeIndex==2 )
{
result += float4(0.5,0.5,0,1);//tex2D(DepthMap3,float2(vShadowTexCoord.x, vShadowTexCoord.y));
}
if ( iCurrentCascadeIndex==3 )
{
result += float4(0,0,0.5,1);//tex2D(DepthMap4,float2(vShadowTexCoord.x, vShadowTexCoord.y));
}
    //CalculatePCFPercentLit ( vShadowMapTextureCoord, fRightTextDepthWeight, 
    //                        fUpTextDepthWeight, fBlurRowSize, fPercentLit );
                                             
if( fCurrentPixelsBlendBandLocation < m_fCascadeBlendArea) 
{  
// the current pixel is within the blend band.
// Repeat text coord calculations for the next cascade. 
// The next cascade index is used for blurring between maps.
//if( !SELECT_CASCADE_BY_INTERVAL_FLAG ) 
//{
// vShadowMapTextureCoord_blend = vShadowMapTextureCoordViewSpace * m_vCascadeScale[iNextCascadeIndex];
// vShadowMapTextureCoord_blend += m_vCascadeOffset[iNextCascadeIndex];
//}
ComputeCoordinatesTransform( iNextCascadeIndex, IN.vInterpPos, 
vShadowMapTextureCoord_blend, 
vShadowMapTextureCoordViewSpace );  
   
// We repeat the calcuation for the next cascade layer, when blending between maps.
if( fCurrentPixelsBlendBandLocation < m_fCascadeBlendArea) 
{  
// the current pixel is within the blend band.
CalculatePCFPercentLit ( vShadowMapTextureCoord_blend, fRightTextDepthWeight_blend, 
fUpTextDepthWeight_blend, fBlurRowSize, fPercentLit_blend );
fPercentLit = lerp( fPercentLit_blend, fPercentLit, fBlendBetweenCascadesAmount ); 
// Blend the two calculated shadows by the blend amount.
}   
}

// use fPercentLit to apply the shadow :)
//result = lerp( float4(0,0,0,1), result, fPercentLit );
    //calculate hud pixel-fog
    float4 cameraPos = mul(IN.WPos, View);
    float hudfogfactor = saturate((cameraPos.z- HudFogDist.x)/(HudFogDist.y - HudFogDist.x));
    
    //Mix in HUD Fog with final color;
    float4 hudfogresult = lerp(result,HudFogColor,hudfogfactor);
    
    //And Finally add in any Water Fog   
    float4 waterfogresult = lerp(hudfogresult,FogColor,IN.WaterFog);
    finalcolor=float4(waterfogresult);    
    return finalcolor;
    
}


/****** technique ********************************/

technique DepthMap
{
  pass p0
    {
VertexShader = compile vs_2_0 VS_Depth(); 
PixelShader  = compile ps_2_0 PS_Depth();
   }
}

technique ShadowMapping
{
    pass P0
    {
        // shaders
        VertexShader = compile vs_3_0 mainVS();
        PixelShader  = compile ps_3_0 mainPS();
        CullMode = None;
        AlphaBlendEnable = FALSE;
        AlphaTestEnable = FALSE;
    }
}

Signing Off

I am keen to get the shadows working before I forget all this so will be putting some hours over the weekend to complete it.  The good news is that all the complex maths and intricate techniques for producing great shadows and filtering them are in the C++ and Shader code, I just have to get the DirectX surfaces playing nicely so we can see them.  Hopefully a few more hours will crack it.  Wish my brain luck!

5 comments:

  1. I wish you every luck possible so you get this running smoothly.

    ReplyDelete
  2. Very nice work! I like the idea of this dev-blog! Good luck!

    ReplyDelete
  3. Good luck, Lee's brain! Oh, Andy got to it first.... ;)

    ReplyDelete
  4. Still now new news? Lees brain has sunken into the deep shadows I think ;D

    ReplyDelete