Ei kuvausta
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PerPixelDisplacement.hlsl 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // This is implementation of parallax occlusion mapping (POM)
  2. // This function require that the caller define a callback for the height sampling name ComputePerPixelHeightDisplacement
  3. // A PerPixelHeightDisplacementParam is used to provide all data necessary to calculate the heights to ComputePerPixelHeightDisplacement it doesn't need to be
  4. // visible by the POM algorithm.
  5. // This function is compatible with tiled uv.
  6. // it return the offset to apply to the UVSet provide in PerPixelHeightDisplacementParam
  7. // viewDirTS is view vector in texture space matching the UVSet
  8. // ref: https://www.gamedev.net/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262
  9. #ifndef POM_USER_DATA_PARAMETERS
  10. #define POM_USER_DATA_PARAMETERS
  11. #endif
  12. #ifndef POM_USER_DATA_ARGUMENTS
  13. #define POM_USER_DATA_ARGUMENTS
  14. #endif
  15. real2
  16. #ifdef POM_NAME_ID
  17. MERGE_NAME(ParallaxOcclusionMapping,POM_NAME_ID)
  18. #else
  19. ParallaxOcclusionMapping
  20. #endif
  21. (real lod, real lodThreshold, int numSteps, real3 viewDirTS, PerPixelHeightDisplacementParam ppdParam, out real outHeight POM_USER_DATA_PARAMETERS)
  22. {
  23. // Convention: 1.0 is top, 0.0 is bottom - POM is always inward, no extrusion
  24. real stepSize = 1.0 / (real)numSteps;
  25. // View vector is from the point to the camera, but we want to raymarch from camera to point, so reverse the sign
  26. // The length of viewDirTS vector determines the furthest amount of displacement:
  27. // real parallaxLimit = -length(viewDirTS.xy) / viewDirTS.z;
  28. // real2 parallaxDir = normalize(Out.viewDirTS.xy);
  29. // real2 parallaxMaxOffsetTS = parallaxDir * parallaxLimit;
  30. // Above code simplify to
  31. real2 parallaxMaxOffsetTS = (viewDirTS.xy / -viewDirTS.z);
  32. real2 texOffsetPerStep = stepSize * parallaxMaxOffsetTS;
  33. // Do a first step before the loop to init all value correctly
  34. real2 texOffsetCurrent = real2(0.0, 0.0);
  35. real prevHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS);
  36. texOffsetCurrent += texOffsetPerStep;
  37. real currHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS);
  38. real rayHeight = 1.0 - stepSize; // Start at top less one sample
  39. // Linear search
  40. for (int stepIndex = 0; stepIndex < numSteps; ++stepIndex)
  41. {
  42. // Have we found a height below our ray height ? then we have an intersection
  43. if (currHeight > rayHeight)
  44. break; // end the loop
  45. prevHeight = currHeight;
  46. rayHeight -= stepSize;
  47. texOffsetCurrent += texOffsetPerStep;
  48. // Sample height map which in this case is stored in the alpha channel of the normal map:
  49. currHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS);
  50. }
  51. // Found below and above points, now perform line interesection (ray) with piecewise linear heightfield approximation
  52. // Refine the search with secant method
  53. #define POM_SECANT_METHOD 1
  54. #if POM_SECANT_METHOD
  55. real pt0 = rayHeight + stepSize;
  56. real pt1 = rayHeight;
  57. real delta0 = pt0 - prevHeight;
  58. real delta1 = pt1 - currHeight;
  59. real delta;
  60. real2 offset;
  61. // Secant method to affine the search
  62. // Ref: Faster Relief Mapping Using the Secant Method - Eric Risser
  63. for (int i = 0; i < 3; ++i)
  64. {
  65. // intersectionHeight is the height [0..1] for the intersection between view ray and heightfield line
  66. real intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
  67. // Retrieve offset require to find this intersectionHeight
  68. offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps;
  69. currHeight = ComputePerPixelHeightDisplacement(offset, lod, ppdParam POM_USER_DATA_ARGUMENTS);
  70. delta = intersectionHeight - currHeight;
  71. if ((delta >= -0.01) && (delta <= 0.01))
  72. break;
  73. // intersectionHeight < currHeight => new lower bounds
  74. if (delta < 0.0)
  75. {
  76. delta1 = delta;
  77. pt1 = intersectionHeight;
  78. }
  79. else
  80. {
  81. delta0 = delta;
  82. pt0 = intersectionHeight;
  83. }
  84. }
  85. #else // regular POM intersection
  86. //real pt0 = rayHeight + stepSize;
  87. //real pt1 = rayHeight;
  88. //real delta0 = pt0 - prevHeight;
  89. //real delta1 = pt1 - currHeight;
  90. //real intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
  91. //real2 offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps;
  92. // A bit more optimize
  93. real delta0 = currHeight - rayHeight;
  94. real delta1 = (rayHeight + stepSize) - prevHeight;
  95. real ratio = delta0 / (delta0 + delta1);
  96. real2 offset = texOffsetCurrent - ratio * texOffsetPerStep;
  97. currHeight = ComputePerPixelHeightDisplacement(offset, lod, ppdParam POM_USER_DATA_ARGUMENTS);
  98. #endif
  99. outHeight = currHeight;
  100. // Fade the effect with lod (allow to avoid pop when switching to a discrete LOD mesh)
  101. offset *= (1.0 - saturate(lod - lodThreshold));
  102. return offset;
  103. }