Share Pastebin
Guest
Public paste!

Untitled

By: a guest | Feb 16th, 2010 | Syntax: None | Size: 22.17 KB | Hits: 37 | Expires: Never
Copy text to clipboard
  1. //--------------------------------------------------------------------------------------
  2. // File: ParallaxOcclusionMapping.fx
  3. //
  4. // Parallax occlusion mapping implementation
  5. //                                        
  6. // Implementation of the algorithm as described in "Dynamic Parallax Occlusion
  7. // Mapping with Approximate Soft Shadows" paper, by N. Tatarchuk, ATI Research,
  8. // to appear in the proceedings of ACM Symposium on Interactive 3D Graphics and Games, 2006.                                            
  9. //                                                                              
  10. // For examples of use in a real-time scene, see ATI X1K demo "ToyShop":        
  11. //    http://www.ati.com/developer/demos/rx1800.html                            
  12. //
  13. // Copyright (c) ATI Research, Inc. All rights reserved.
  14. //--------------------------------------------------------------------------------------
  15.  
  16. //--------------------------------------------------------------------------------------
  17. // Global variables
  18. //--------------------------------------------------------------------------------------
  19.  
  20. texture g_baseTexture;              // Base color texture
  21. texture g_nmhTexture;               // Normal map and height map texture pair
  22.  
  23. float4 g_materialAmbientColor;      // Material's ambient color
  24. float4 g_materialDiffuseColor;      // Material's diffuse color
  25. float4 g_materialSpecularColor;     // Material's specular color
  26.  
  27. float  g_fSpecularExponent;         // Material's specular exponent
  28. bool   g_bAddSpecular;              // Toggles rendering with specular or without
  29.  
  30. // Light parameters:
  31. float3 g_LightDir;                  // Light's direction in world space
  32. float4 g_LightDiffuse;              // Light's diffuse color
  33. float4 g_LightAmbient;              // Light's ambient color
  34.  
  35. float4   g_vEye;                    // Camera's location
  36. float    g_fBaseTextureRepeat;      // The tiling factor for base and normal map textures
  37. float    g_fHeightMapScale;         // Describes the useful range of values for the height field
  38.  
  39. // Matrices:
  40. float4x4 g_mWorld;                  // World matrix for object
  41. float4x4 g_mWorldViewProjection;    // World * View * Projection matrix
  42. float4x4 g_mView;                   // View matrix
  43.  
  44. bool     g_bVisualizeLOD;           // Toggles visualization of level of detail colors
  45. bool     g_bVisualizeMipLevel;      // Toggles visualization of mip level
  46. bool     g_bDisplayShadows;         // Toggles display of self-occlusion based shadows
  47.  
  48. float2   g_vTextureDims;            // Specifies texture dimensions for computation of mip level at
  49.                                     // render time (width, height)
  50.  
  51. int      g_nLODThreshold;           // The mip level id for transitioning between the full computation
  52.                                     // for parallax occlusion mapping and the bump mapping computation
  53.  
  54. float    g_fShadowSoftening;        // Blurring factor for the soft shadows computation
  55.  
  56. int      g_nMinSamples;             // The minimum number of samples for sampling the height field profile
  57. int      g_nMaxSamples;             // The maximum number of samples for sampling the height field profile
  58.  
  59.  
  60.  
  61. //--------------------------------------------------------------------------------------
  62. // Texture samplers
  63. //--------------------------------------------------------------------------------------
  64. sampler tBase =
  65. sampler_state
  66. {
  67.     Texture = < g_baseTexture >;
  68.     MipFilter = LINEAR;
  69.     MinFilter = LINEAR;
  70.     MagFilter = LINEAR;
  71. };
  72. sampler tNormalHeightMap =
  73. sampler_state
  74. {
  75.     Texture = < g_nmhTexture >;
  76.     MipFilter = LINEAR;
  77.     MinFilter = LINEAR;
  78.     MagFilter = LINEAR;
  79. };
  80.  
  81.  
  82. //--------------------------------------------------------------------------------------
  83. // Vertex shader output structure
  84. //--------------------------------------------------------------------------------------
  85. struct VS_OUTPUT
  86. {
  87.     float4 position          : POSITION;
  88.     float2 texCoord          : TEXCOORD0;
  89.     float3 vLightTS          : TEXCOORD1;   // light vector in tangent space, denormalized
  90.     float3 vViewTS           : TEXCOORD2;   // view vector in tangent space, denormalized
  91.     float2 vParallaxOffsetTS : TEXCOORD3;   // Parallax offset vector in tangent space
  92.     float3 vNormalWS         : TEXCOORD4;   // Normal vector in world space
  93.     float3 vViewWS           : TEXCOORD5;   // View vector in world space
  94.    
  95. };  
  96.  
  97.  
  98. //--------------------------------------------------------------------------------------
  99. // This shader computes standard transform and lighting
  100. //--------------------------------------------------------------------------------------
  101. VS_OUTPUT RenderSceneVS( float4 inPositionOS  : POSITION,
  102.                          float2 inTexCoord    : TEXCOORD0,
  103.                          float3 vInNormalOS   : NORMAL,
  104.                          float3 vInBinormalOS : BINORMAL,
  105.                          float3 vInTangentOS  : TANGENT )
  106. {
  107.     VS_OUTPUT Out;
  108.        
  109.     // Transform and output input position
  110.     Out.position = mul( inPositionOS, g_mWorldViewProjection );
  111.        
  112.     // Propagate texture coordinate through:
  113.     Out.texCoord = inTexCoord * g_fBaseTextureRepeat;
  114.  
  115.     // Transform the normal, tangent and binormal vectors from object space to homogeneous projection space:
  116.     float3 vNormalWS   = mul( vInNormalOS,   (float3x3) g_mWorld );
  117.     float3 vTangentWS  = mul( vInTangentOS,  (float3x3) g_mWorld );
  118.     float3 vBinormalWS = mul( vInBinormalOS, (float3x3) g_mWorld );
  119.    
  120.     // Propagate the world space vertex normal through:  
  121.     Out.vNormalWS = vNormalWS;
  122.    
  123.     vNormalWS   = normalize( vNormalWS );
  124.     vTangentWS  = normalize( vTangentWS );
  125.     vBinormalWS = normalize( vBinormalWS );
  126.    
  127.     // Compute position in world space:
  128.     float4 vPositionWS = mul( inPositionOS, g_mWorld );
  129.                  
  130.     // Compute and output the world view vector (unnormalized):
  131.     float3 vViewWS = g_vEye - vPositionWS;
  132.     Out.vViewWS = vViewWS;
  133.  
  134.     // Compute denormalized light vector in world space:
  135.     float3 vLightWS = g_LightDir;
  136.        
  137.     // Normalize the light and view vectors and transform it to the tangent space:
  138.     float3x3 mWorldToTangent = float3x3( vTangentWS, vBinormalWS, vNormalWS );
  139.        
  140.     // Propagate the view and the light vectors (in tangent space):
  141.     Out.vLightTS = mul( vLightWS, mWorldToTangent );
  142.     Out.vViewTS  = mul( mWorldToTangent, vViewWS  );
  143.        
  144.     // Compute the ray direction for intersecting the height field profile with
  145.     // current view ray. See the above paper for derivation of this computation.
  146.          
  147.     // Compute initial parallax displacement direction:
  148.     float2 vParallaxDirection = normalize(  Out.vViewTS.xy );
  149.        
  150.     // The length of this vector determines the furthest amount of displacement:
  151.     float fLength         = length( Out.vViewTS );
  152.     float fParallaxLength = sqrt( fLength * fLength - Out.vViewTS.z * Out.vViewTS.z ) / Out.vViewTS.z;
  153.        
  154.     // Compute the actual reverse parallax displacement vector:
  155.     Out.vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
  156.        
  157.     // Need to scale the amount of displacement to account for different height ranges
  158.     // in height maps. This is controlled by an artist-editable parameter:
  159.     Out.vParallaxOffsetTS *= g_fHeightMapScale;
  160.  
  161.    return Out;
  162. }  
  163.  
  164.  
  165. //--------------------------------------------------------------------------------------
  166. // Pixel shader output structure
  167. //--------------------------------------------------------------------------------------
  168. struct PS_OUTPUT
  169. {
  170.     float4 RGBColor : COLOR0;  // Pixel color    
  171. };
  172.  
  173. struct PS_INPUT
  174. {
  175.    float2 texCoord          : TEXCOORD0;
  176.    float3 vLightTS          : TEXCOORD1;   // light vector in tangent space, denormalized
  177.    float3 vViewTS           : TEXCOORD2;   // view vector in tangent space, denormalized
  178.    float2 vParallaxOffsetTS : TEXCOORD3;   // Parallax offset vector in tangent space
  179.    float3 vNormalWS         : TEXCOORD4;   // Normal vector in world space
  180.    float3 vViewWS           : TEXCOORD5;   // View vector in world space
  181. };
  182.  
  183.  
  184. //--------------------------------------------------------------------------------------
  185. // Function:    ComputeIllumination
  186. //
  187. // Description: Computes phong illumination for the given pixel using its attribute
  188. //              textures and a light vector.
  189. //--------------------------------------------------------------------------------------
  190. float4 ComputeIllumination( float2 texCoord, float3 vLightTS, float3 vViewTS, float fOcclusionShadow )
  191. {
  192.    // Sample the normal from the normal map for the given texture sample:
  193.    float3 vNormalTS = normalize( tex2D( tNormalHeightMap, texCoord ) * 2 - 1 );
  194.    
  195.    // Sample base map:
  196.    float4 cBaseColor = tex2D( tBase, texCoord );
  197.    
  198.    // Compute diffuse color component:
  199.    float3 vLightTSAdj = float3( vLightTS.x, -vLightTS.y, vLightTS.z );
  200.    
  201.    float4 cDiffuse = saturate( dot( vNormalTS, vLightTSAdj )) * g_materialDiffuseColor;
  202.    
  203.    // Compute the specular component if desired:  
  204.    float4 cSpecular = 0;
  205.    if ( g_bAddSpecular )
  206.    {
  207.       float3 vReflectionTS = normalize( 2 * dot( vViewTS, vNormalTS ) * vNormalTS - vViewTS );
  208.            
  209.       float fRdotL = saturate( dot( vReflectionTS, vLightTSAdj ));
  210.       cSpecular = saturate( pow( fRdotL, g_fSpecularExponent )) * g_materialSpecularColor;
  211.    }
  212.    
  213.    // Composite the final color:
  214.    float4 cFinalColor = (( g_materialAmbientColor + cDiffuse ) * cBaseColor + cSpecular ) * fOcclusionShadow;
  215.    
  216.    return cFinalColor;  
  217. }  
  218.  
  219.  
  220. //--------------------------------------------------------------------------------------
  221. // Parallax occlusion mapping pixel shader
  222. //
  223. // Note: this shader contains several educational modes that would not be in the final
  224. //       game or other complicated scene rendering. The blocks of code in various "if"
  225. //       statements for turning off visual qualities (such as visual level of detail
  226. //       or specular or shadows, etc), can be handled differently, and more optimally.
  227. //       It is implemented here purely for educational purposes.
  228. //--------------------------------------------------------------------------------------
  229. float4 RenderScenePS( PS_INPUT i ) : COLOR0
  230. {
  231.  
  232.    //  Normalize the interpolated vectors:
  233.    float3 vViewTS   = normalize( i.vViewTS  );
  234.    float3 vViewWS   = normalize( i.vViewWS  );
  235.    float3 vLightTS  = normalize( i.vLightTS );
  236.    float3 vNormalWS = normalize( i.vNormalWS );
  237.      
  238.    float4 cResultColor = float4( 0, 0, 0, 1 );
  239.  
  240.    // Adaptive in-shader level-of-detail system implementation. Compute the
  241.    // current mip level explicitly in the pixel shader and use this information
  242.    // to transition between different levels of detail from the full effect to
  243.    // simple bump mapping. See the above paper for more discussion of the approach
  244.    // and its benefits.
  245.    
  246.    // Compute the current gradients:
  247.    float2 fTexCoordsPerSize = i.texCoord * g_vTextureDims;
  248.  
  249.    // Compute all 4 derivatives in x and y in a single instruction to optimize:
  250.    float2 dxSize, dySize;
  251.    float2 dx, dy;
  252.  
  253.    float4( dxSize, dx ) = ddx( float4( fTexCoordsPerSize, i.texCoord ) );
  254.    float4( dySize, dy ) = ddy( float4( fTexCoordsPerSize, i.texCoord ) );
  255.                  
  256.    float  fMipLevel;      
  257.    float  fMipLevelInt;    // mip level integer portion
  258.    float  fMipLevelFrac;   // mip level fractional amount for blending in between levels
  259.  
  260.    float  fMinTexCoordDelta;
  261.    float2 dTexCoords;
  262.  
  263.    // Find min of change in u and v across quad: compute du and dv magnitude across quad
  264.    dTexCoords = dxSize * dxSize + dySize * dySize;
  265.  
  266.    // Standard mipmapping uses max here
  267.    fMinTexCoordDelta = max( dTexCoords.x, dTexCoords.y );
  268.  
  269.    // Compute the current mip level  (* 0.5 is effectively computing a square root before )
  270.    fMipLevel = max( 0.5 * log2( fMinTexCoordDelta ), 0 );
  271.    
  272.    // Start the current sample located at the input texture coordinate, which would correspond
  273.    // to computing a bump mapping result:
  274.    float2 texSample = i.texCoord;
  275.    
  276.    // Multiplier for visualizing the level of detail (see notes for 'nLODThreshold' variable
  277.    // for how that is done visually)
  278.    float4 cLODColoring = float4( 1, 1, 3, 1 );
  279.  
  280.    float fOcclusionShadow = 1.0;
  281.  
  282.    if ( fMipLevel <= (float) g_nLODThreshold )
  283.    {
  284.       //===============================================//
  285.       // Parallax occlusion mapping offset computation //
  286.       //===============================================//
  287.  
  288.       // Utilize dynamic flow control to change the number of samples per ray
  289.       // depending on the viewing angle for the surface. Oblique angles require
  290.       // smaller step sizes to achieve more accurate precision for computing displacement.
  291.       // We express the sampling rate as a linear function of the angle between
  292.       // the geometric normal and the view direction ray:
  293.       int nNumSteps = (int) lerp( g_nMaxSamples, g_nMinSamples, dot( vViewWS, vNormalWS ) );
  294.  
  295.       // Intersect the view ray with the height field profile along the direction of
  296.       // the parallax offset ray (computed in the vertex shader. Note that the code is
  297.       // designed specifically to take advantage of the dynamic flow control constructs
  298.       // in HLSL and is very sensitive to specific syntax. When converting to other examples,
  299.       // if still want to use dynamic flow control in the resulting assembly shader,
  300.       // care must be applied.
  301.       //
  302.       // In the below steps we approximate the height field profile as piecewise linear
  303.       // curve. We find the pair of endpoints between which the intersection between the
  304.       // height field profile and the view ray is found and then compute line segment
  305.       // intersection for the view ray and the line segment formed by the two endpoints.
  306.       // This intersection is the displacement offset from the original texture coordinate.
  307.       // See the above paper for more details about the process and derivation.
  308.       //
  309.  
  310.       float fCurrHeight = 0.0;
  311.       float fStepSize   = 1.0 / (float) nNumSteps;
  312.       float fPrevHeight = 1.0;
  313.       float fNextHeight = 0.0;
  314.  
  315.       int    nStepIndex = 0;
  316.       bool   bCondition = true;
  317.  
  318.       float2 vTexOffsetPerStep = fStepSize * i.vParallaxOffsetTS;
  319.       float2 vTexCurrentOffset = i.texCoord;
  320.       float  fCurrentBound     = 1.0;
  321.       float  fParallaxAmount   = 0.0;
  322.  
  323.       float2 pt1 = 0;
  324.       float2 pt2 = 0;
  325.        
  326.       float2 texOffset2 = 0;
  327.  
  328.       while ( nStepIndex < nNumSteps )
  329.       {
  330.          vTexCurrentOffset -= vTexOffsetPerStep;
  331.  
  332.          // Sample height map which in this case is stored in the alpha channel of the normal map:
  333.          fCurrHeight = tex2Dgrad( tNormalHeightMap, vTexCurrentOffset, dx, dy ).a;
  334.  
  335.          fCurrentBound -= fStepSize;
  336.  
  337.          if ( fCurrHeight > fCurrentBound )
  338.          {  
  339.             pt1 = float2( fCurrentBound, fCurrHeight );
  340.             pt2 = float2( fCurrentBound + fStepSize, fPrevHeight );
  341.  
  342.             texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
  343.  
  344.             nStepIndex = nNumSteps + 1;
  345.             fPrevHeight = fCurrHeight;
  346.          }
  347.          else
  348.          {
  349.             nStepIndex++;
  350.             fPrevHeight = fCurrHeight;
  351.          }
  352.       }  
  353.  
  354.       float fDelta2 = pt2.x - pt2.y;
  355.       float fDelta1 = pt1.x - pt1.y;
  356.      
  357.       float fDenominator = fDelta2 - fDelta1;
  358.      
  359.       // SM 3.0 requires a check for divide by zero, since that operation will generate
  360.       // an 'Inf' number instead of 0, as previous models (conveniently) did:
  361.       if ( fDenominator == 0.0f )
  362.       {
  363.          fParallaxAmount = 0.0f;
  364.       }
  365.       else
  366.       {
  367.          fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
  368.       }
  369.      
  370.       float2 vParallaxOffset = i.vParallaxOffsetTS * (1 - fParallaxAmount );
  371.  
  372.       // The computed texture offset for the displaced point on the pseudo-extruded surface:
  373.       float2 texSampleBase = i.texCoord - vParallaxOffset;
  374.       texSample = texSampleBase;
  375.  
  376.       // Lerp to bump mapping only if we are in between, transition section:
  377.        
  378.       cLODColoring = float4( 1, 1, 1, 1 );
  379.  
  380.       if ( fMipLevel > (float)(g_nLODThreshold - 1) )
  381.       {
  382.          // Lerp based on the fractional part:
  383.          fMipLevelFrac = modf( fMipLevel, fMipLevelInt );
  384.  
  385.          if ( g_bVisualizeLOD )
  386.          {
  387.             // For visualizing: lerping from regular POM-resulted color through blue color for transition layer:
  388.             cLODColoring = float4( 1, 1, max( 1, 2 * fMipLevelFrac ), 1 );
  389.          }
  390.  
  391.          // Lerp the texture coordinate from parallax occlusion mapped coordinate to bump mapping
  392.          // smoothly based on the current mip level:
  393.          texSample = lerp( texSampleBase, i.texCoord, fMipLevelFrac );
  394.  
  395.      }  
  396.      
  397.      if ( g_bDisplayShadows == true )
  398.      {
  399.         float2 vLightRayTS = vLightTS.xy * g_fHeightMapScale;
  400.      
  401.         // Compute the soft blurry shadows taking into account self-occlusion for
  402.         // features of the height field:
  403.    
  404.         float sh0 =  tex2Dgrad( tNormalHeightMap, texSampleBase, dx, dy ).a;
  405.         float shA = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.88, dx, dy ).a - sh0 - 0.88 ) *  1 * g_fShadowSoftening;
  406.         float sh9 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.77, dx, dy ).a - sh0 - 0.77 ) *  2 * g_fShadowSoftening;
  407.         float sh8 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.66, dx, dy ).a - sh0 - 0.66 ) *  4 * g_fShadowSoftening;
  408.         float sh7 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.55, dx, dy ).a - sh0 - 0.55 ) *  6 * g_fShadowSoftening;
  409.         float sh6 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.44, dx, dy ).a - sh0 - 0.44 ) *  8 * g_fShadowSoftening;
  410.         float sh5 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.33, dx, dy ).a - sh0 - 0.33 ) * 10 * g_fShadowSoftening;
  411.         float sh4 = (tex2Dgrad( tNormalHeightMap, texSampleBase + vLightRayTS * 0.22, dx, dy ).a - sh0 - 0.22 ) * 12 * g_fShadowSoftening;
  412.    
  413.         // Compute the actual shadow strength:
  414.         fOcclusionShadow = 1 - max( max( max( max( max( max( shA, sh9 ), sh8 ), sh7 ), sh6 ), sh5 ), sh4 );
  415.      
  416.         // The previous computation overbrightens the image, let's adjust for that:
  417.         fOcclusionShadow = fOcclusionShadow * 0.6 + 0.4;        
  418.      }      
  419.    }  
  420.  
  421.    // Compute resulting color for the pixel:
  422.    cResultColor = ComputeIllumination( texSample, vLightTS, vViewTS, fOcclusionShadow );
  423.              
  424.    if ( g_bVisualizeLOD )
  425.    {
  426.       cResultColor *= cLODColoring;
  427.    }
  428.    
  429.    // Visualize currently computed mip level, tinting the color blue if we are in
  430.    // the region outside of the threshold level:
  431.    if ( g_bVisualizeMipLevel )
  432.    {
  433.       cResultColor = fMipLevel.xxxx;      
  434.    }  
  435.  
  436.    // If using HDR rendering, make sure to tonemap the resuld color prior to outputting it.
  437.    // But since this example isn't doing that, we just output the computed result color here:
  438.    return cResultColor;
  439. }  
  440.  
  441.  
  442. //--------------------------------------------------------------------------------------
  443. // Bump mapping shader
  444. //--------------------------------------------------------------------------------------
  445. float4 RenderSceneBumpMapPS( PS_INPUT i ) : COLOR0
  446. {
  447.    //  Normalize the interpolated vectors:
  448.    float3 vViewTS   = normalize( i.vViewTS  );
  449.    float3 vLightTS  = normalize( i.vLightTS );
  450.      
  451.    float4 cResultColor = float4( 0, 0, 0, 1 );
  452.  
  453.    // Start the current sample located at the input texture coordinate, which would correspond
  454.    // to computing a bump mapping result:
  455.    float2 texSample = i.texCoord;
  456.  
  457.    // Compute resulting color for the pixel:
  458.    cResultColor = ComputeIllumination( texSample, vLightTS, vViewTS, 1.0f );
  459.              
  460.    // If using HDR rendering, make sure to tonemap the resuld color prior to outputting it.
  461.    // But since this example isn't doing that, we just output the computed result color here:
  462.    return cResultColor;
  463. }  
  464.  
  465.  
  466. //--------------------------------------------------------------------------------------
  467. // Apply parallax mapping with offset limiting technique to the current pixel
  468. //--------------------------------------------------------------------------------------
  469. float4 RenderSceneParallaxMappingPS( PS_INPUT i ) : COLOR0
  470. {
  471.    const float sfHeightBias = 0.01;
  472.    
  473.    //  Normalize the interpolated vectors:
  474.    float3 vViewTS   = normalize( i.vViewTS  );
  475.    float3 vLightTS  = normalize( i.vLightTS );
  476.    
  477.    // Sample the height map at the current texture coordinate:
  478.    float fCurrentHeight = tex2D( tNormalHeightMap, i.texCoord ).a;
  479.    
  480.    // Scale and bias this height map value:
  481.    float fHeight = fCurrentHeight * g_fHeightMapScale + sfHeightBias;
  482.    
  483.    // Perform offset limiting if desired:
  484.    fHeight /= vViewTS.z;
  485.    
  486.    // Compute the offset vector for approximating parallax:
  487.    float2 texSample = i.texCoord + vViewTS.xy * fHeight;
  488.    
  489.    float4 cResultColor = float4( 0, 0, 0, 1 );
  490.  
  491.    // Compute resulting color for the pixel:
  492.    cResultColor = ComputeIllumination( texSample, vLightTS, vViewTS, 1.0f );
  493.              
  494.    // If using HDR rendering, make sure to tonemap the resuld color prior to outputting it.
  495.    // But since this example isn't doing that, we just output the computed result color here:
  496.    return cResultColor;
  497. }  
  498.  
  499.  
  500. //--------------------------------------------------------------------------------------
  501. // Renders scene to render target
  502. //--------------------------------------------------------------------------------------
  503. technique RenderSceneWithPOM
  504. {
  505.     pass P0
  506.     {          
  507.         VertexShader = compile vs_3_0 RenderSceneVS();
  508.         PixelShader  = compile ps_3_0 RenderScenePS();
  509.     }
  510. }
  511.  
  512. technique RenderSceneWithBumpMap
  513. {
  514.     pass P0
  515.     {          
  516.         VertexShader = compile vs_2_0 RenderSceneVS();
  517.         PixelShader  = compile ps_2_0 RenderSceneBumpMapPS();
  518.     }
  519. }
  520. technique RenderSceneWithPM
  521. {
  522.     pass P0
  523.     {          
  524.         VertexShader = compile vs_2_0 RenderSceneVS();
  525.         PixelShader  = compile ps_2_0 RenderSceneParallaxMappingPS();
  526.     }
  527. }