Lib Pom - Shader API
lib-pom.glsl
Public Functions: getParallaxOffset applyParallaxOffset
Import from library
import lib-sampler.glsl
Parallax occlusion mapping related uniforms
//: param auto is_2d_view
uniform bool isTextureView;
//: param auto channel_displacement
uniform SamplerSparse displacement_tex;
//: param custom { "label": "Enable", "default": false, "group": "Parallax Occlusion Mapping" }
uniform bool usePOM;
//: param custom { "label": "Strength", "default": 1.0, "min": 0.01, "max": 10.0, "group": "Parallax Occlusion Mapping" }
uniform float pomStrength;
//: param custom { "label": "Minimum samples", "default": 4, "min": 1, "max": 64, "group": "Parallax Occlusion Mapping" }
uniform int minPOMSamples;
//: param custom { "label": "Maximum samples", "default": 16, "min": 1, "max": 64, "group": "Parallax Occlusion Mapping" }
uniform int maxPOMSamples;
Compute the offset of texture coordinates based on parallax
vec2 getParallaxOffset(SparseCoord coord, vec3 viewTS)
{
if (!usePOM || isTextureView || !displacement_tex.is_set) return vec2(0.0);
vec2 dfdx,dfdy;
textureSparseQueryGrad(dfdx, dfdy, displacement_tex, coord);
// Convention: 1.0 is top, -1.0 is bottom - POM is always inward, no extrusion
int nbSteps = int(mix(maxPOMSamples, minPOMSamples, viewTS.z));
float amplitude = 4.0 * pomStrength / (HEIGHT_FACTOR * abs(viewTS.z) * nbSteps);
vec3 rayStep = vec3(-amplitude * viewTS.xy, -2.0 / nbSteps);
// Raymarch until we cross the surface
vec3 rayPos = vec3(coord.tex_coord, 1.0);
float prevHeight;
float currHeight = getDisplacement(textureGrad(displacement_tex.tex, rayPos.xy, dfdx, dfdy));
int i = 0;
do {
rayPos += rayStep;
prevHeight = currHeight;
currHeight = getDisplacement(textureGrad(displacement_tex.tex, rayPos.xy, dfdx, dfdy));
i++;
} while (i < nbSteps && currHeight < rayPos.z);
// Binary search with linear interpolation to refine intersection
vec3 prevRayPos = rayPos - rayStep;
vec3 newRayPos = prevRayPos;
float newHeight = prevHeight;
i = 0;
while (i < 3 && abs(newHeight - newRayPos.z) > 1e-3) {
float prevDelta = prevRayPos.z - prevHeight;
float delta = currHeight - rayPos.z;
newRayPos = (prevDelta * rayPos + delta * prevRayPos) / (prevDelta + delta);
newHeight = getDisplacement(textureGrad(displacement_tex.tex, newRayPos.xy, dfdx, dfdy));
if (newHeight > newRayPos.z) {
currHeight = newHeight;
rayPos = newRayPos;
} else {
prevHeight = newHeight;
prevRayPos = newRayPos;
}
i++;
}
return newRayPos.xy - coord.tex_coord;
}
Update input texture coordinates with parallax offset
void applyParallaxOffset(inout V2F inputs, vec3 viewTS)
{
vec2 offset = getParallaxOffset(inputs.sparse_coord, viewTS);
if (any(notEqual(offset,vec2(0.0)))) {
inputs.tex_coord += offset;
inputs.sparse_coord = getSparseCoord(inputs.tex_coord);
}
}
recommendation-more-help
4517c71e-0531-47f5-b14d-d3b9de4d0104