Documentation
Shader Example: Parallex Mapping
Parallex mapping with procedural heightmap.
Shader Example: Parallex Mapping
Snippets
Shadertoy
// Shadertoy: parallax mapping demo without textures
// Paste into https://www.shadertoy.com/ as the Image shader.
float heightMap(vec2 uv)
{
// Procedural height field
float h = 0.0;
h += 0.50 * sin(uv.x * 10.0) * sin(uv.y * 10.0);
h += 0.25 * sin(uv.x * 20.0 + 1.0) * sin(uv.y * 20.0 + 0.7);
return h * 0.5 + 0.5;
}
vec3 normalFromHeight(vec2 uv)
{
float e = 0.002;
float h = heightMap(uv);
float hx = heightMap(uv + vec2(e, 0.0));
float hy = heightMap(uv + vec2(0.0, e));
vec3 dpdx = vec3(e, 0.0, hx - h);
vec3 dpdy = vec3(0.0, e, hy - h);
return normalize(cross(dpdx, dpdy));
}
vec2 parallaxOffset(vec2 uv, vec3 viewDirTS)
{
float heightScale = 0.08;
// Basic parallax mapping
float h = heightMap(uv);
return uv - viewDirTS.xy / max(viewDirTS.z, 0.05) * ((h - 0.5) * heightScale);
}
vec3 albedo(vec2 uv)
{
// Checker + gradient for visibility
vec2 g = floor(uv * 8.0);
float checker = mod(g.x + g.y, 2.0);
vec3 a = mix(vec3(0.15, 0.18, 0.22), vec3(0.75, 0.80, 0.90), checker);
a *= 0.7 + 0.3 * heightMap(uv);
return a;
}
mat3 camera(vec3 ro, vec3 ta)
{
vec3 f = normalize(ta - ro);
vec3 r = normalize(cross(vec3(0.0, 1.0, 0.0), f));
vec3 u = cross(f, r);
return mat3(r, u, f);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
// Camera
float t = iTime * 0.8;
vec3 ro = vec3(1.8 * cos(t), 0.9, 1.8 * sin(t));
vec3 ta = vec3(0.0, 0.0, 0.0);
mat3 cam = camera(ro, ta);
vec3 rd = normalize(cam * vec3(p, 1.8));
// Intersect plane z=0 in tangent/object space
// Plane basis: x,y = tangent plane, z = height direction
float planeZ = 0.0;
float tt = (planeZ - ro.z) / rd.z;
vec3 col = vec3(0.02, 0.03, 0.05);
if (tt > 0.0)
{
vec3 hit = ro + rd * tt;
vec2 uv = hit.xy;
// Tile UVs
uv *= 1.2;
// View dir in tangent space for a flat XY plane
vec3 viewDirTS = normalize(-rd);
// Apply parallax
vec2 puv = parallaxOffset(uv, viewDirTS);
// Normal + lighting from displaced UV
vec3 n = normalFromHeight(puv);
vec3 l = normalize(vec3(0.6, 0.7, 0.5));
vec3 v = normalize(-rd);
vec3 h = normalize(l + v);
float diff = max(dot(n, l), 0.0);
float spec = pow(max(dot(n, h), 0.0), 48.0);
vec3 base = albedo(puv);
col = base * (0.15 + 0.85 * diff) + 0.35 * spec;
// Slight vignette / fade
col *= exp(-0.08 * tt);
}
fragColor = vec4(pow(col, vec3(0.4545)), 1.0);
}