A game about forced loneliness, made by TACStudios
at master 128 lines 5.1 kB view raw
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 10#ifndef POM_USER_DATA_PARAMETERS 11 #define POM_USER_DATA_PARAMETERS 12#endif 13 14#ifndef POM_USER_DATA_ARGUMENTS 15 #define POM_USER_DATA_ARGUMENTS 16#endif 17 18real2 19#ifdef POM_NAME_ID 20MERGE_NAME(ParallaxOcclusionMapping,POM_NAME_ID) 21#else 22ParallaxOcclusionMapping 23#endif 24(real lod, real lodThreshold, int numSteps, real3 viewDirTS, PerPixelHeightDisplacementParam ppdParam, out real outHeight POM_USER_DATA_PARAMETERS) 25{ 26 // Convention: 1.0 is top, 0.0 is bottom - POM is always inward, no extrusion 27 real stepSize = 1.0 / (real)numSteps; 28 29 // View vector is from the point to the camera, but we want to raymarch from camera to point, so reverse the sign 30 // The length of viewDirTS vector determines the furthest amount of displacement: 31 // real parallaxLimit = -length(viewDirTS.xy) / viewDirTS.z; 32 // real2 parallaxDir = normalize(Out.viewDirTS.xy); 33 // real2 parallaxMaxOffsetTS = parallaxDir * parallaxLimit; 34 // Above code simplify to 35 real2 parallaxMaxOffsetTS = (viewDirTS.xy / -viewDirTS.z); 36 real2 texOffsetPerStep = stepSize * parallaxMaxOffsetTS; 37 38 // Do a first step before the loop to init all value correctly 39 real2 texOffsetCurrent = real2(0.0, 0.0); 40 real prevHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS); 41 texOffsetCurrent += texOffsetPerStep; 42 real currHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS); 43 real rayHeight = 1.0 - stepSize; // Start at top less one sample 44 45 // Linear search 46 for (int stepIndex = 0; stepIndex < numSteps; ++stepIndex) 47 { 48 // Have we found a height below our ray height ? then we have an intersection 49 if (currHeight > rayHeight) 50 break; // end the loop 51 52 prevHeight = currHeight; 53 rayHeight -= stepSize; 54 texOffsetCurrent += texOffsetPerStep; 55 56 // Sample height map which in this case is stored in the alpha channel of the normal map: 57 currHeight = ComputePerPixelHeightDisplacement(texOffsetCurrent, lod, ppdParam POM_USER_DATA_ARGUMENTS); 58 } 59 60 // Found below and above points, now perform line interesection (ray) with piecewise linear heightfield approximation 61 62 // Refine the search with secant method 63#define POM_SECANT_METHOD 1 64#if POM_SECANT_METHOD 65 66 real pt0 = rayHeight + stepSize; 67 real pt1 = rayHeight; 68 real delta0 = pt0 - prevHeight; 69 real delta1 = pt1 - currHeight; 70 71 real delta; 72 real2 offset; 73 74 // Secant method to affine the search 75 // Ref: Faster Relief Mapping Using the Secant Method - Eric Risser 76 for (int i = 0; i < 3; ++i) 77 { 78 // intersectionHeight is the height [0..1] for the intersection between view ray and heightfield line 79 real intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0); 80 // Retrieve offset require to find this intersectionHeight 81 offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps; 82 83 currHeight = ComputePerPixelHeightDisplacement(offset, lod, ppdParam POM_USER_DATA_ARGUMENTS); 84 85 delta = intersectionHeight - currHeight; 86 87 if ((delta >= -0.01) && (delta <= 0.01)) 88 break; 89 90 // intersectionHeight < currHeight => new lower bounds 91 if (delta < 0.0) 92 { 93 delta1 = delta; 94 pt1 = intersectionHeight; 95 } 96 else 97 { 98 delta0 = delta; 99 pt0 = intersectionHeight; 100 } 101 } 102 103#else // regular POM intersection 104 105 //real pt0 = rayHeight + stepSize; 106 //real pt1 = rayHeight; 107 //real delta0 = pt0 - prevHeight; 108 //real delta1 = pt1 - currHeight; 109 //real intersectionHeight = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0); 110 //real2 offset = (1 - intersectionHeight) * texOffsetPerStep * numSteps; 111 112 // A bit more optimize 113 real delta0 = currHeight - rayHeight; 114 real delta1 = (rayHeight + stepSize) - prevHeight; 115 real ratio = delta0 / (delta0 + delta1); 116 real2 offset = texOffsetCurrent - ratio * texOffsetPerStep; 117 118 currHeight = ComputePerPixelHeightDisplacement(offset, lod, ppdParam POM_USER_DATA_ARGUMENTS); 119 120#endif 121 122 outHeight = currHeight; 123 124 // Fade the effect with lod (allow to avoid pop when switching to a discrete LOD mesh) 125 offset *= (1.0 - saturate(lod - lodThreshold)); 126 127 return offset; 128}