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

    Module rendering/picking/mesh/shaders

    GLSL3 picking shader source for meshes + the ShaderSource record.

    The simplest of the four pick shaders, and the only one whose element id is a built-in rather than an attribute. Three structural differences from its siblings, all from spec §6.5:

    1. No aSortedIndex indirection. Mesh depth sorting permutes whole index triples, so there is no draw-slot → storage-slot mapping and no luxarElementIdParts() call. The element id is gl_VertexID, split by the same shared luxarElementIdSplit() the sorted-index helper uses, so both paths agree with voteWinner's recombination by construction.
    2. Vertex granularity, not element granularity. The draw is indexed (faces is the index buffer), so under drawElements gl_VertexID is the ordinal of the vertex in the vertices array — a stable per-vertex id. gl_VertexID / 3 would be meaningless (shared vertices break it, and WebGL2 has no gl_PrimitiveID), and a FACE ordinal would be renumbered on every slice change, since §5.4 rewrites only the index buffer. A vertex ordinal is invariant across slices, which is why it is the chosen granularity — and it indexes the per-vertex label CSR (§3.2) directly.
    3. No quad expansion, and only half the camera uniforms. A mesh has no screen-space footprint to size, so this pair binds no uResolution and no focal length. It does bind uNearCull and the wrapper IS a CameraAwareMaterial — matching the visual mesh material, whose near fade the pick coverage has to reproduce or a fading surface would stay fully pickable (#1431).

    Both mode-dependent behaviours are runtime uniforms, not defines, so mesh-pick stays a single codegen variant and a blending-mode switch from the layers panel never recompiles the pick program:

    Uniform Set from Effect
    uSurfaceDepth isNormalMode(mode) || isOpaqueMode(mode) real projected depth (front-most wins) vs brightness-as-depth (brightest wins)
    uAlphaCutout isOpaqueMode(mode) apply the visual shader's identical a < uAlphaCutoff discard
    LUXAR_MESH_PICK_BASE_COLOR_TEX the node has a texture sample the texture's ALPHA into coverage, so cutout holes are unpickable

    Under uSurfaceDepth == 1 this shader writes gl_FragCoord.z. Its TSL twin writes pickFragmentDepthTSL() (../_shared/pick-depth-tsl.ts), which selects per draw, from the projection matrix, viewZToPerspectiveDepth(positionView.z, near, far) — exactly the hyperbolic window-space depth gl_FragCoord.z is — or viewZToOrthographicDepth, exactly the orthographic gl_FragCoord.z. So the two backends write the SAME value per fragment, and cross-node depth comparisons — including against the commutative modes' 1 / (1 + brightness) fragments sharing this buffer — resolve identically. (three's own depth node made that choice from the camera a graph was BUILT under, and three reuses a build across cameras, so graphs built under different camera kinds disagreed after a perspective/ortho switch; every pick family now shares the helper.)

    The codegen snapshot mesh-pick.fragment.glsl.txt therefore carries both expansions behind a select on the projection matrix's [3][3] element.

    Source-of-truth for GLSL3; the WebGPU counterpart lives in ./pick.tsl and is referenced through the ShaderSource.webgpu factory below.

    MESH_PICK_VERTEX_SHADER
    MESH_PICK_FRAGMENT_SHADER
    MESH_PICK_SOURCE