Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Module rendering/materials/_shared/material-builder

    buildMaterial — central branching helper for the dual-stack renderer (THREE.WebGLRenderer + WebGPURenderer).

    A caller delegates inner construction here — today that is the post-processing passes only (FxaaPass, BloomChain), the geometry materials having their own wrappers. The helper branches on RendererCapabilities.apiSurface:

    • WebGL2 path returns a configured THREE.ShaderMaterial from source.webgl.{vertex,fragment}.
    • WebGPU path calls source.webgpu(uniforms) to get a NodeMaterial.

    Both webgl and webgpu fields on ShaderSource are optional in the type so future single-backend shaders can opt out cleanly, but the helper throws when the active backend's source is absent. Under WebGPU specifically we do NOT silently fall back to ShaderMaterial — WebGPURenderer cannot dispatch ShaderMaterial even when running on its internal WebGL2 backend (see BROWSER_SUPPORT_POLICY.md), so the silent fallback would render blank quads. Throwing surfaces the gap at construction time instead.

    The config's render state is authoritative on both backends. blending / depthTest / depthWrite / transparent / toneMapped / side are resolved ONCE by resolveRenderState and then applied to whichever material the branch produced, so a TSL factory's internally-set values are overridden by whatever the host passed — and by the same resolved defaults when the host passed nothing. The WebGPU branch used to thread config.uniforms alone and drop the rest, which silently disabled the bloom upsample pass's AdditiveBlending: each upsample overwrote its destination mip instead of accumulating into it, so WebGPU bloom rendered as a flat dim wash (#2563). Resolving in one place is what stops the two branches drifting apart for the same config.

    defines is deliberately NOT threaded on the WebGPU branch. It is an OPTIONAL field on three's Material type that Material itself never initialises (ShaderMaterial and several built-in mesh material classes do), so a bare NodeMaterial has defines === undefined — and nothing in three's node pipeline reads it: .defines appears nowhere in the three.webgpu build, GLSL fallback included, three's GLSL program builder being its only consumer. Threading it would therefore be inert as far as three is concerned. Luxar's *TSLMaterial classes do keep their own flag bag there (hence their if (!this.defines) this.defines = {} guards), but they fill it from their factory config, not from this helper.

    Two limits on how far the cross-backend agreement reaches:

    • toneMapped is threaded for symmetry but is only OBSERVED on the WebGL path — material.toneMapped appears nowhere in the three.webgpu build either. That backend tone-maps in an output pass driven by renderer.toneMapping, which PostProcessingManager pins to NoToneMapping in its constructor because the mega-shader grades instead.
    • CustomBlending is expressible but its FACTORS are not: there is no blendEquation / blendSrc / blendDst here, so such a request lands on Material's default factors on both backends. Luxar's max, opaque and gsplat normal states all carry factors (volumetric delegates to the gsplat-normal helper for exactly that state) and reach materials through applyBlendingStateToMaterial (rendering/blending-state.ts), not through here.

    A factory that derives its own COMPLETE blending state therefore must not be routed through buildMaterial unless the caller passes that whole state — the resolved defaults overwrite whatever the config omits.

    One limit that is NOT backend-specific, since it is easy to assume otherwise: premultipliedAlpha is not expressible in the config either, and SubtractiveBlending / MultiplyBlending require it on EVERY path — WebGPUPipelineUtils._getBlending, WebGLState and the WebGL fallback's WebGLState all refuse those two identically, logging an error and issuing no blend state (on the WebGL path the cached blending is still marked as applied, so the draw silently keeps the previous material's blend factors). Both presets are unusable through this helper on either backend.

    BuildMaterialConfig
    buildMaterial