Skip to content

Export the POM march as a composable parallaxOcclusionUv function#9

Open
promontis wants to merge 1 commit into
mainfrom
feat/composable-parallax-uv
Open

Export the POM march as a composable parallaxOcclusionUv function#9
promontis wants to merge 1 commit into
mainfrom
feat/composable-parallax-uv

Conversation

@promontis

Copy link
Copy Markdown
Owner

What

Extracts the parallax-occlusion march out of createPomMaterial into a new exported TSL primitive:

parallaxOcclusionUv(sampleHeight, options?) // -> Fn((startUv, viewDirTs, depthScale) => vec2)

createPomMaterial is refactored on top of it — its behaviour and public API are unchanged.

Why

I'm using this POM technique in a WebGPU terrain renderer, where the material-level API can't be used directly because the terrain needs:

  1. A layered height field — the depth driver is a select/blend between forest and moss displacement maps, not one texture. Solved with a sampleHeight(uv, ddx, ddy) callback; the start-UV gradients are passed in so the callback can mip-pin with .sample(uv).grad(ddx, ddy).
  2. A world-XZ tangent frame — ground is mostly horizontal, so viewDirTs is caller-supplied instead of hard-wired to parallaxDirection.
  3. A distance fadedepthScale accepts any node, so it can be driven to zero with distance and the march exits on its first step.
  4. TuningminLayers/maxLayers (equal = fixed count, the adaptive mix is skipped), refinementSteps, minViewZ, and edgeBehavior: 'none' for repeating surfaces where the [0,1] discard is wrong.

Notes

  • New subpath export @promontis/threejs-pom/parallaxOcclusionUv + entry in the Vite lib config.
  • README section with a layered-terrain example, CHANGELOG entry under Unreleased.
  • npm run build (lib + demo) passes. No behaviour change for existing users — the default options reproduce the old constants exactly, including the discard.

WebGPU/TSL context: tested against the same march running in a MeshStandardNodeMaterial colorNode graph on WebGPURenderer (Chrome, macOS).

🤖 Generated with Claude Code

The march was locked inside createPomMaterial: single height texture,
parallaxDirection tangent frame, fixed 24-40 layers, [0,1] discard. Using
it on a layered terrain (blended height maps, world-XZ ground frame,
distance-faded depth) meant copying the loop out by hand.

parallaxOcclusionUv(sampleHeight, options?) exposes the same linear
search + binary refinement with:

- a height-sampler callback (uv, ddx, ddy) so the field can be a blend,
  a select between layers, or fully procedural, with start-UV gradients
  available for mip pinning
- caller-supplied startUv / viewDirTs / depthScale nodes, so any tangent
  frame works and depth can be faded with distance
- minLayers/maxLayers/refinementSteps/minViewZ tuning, with a fixed
  layer count when min == max
- edgeBehavior: 'discard' (single-tile, unchanged default) or 'none'
  for repeating surfaces

createPomMaterial now calls it internally; behaviour is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant