// Steep parallax mapping shader
// Based on shaders:
// Steep parallax mapping (c) 2005 Morgan McGuire and Max McGuire (Iron Lore Entertainment)
// Parallax occlusion mapping (c) 2005 Natalya Tatarchuk (ATI Research, Inc.)
//
struct PARALLAX_DATA
{
float4 vPosition : POSITION;
float2 vTexCoord : TEXCOORD0;
float3 vEye : TEXCOORD1;
float3 vLight : TEXCOORD2;
};
PARALLAX_DATA ParallaxVS(
float4 vPosition : POSITION,
float4 vNormal : NORMAL,
float2 vTexCoord : TEXCOORD0,
float3 vTangent : TEXCOORD1,
float3 vBinormal : TEXCOORD2
)
{
PARALLAX_DATA o;
// Tangent Space
float3 binormal = mul( vBinormal.xyz, mW );
float3 tangent = mul( vTangent.xyz, mW );
float3 normal = mul( vNormal.xyz, mW );
// Vertex Position -> World Space
float4 vPositionWorld = mul( vPosition, mW );// * fGeometryScale;
// Eye Vector
float3 eye = vCameraPos.xyz - vPositionWorld.xyz;
o.vEye.x = dot( eye, binormal );
o.vEye.y = dot( eye, tangent );
o.vEye.z = dot( eye, normal );
vPositionWorld *= fGeometryScale;
// Lighting
float3 light = (Lights[0].vPosition.xyz - vPositionWorld.xyz)*Lights[0].fAttenuation;
o.vLight.x = dot( light, binormal );
o.vLight.y = dot( light, tangent );
o.vLight.z = dot( light, normal );
// Texture Coords
o.vTexCoord = vTexCoord;
// Position
o.vPosition = mul( vPosition, mWVP );
// Finalize
return o;
}
const float fThreshold = 4;
const float fTexSize = 512;
const int maxSamples = 50;
const int minSamples = 8;
float4 ParallaxPS ( PARALLAX_DATA In ) : COLOR
{
float fHeight = 0.0;
float2 vTexCoord = In.vTexCoord;
float3 vEye = normalize( In.vEye );
// Compute current gradients:
float2 fTexCoordsPerSize = vTexCoord * fTexSize;
// Compute all 4 derivatives in x and y in one instruction:
float2 dxSize, dySize;
float2 dx, dy;
float4( dxSize, dx ) = ddx( float4( fTexCoordsPerSize, vTexCoord ) );
float4( dySize, dy ) = ddy( float4( fTexCoordsPerSize, vTexCoord ) );
// Find min of change in u and v across quad: compute du and dv magnitude across quad
float2 dTexCoords = dxSize * dxSize + dySize * dySize;
// standard mipmapping uses max here
float fMinTexCoordDelta = max( dTexCoords.x, dTexCoords.y );
//compute mip level (* 0.5 is effectively computing a square root before the )
float fMipLevel = max( 0.5 * log2( fMinTexCoordDelta ), 0 );
if ( fMipLevel <= fThreshold )
{
int nNumSteps = (int) lerp( maxSamples, minSamples, vEye.z );
float fStep = 1.0 / (float)nNumSteps;
float2 vDelta = float2( In.vEye.x, In.vEye.y ) * fParallaxOffset * fStep / In.vEye.z;
float fCurHeight = 1.0;
int nStepIndex = 0;
while ( nStepIndex < nNumSteps )
{
vTexCoord += vDelta;
fHeight = tex2Dgrad( HeightMapSampler, vTexCoord, dx, dy ).x;
fCurHeight -= fStep;
if ( fHeight > fCurHeight )
nStepIndex = nNumSteps + 1;
else
nStepIndex++;
}
}
// Bump Mapping
float3 vN = tex2D( NormalMapSampler, vTexCoord ) * 2.0 - 1.0;
// Lighting
float3 vLight = normalize( In.vLight );
float3 vHalfAngle = vLight + vEye;
float3 vHalf = normalize( vHalfAngle );
float fNdotL = saturate( dot( vN.xyz, vLight.xyz ) );
float fNdotH = saturate( dot( vN.xyz, vHalf.xyz ) );
float fSpec = pow( fNdotH, fMaterialPower );
float vAttenuation = saturate( 1.0 - dot( In.vLight, In.vLight ) );
float4 vDiffuse = Lights[0].vDiffuse * fNdotL * vAttenuation;
float4 vSpecular = Lights[0].vSpecular * fSpec * vAttenuation;
float selfShadow = 1.0;
if ( fMipLevel <= fThreshold && fNdotL > 0 )
{
// Trace a shadow ray along the light vector.
int nNumShadowSteps = (int) lerp( maxSamples, minSamples, vLight.z );
float fStep = 1.0 / (float)nNumShadowSteps;
float2 vShadowCoord = vTexCoord;
float2 vDelta = float2( vLight.x, vLight.y ) * fParallaxOffset * fStep / vLight.z;
float fCurHeight = fHeight + fStep * 0.1;
int nStepIndex = 0;
while ( nStepIndex < nNumShadowSteps )
{
vShadowCoord -= vDelta;
fHeight = tex2Dgrad( HeightMapSampler, vShadowCoord, dx, dy ).x;
fCurHeight += fStep;
if ( fHeight > fCurHeight || fCurHeight >= 1.0 )
nStepIndex = nNumShadowSteps + 1;
else
nStepIndex++;
}
// We are in shadow if we left the loop because
// we hit a point
selfShadow = fHeight < fCurHeight;
}
// Finalize
return vDiffuse * selfShadow * tex2D( DiffuseMapSampler, vTexCoord ) +
vSpecular * tex2D( SpecularMapSampler, vTexCoord );
}