//----------------------------------------------------------------------------- // parallax.fx // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Parameters //----------------------------------------------------------------------------- float4x4 mWVP : WorldViewProjection; float4x4 mW : World; texture texColorHeight : ColorMap0; float4 EyePositionWS : ViewPosition; float fHeightMapScale = 0.04; float fTexelsPerSide = sqrt(512.0 * 512.0 * 2); int nMaxSamples = 50; int nMinSamples = 4; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Samplers //----------------------------------------------------------------------------- sampler Sampler = sampler_state { Texture = (texColorHeight); MipFilter = LINEAR; MinFilter = LINEAR; MagFilter = LINEAR; AddressU = Wrap; AddressV = Wrap; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- struct vertex { float3 position : POSITION; float2 texcoord : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; }; //----------------------------------------------------------------------------- struct fragment { float4 position : POSITION; float2 texcoord : TEXCOORD0; float3 eye : TEXCOORD1; float3 normal : TEXCOORD2; }; //----------------------------------------------------------------------------- struct pixel { float4 color : COLOR; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Vertex Shader //----------------------------------------------------------------------------- fragment VS( vertex IN ) { fragment OUT; float4 VertexPositionWS = mul( float4(IN.position, 1), mW ); float3 P = VertexPositionWS.xyz; float3 E = EyePositionWS.xyz - P; // The per-vertex tangent, binormal, normal form the tangent to object space // rotation matrix. Multiply by the world matrix to form tangent to world space // rotation matrix. Then transpose the matrix to form the inverse tangent to // world space, otherwise called the world to tangent space rotation matrix. float3x3 tangentToWorldSpace; tangentToWorldSpace[0] = mul( IN.tangent, mW ); tangentToWorldSpace[1] = mul( IN.binormal, mW ); tangentToWorldSpace[2] = mul( IN.normal, mW ); float3x3 worldToTangentSpace = transpose(tangentToWorldSpace); OUT.position = mul( float4(IN.position, 1), mWVP ); // standard transformation to clip space OUT.texcoord = IN.texcoord; // pass through the original texcoords OUT.eye = mul( E, worldToTangentSpace ); // pass through the TANGENT space eye vector (un-normalized) OUT.normal = mul( IN.normal, worldToTangentSpace ); // pass through the TANGENT space normal vector (un-normalized) return OUT; } //----------------------------------------------------------------------------- // Pixel Shader //----------------------------------------------------------------------------- pixel PS( fragment IN ) { pixel OUT; float fParallaxLimit = length(IN.eye.xy) / IN.eye.z; // calculate the parallax offset vector max length fParallaxLimit *= fHeightMapScale; // scale the vector according to height-map scale float2 vOffset = normalize( -IN.eye.xy ); // calculate the parallax offset vector direction vOffset = vOffset * fParallaxLimit; // calculate the final maximum offset vector float3 E = normalize(IN.eye); float3 N = normalize(IN.normal); //int nNumSamples = (int)lerp( nMaxSamples, nMinSamples, dot( E, N ) ); // calculate dynamic number of samples (Tatarchuk's method) int nNumSamples = (int)(fParallaxLimit * fTexelsPerSide); // calculate dynamic number of samples (Zink's method) float fStepSize = 1.0 / (float)nNumSamples; // calculate the texcoord step size float2 dx, dy; dx = ddx( IN.texcoord ); // calculate the texcoord partial derivative in x in screen space for tex2Dgrad dy = ddy( IN.texcoord ); // calculate the texcoord partial derivative in y in screen space for tex2Dgrad float2 vOffsetStep = fStepSize * vOffset; float2 vCurrOffset = float2( 0, 0 ); float2 vLastOffset = float2( 0, 0 ); float2 vFinalOffset = float2( 0, 0 ); float4 vCurrSample; float4 vLastSample; float stepHeight = 1.0; int nCurrSample = 0; while ( nCurrSample < nNumSamples ) { vCurrSample = tex2Dgrad( Sampler, IN.texcoord + vCurrOffset, dx, dy ); // sample the current texcoord offset if ( vCurrSample.a > stepHeight ) { // calculate the linear intersection point float Ua = (vLastSample.a - (stepHeight+fStepSize)) / ( fStepSize + (vCurrSample.a - vLastSample.a)); vFinalOffset = vLastOffset + Ua * vOffsetStep; vCurrSample = tex2Dgrad( Sampler, IN.texcoord + vFinalOffset, dx, dy ); // sample the corrected tex coords nCurrSample = nNumSamples + 1; // exit the while loop } else { nCurrSample++; // increment to the next sample stepHeight -= fStepSize; // change the required height-map height vLastOffset = vCurrOffset; // remember this texcoord offset for next time vCurrOffset += vOffsetStep; // increment to the next texcoord offset vLastSample = vCurrSample; } } OUT.color = vCurrSample; // return the final sampled color value return OUT; } //----------------------------------------------------------------------------- // Techniques //----------------------------------------------------------------------------- technique LitTexturedSpecular { pass Pass0 { VertexShader = compile vs_3_0 VS(); PixelShader = compile ps_3_0 PS(); } } //-----------------------------------------------------------------------------