Primary implementation surface
These routes are generated from canonical source. Their exact status remains separate from implementation availability.
Author workload-selected WebGPU/TSL procedural materials in Three.js. Use for coupled terrain/coast/seabed response bundles, grass, rock, dry/wet sand and reef identities, NodeMaterial PBR fields, atlas and triplanar filtering, footprint filtering, specular AA, terrain wetness, stylized palette/facet policies, emissive or raymarched fields, per-instance dissolve, derivative normals, and explicit physical-response bundles.
These routes are generated from canonical source. Their exact status remains separate from implementation availability.
This skill contributes to the following cross-skill owner graphs.
Materials are authored as PBR identity fields: albedo, roughness, and normal all derive from shared procedural causes. Surface normals come from height derivatives — screen-space derivatives give filtering for free:
$$\mathbf n = \operatorname{normalize}\!\big(\mathbf n_g - \partial_x h\,\mathbf t - \partial_y h\,\mathbf b\big)$$Specular antialiasing widens roughness where the normal field varies inside a pixel (Kaplanyan-style variance from derivatives), preventing distant sparkle:
$$\alpha' = \sqrt{\alpha^2 + \operatorname{clamp}\!\big(\|\partial_x \mathbf n\|^2 + \|\partial_y \mathbf n\|^2\big)}$$Triplanar projection blends three axis-aligned samples with a sharpened weight $w_i = |n_i|^k / \sum |n_j|^k$, and emissive surfaces (lava) map temperature through a blackbody-inspired ramp so brightness lives in scene-relative HDR units, not display units.
Every image identifies what it proves. Page screenshots demonstrate the published presentation only; generated inputs demonstrate asset channels only; canonical acceptance still requires render-target readback and a schema-v2 bundle.
4 published images
generated asset preview
generated asset preview
generated asset preview
generated asset preview
The complete SKILL.md as loaded by agents — verbatim, rendered.
Build procedural materials as WebGPURenderer + TSL + NodeMaterial graphs.
The canonical physically lit lane is a MeshStandardNodeMaterial or
MeshPhysicalNodeMaterial whose node slots preserve Three.js lighting,
environment, shadow, transmission, clearcoat, sheen, anisotropy, and output
upgrades while replacing only the material causes.
Every numerical claim emitted from this skill carries one label:
Unlabelled vector widths, channel counts, and API-version digits are structural, not performance or physics claims.
Use $threejs-choose-skills preflight for scenes that also need atmosphere,
clouds, oceans, shadows, post, or validation ownership.
When a rendered surface also participates in contact, flow, heat, wetting, or
another physical interaction, use the route's
physics-domain and interaction contract.
Bind its semantic surface explicitly to a PhysicsMaterialId in the shared
PhysicsMaterialRegistry. Never infer friction, restitution, density,
compliance, permeability, adhesion, or thermal/hydraulic response from base
color, roughness, metalness, clearcoat, opacity, a texture name, or visual
wetness.
Every dynamic physics/receiver/lighting cause is sampled through its unchanged
PhysicsSignalDescriptor at an exact PhysicsInstant or
PhysicsTimeInterval, including context, frame/origin/transform revision,
requested/actual footprint and filter, validity/error, state/resource
generation, residency, and cadence/latency. A rendered dynamic cause resolves
through a view-independent candidate pair: its previous/current arms own
independent provenance and state handles, CameraViewPublication owns render
mapping, ViewPreparationPublication owns per-view caches/shadows/resets, and
the sealed snapshot references candidate binding IDs and leases. Convert world
coordinates only through the active PhysicsContext boundary and the camera
publication's derived render transform; material uniforms do not define another
scale, clock, provider, transform, or state owner.
Algorithm class dominates material throughput. Start from one shared TSL cause graph, not from independent texture/noise calls per PBR channel:
stable coordinates
-> structural fields
-> material identity weights
-> causal modifiers
-> filtered microstructure
-> derivative normals and specular AA
-> NodeMaterial PBR slots
-> node post/output
Texture-space/decoupled shading is a separate architecture, not the default specular-stability path. It requires an explicit radiance-cache owner, update and visibility model, and measured bandwidth/error contract; do not silently replace derivative normals or specular AA with cached radiance.
The target graph writes:
colorNode from linear authored identity colors or SRGBColorSpace color
textures sampled through TSL.roughnessNode, metalnessNode, aoNode, opacityNode, and physical slots
from the same identity weights.normalNode from normalMap(), bumpMap(texture(...)) for texture height
maps, or a surface-gradient normal built from the same scalar procedural
height. Fragment derivatives must execute in derivative-uniform control flow;
vertex/compute displacement uses analytic or stored gradients.emissiveNode only for actual material emission; route glow through
$threejs-bloom and BloomNode in the node render pipeline.positionNode and castShadowPositionNode consume the same local-space
displacement node when material displacement must match visible and caster
geometry. receivedShadowPositionNode is a separate world-space receiver
override in r185; leave it null unless a world-space replacement is
derived and validated explicitly.maskNode, alphaTestNode, castShadowNode, or maskShadowNode for
dissolve and cutout behavior, driven by the same instance fields.Read references/procedural-pbr-system.md for the WebGPU/TSL material system, quality tiers, budgets, atlas/triplanar costs, derivative normals, specular AA, planet fields, wetness, emissive ownership, per-instance dissolve, and validation.
Canonical walnut, antique-gold, ebony, and lava TSL example: examples/tsl-procedural-pbr/.
The example now enforces dielectric/conductor metalness endpoints, meter-valued
height through the route's metersPerWorldUnit, footprint-filtered structural
bands, and
removed material slope-energy transfer so r185 geometry roughness is not
counted twice. Its spectral-support/variance multipliers and identity ranges
remain Authored trial values. The Node construction validator is structural
evidence, not an energy, visual-reference, timing, or thermal acceptance proof.
Use the sibling examples as domain sources, not implementation recipes:
$threejs-procedural-fields for designing shared scalar/vector causes.$threejs-procedural-planets for planet-space coordinates, altitude
filtering, and orbit-to-close material/geometry parity.$threejs-water-optics for coupled reflection, refraction, absorption,
crest response, and water-surface diagnostics.$threejs-bloom and $threejs-image-pipeline for HDR emissive extraction,
tone mapping, output conversion, and node post ownership.$threejs-scalable-real-time-shadows when material displacement, alpha, or projected
environmental occlusion must remain shadow-consistent.For generated islands, coasts, riverbanks, reefs, or exposed terrain, consume
one versioned field contract from $threejs-procedural-fields. Do not recreate
coast, slope, moisture, or substrate noise inside each material. At minimum,
the contract declares units, sign conventions, coordinate frame, generation
revision, filtering policy, and update cadence for:
signed coast distance and nearest-coast tangent/normal
terrain/seabed elevation, water-rest elevation, and water-column depth
geometric slope, curvature/cavity, drainage/moisture, and exposure
substrate/material identity and terrace/cliff/beach semantic masks
salt/spray exposure, run-up or inundation envelope, and persistent wetness
reef/rock/sand/organic-cover eligibility and authored exclusion masks
water-column depth is Derived from compatible elevations; it is not a
second painted shallow-water mask. Water owns dynamic free-surface, foam, and
optical transport. The route-selected receiver owner alone integrates liquid/
snow storage, precipitation, inundation/wash, infiltration, drainage,
evaporation, and melt. This skill owns only the visual dry/wet terrain and
submerged-substrate response projected from those immutable signals. A white
shoreline stripe painted into
terrain albedo is not foam, and cyan seabed emission is not shallow-water
transport.
Build normalized identity weights for grass/organic cover, dry rock or cliff, dry sand, wet sand or waterline substrate, submerged sand, reef/rock, and any project-specific identity. Preserve hard semantic exclusions separately, then filter only the visible transition width from the projected footprint. Every identity is a response bundle:
linear base reflectance + roughness-alpha + metalness endpoint
resolved height/normal spectrum + removed-slope variance
porosity/absorption or wet-film approximation
macro color variation + microstructure scale
material-slot/semantic ID + diagnostic color
Blend alpha = roughness^2, not unrelated roughness scalars. Filter the same
weights into color, roughness, height/normal, AO/cavity, and wetness; otherwise
the grass edge, cliff normal, and sand response detach under motion. Terrain
geometry owns silhouettes and intentionally faceted normals. Material normals
add only footprint-valid detail and must not smooth away authored cliff facets.
Stylization is an authored identity transform, not license to violate material causality. Quantize a controlled palette, macro-value families, roughness families, and geometric facet normals before lighting while retaining scene-linear PBR and one output transform. Do not bake light-facing highlights, ambient occlusion, foam, or turquoise water into terrain base color. Under shallow water, seabed color and roughness remain substrate properties; the water owner supplies depth-dependent attenuation, refraction, surface Fresnel, caustics, and foam.
The detailed response, filtering, and asset-channel contract is in references/procedural-pbr-system.md.
Keep MaterialResponseBundleId and PhysicsMaterialId distinct. Bind both to
the semantic surface/asset slot that needs them; neither ID is derived from the
other, and the mapping need not be one-to-one. A physics record declares its
constitutive model, SI units, validity range/state, uncertainty, provenance,
version, and solver combine rule. Depending on the selected solver it may
contain density; static/dynamic and anisotropic friction; restitution with an
impact-speed range; normal/tangential compliance and damping; permeability,
porosity, absorption, capillary/contact-angle or hydraulic data; adhesion as a
named surface-energy or traction model; and thermal conductivity, specific
heat, emissivity spectrum, or phase-change data. Omit unsupported
properties explicitly; do not fabricate plausible constants.
Collider and support proxies carry PhysicsMaterialId independently of render
material and LOD. Wet, icy, damaged, compacted, or thermally changed states
switch to a versioned physics material/state only through the owning physical
model; a darker or shinier shader state alone does not change contact response.
Material-pair lookup uses a deterministic canonical ID/registry/law-state key
unless
the constitutive law is explicitly directed. Latch both material IDs and state
versions atomically for a solve; select exactly one admissible pair law, reject
incompatible restitution-plus-damping energy models, and report dissipated work.
Read the detailed registry and validation contract in
references/procedural-pbr-system.md.
Initialize the renderer before selecting quality. This skill has one production path: native WebGPU.
await renderer.init();
if (renderer.backend.isWebGPUBackend !== true) {
throw new Error("threejs-procedural-materials requires native WebGPU.");
}
Call renderer.compute()/computeAsync() only for a cause-map or instance
update selected by the procedural-fields amortized cost gate.
After initialization, computeAsync() provides no GPU-completion fence in
r185; use compute() for submission and an actual readback/map or timestamps
for completion evidence.
Quality tiers:
| Tier | Material architecture | Targets |
|---|---|---|
| Full | TSL fields, cost-gated StorageTexture cause maps, storage-backed instance state, filtered normals/specular AA, projection only where UVs cannot preserve scale |
Measured close-inspection target inside budget |
| Budgeted | same graph with packed sampled data, single-/two-axis projection where valid, fewer filtered bands, lower update cadence | Measured full tier misses the named target's traffic, thermal, or frame gate |
| Minimum native | precomputed generated variants, lower field resolution, filtered UV/array sampling, static instance attributes | Gated WebGPU exists; Budgeted still misses target |
These rows change presentation work only. A physics-facing cause/provider may
change representation, filter, cadence, state, or error only through a
coordinator-admitted QualityTransition; material quality selection has no
authority to mutate physics or receiver state.
Dynamic cause-map producers are explicit PhysicsGraph stages when they
advance physical/receiver state, with declared versions and GPU dependencies.
A view-independent render projection publishes each stable binding as a leased
PresentedStatePair in PhysicsPresentationCandidate; each pair arm carries
its own PresentationSampleProvenance, presentedInstant, state handle, and
spatial binding. A projection selected or built for one view belongs instead to
ViewPreparationPublication. Materials resolve the sealed snapshot's candidate
binding/lease refs plus its CameraViewPublication render mapping; snapshots do
not copy pairs or transforms. A new generation never overwrites a leased
previous/current resource in place, and multi-target completion/retirement is
recorded by lease ID in FrameExecutionRecord.
There is no universal desktop/mobile millisecond, map-extent, band-count, or MiB row. The application declares whole-frame Gated GPU p95, CPU p95, peak-live-byte, quality-error, and sustained-thermal limits for named workloads. Select a tier only from Measured evidence for that workload.
Record output extent/DPR, covered fragments, overdraw/MSAA/helper estimate, active procedural operations, tile extent and dirty/update cadence, per-stage bindings, executed texture operations, producer/consumer bytes, mip/layer/ ping-pong lifetimes, and cache/effective-bandwidth evidence. Report base, candidate, and interleaved paired-delta GPU p50/p95 plus contemporaneous whole-frame GPU/CPU p50/p95 and peak live bytes. Mobile-class evidence includes warmup, sustained interval, power state, and throttling result.
Interleave matched frames and compute
delta_k=tGPU_k(graph+material)-tGPU_k(graph) before taking p50/p95; never
subtract independent quantiles. Marginal evidence diagnoses the material;
whole-frame gates accept it. CPU frame intervals are not GPU timings.
The material integration is Gated to avoid an undeclared extra full-scene render. Reuse the scene owner's MRT and account for every attachment byte, or declare and budget the additional scene pass at application level. Bloom, AO, grading, and other post passes retain sibling ownership and separate ledgers.
Per-material accounting:
triplanarTexture() issues Derived three filtered samples
per texture and uses simple absolute-normal weights. Two separately bound
color/data textures therefore add six samples; a separate normal texture adds
three more before any manual taps. Reserve this path for UV-less surfaces
after measuring it. Arrays/atlases reduce bindings, not sample operations;
custom hex tiling has its own measured sample count and does not solve seams.Count bindings and executed sample operations separately. WebGPU device
defaults are Gated at 16 sampled textures, 16 samplers, and 4 storage
textures per shader stage; query renderer.backend.device.limits because the
actual device may expose more and Three.js lighting, environment, shadows, and
other bindings in the same material pipeline spend from those stage limits.
Post passes have separate pipeline layouts and need separate ledgers; they do
not reduce the material pipeline's binding limit. Compatibility-mode devices
can expose zero vertex-stage storage resources, so query the stage-specific
limits rather than the aggregate compute limit. The material allowance is:
B_material = B_device - B_renderer - B_scene_shared
S_material = sum(active-path projectionCount * textureLookups * manualTaps)
B_* comes from the compiled pipeline layout; S_material comes from generated
WGSL/graph inspection and includes the worst hot branch. As an Authored
trial, teams may record a candidate sample ceiling in the workload manifest,
but it has no portable acceptance status. Any sample count requires Measured
A/B, whole-frame, traffic, and thermal evidence on the target.
Pack lanes only when color space, coordinates, derivatives, precision, filter,
and update cadence agree.
F0 = ((n2 - n1) / (n2 + n1))^2; against air (n1 = 1), ior = n2 = 1.5
gives F0 = 0.04, and water at ior ~= 1.333 gives F0 ~= 0.0204. Drive
MeshPhysicalNodeMaterial IOR/specular slots and let Three.js retain its GGX
and multiscattering energy path. Do not multiply a second Fresnel lobe into
colorNode.0; exposed homogeneous metal uses 1. Oxide, dirt, coating, and substrate
are separate masks/response bundles. Fractional metalness is only a declared
subpixel-mixture approximation; broad fractional values make metals waxy.alpha = roughness^2 then take sqrt; a weighted
mixture of distinct lobes is not exactly another GGX lobe, so keep visible
material regions discrete or use explicit layers.m, Derived unresolved
vector variance is max(1 - dot(m,m), 0); combine it with within-pixel
derivative variance.getRoughness()
from normalViewGeometry. Custom specular AA must add only unresolved
material-detail variance, or it double counts geometric roughness.v_box ~= (|dNdx|^2 + |dNdy|^2)/12. Map that statistic into GGX
alpha = roughness^2 with an Authored calibration, then replace it with
a Measured fit to a supersampled reference; there is no universal
multiplier. Clamp and expose pre/post roughness and variance views.v_j=(2*pi*A_j*f_j)^2/2 * (1-w_j^2); sum independent bands.
Noise support and variance coefficients start Authored and become
Measured only after spectrum/reference fitting. Do not add both this term
and a mip/box statistic for the same unresolved energy.F0 = 0.04; it is not an
exact water-film lobe (F0 ~= 0.0204). If wetness uses clearcoat, label that
an Authored approximation and validate the mismatch under grazing light.SRGBColorSpace; generated color fields stay linear until
the output owner.NoColorSpace unless the channel is explicitly
color.HalfFloatType until tone mapping.RenderPipeline.outputColorTransform or one explicit renderOutput() node.RenderPipeline, pass(), mrt(), PassNode.setResolutionScale(),
outputColorTransform, and renderOutput() are the current node post path.
PhysicsMaterialId from PBR channels,
filenames, shader state, or the current render LOD;Use $threejs-procedural-fields when the hard part is the shared scalar/vector
cause design. Use $threejs-procedural-planets for complete planet bodies and
geometry/material parity. Use $threejs-water-optics for physically coupled
water. Use $threejs-bloom, $threejs-image-pipeline, and
$threejs-exposure-color-grading when the material requires HDR extraction,
tone mapping, or final image ownership. Use $threejs-scalable-real-time-shadows for
CSMShadowNode, TileShadowNode, or material-aware shadow decisions.
Preserved concept proxies and generated-asset previews. They are excluded from primary completion counts and link to the canonical lab through the schema-v2 registry.