Variable POINT_PICK_VERTEX_SHADERConst
POINT_PICK_VERTEX_SHADER: "\n precision highp float;\n\n \nbool isInvalidFloat(float v) {\n return isnan(v) || isinf(v);\n}\n\nfloat sanitizePositive(float v, float fallback) {\n return (isInvalidFloat(v) || v <= 0.0) ? fallback : v;\n}\n\nfloat sanitizeNonNegative(float v, float fallback) {\n return (isInvalidFloat(v) || v < 0.0) ? fallback : v;\n}\n\n// Per-element opacity sanitizer: NaN/Inf route to the 1.0 opaque\n// identity (corruption stays LOUD), finite values clamp to [0, 1]\n// (alpha is opacity, never HDR — Python pins the range at write; this\n// guards hand-crafted zarr). The clamp keeps the zero boundary\n// CONTINUOUS (a -1e-4 epsilon vanishes like +0.0 renders, instead of\n// jumping to full opacity) and keeps the value mediump-varying-safe.\nfloat sanitizeAlpha(float v) {\n return isInvalidFloat(v) ? 1.0 : clamp(v, 0.0, 1.0);\n}\n\n \nfloat perspectiveNearFade(int isOrtho, float viewZ, float nearCull) {\n if (isOrtho == 1) return 1.0;\n if (viewZ >= 0.0) return 0.0;\n return smoothstep(nearCull, nearCull * 2.0, -viewZ);\n}\n\n \nint luxarIsOrthoProjection() {\n return projectionMatrix[3][3] > 0.5 ? 1 : 0;\n}\nfloat luxarProjectionSizeScale() {\n return abs(projectionMatrix[1][1]);\n}\n\n\n // Per-vertex (4 corners shared across all instances)\n in vec2 aQuadCorner;\n\n // Draw-slot -> storage-slot mapping (identity in Phase 1; permuted\n // by the sort worker in Phase 2+). Also the pick ELEMENT id: the\n // pick buffer must report the storage slot -- the id the rest of\n // the pipeline (loaders, selection) addresses points by -- not the\n // transient draw slot.\n \n// Element index split into two 16-bit halves, low in .x and high in .y.\n// The pick pass carries the index through an RGBA32F buffer, and float32\n// has a 24-bit mantissa — so a single float channel cannot represent\n// consecutive indices past 16,777,216, while a node's capacity reaches\n// 2^25 on a 32768-texel device. Both halves are <= 65535, hence exact,\n// and the pick decoder recombines them (see picking-system/pick-render.ts).\n// Kept in INT space: doing the split on a float would already have lost\n// the bit it is meant to preserve.\nvec2 luxarElementIdSplit(uint i) {\n return vec2(float(i & 0xFFFFu), float(i >> 16u));\n}\n\nin uint aSortedIndex;\nin uint aSortedIndexB;\nuniform int uSortedIndexSlot;\n\nuint luxarSortedIndex() {\n return uSortedIndexSlot == 1 ? aSortedIndexB : aSortedIndex;\n}\n\n// The STORAGE slot's id parts — the id the rest of the pipeline (loaders,\n// selection) addresses elements by, not the transient draw slot. Mesh reads\n// the shared split directly instead, off gl_VertexID (spec §6.5).\nvec2 luxarElementIdParts() {\n return luxarElementIdSplit(luxarSortedIndex());\n}\n// Projected-density thinning (scene/density-guard.ts): the fraction of this\n// node's elements to DROP, chosen per STORAGE index with a deterministic\n// integer hash so the kept subset is stable under depth re-sorting, identical\n// across the visual and picking passes, and spatially uniform (storage order\n// is Hilbert/BSP-coherent, so a prefix would be a hole). A material that does\n// not set the uniform reads 0 and drops nothing.\nuniform float uDensityDrop;\nbool luxarDensityDropped() {\n if (uDensityDrop <= 0.0) return false;\n uint h = luxarSortedIndex();\n h ^= h >> 16u;\n h *= 0x7feb352du;\n h ^= h >> 15u;\n h *= 0x846ca68bu;\n h ^= h >> 16u;\n return float(h) * (1.0 / 4294967296.0) < uDensityDrop;\n}\n\n\n // Point data texture: RGBA32F, 3 texels/point (see\n // rendering/element-texture-layout.ts). Picking needs texels 0-1\n // only (center/radius/sharpness) -- color and scalar are not fetched.\n uniform highp sampler2D uPointTex;\n\n uniform float maxPointSize;\n uniform float radiusScale;\n uniform float uNearCull;\n uniform float uPixelRatio;\n uniform float uNodeId;\n uniform vec2 uResolution;\n\n out highp float vRadius;\n out mediump float vBeta;\n out mediump float vNearFade;\n out mediump float vPickSize; // RAW pick sprite size (pre-clamp) — sizeScale² parity with the visual shader\n out mediump vec2 vSpriteCoord;\n flat out highp float vNodeId;\n flat out highp vec2 vElementId;\n\n void main() {\n // === Point-texture fetch prologue (visual-shader parity) ===\n // Width is a multiple of 3, so a point's texels share one row.\n // Projected-density thinning: a point the visual pass dropped must not be\n // pickable either (same hash, same uniform value — see density-drop.ts).\n if (luxarDensityDropped()) {\n gl_Position = vec4(0.0, 0.0, -2.0, 1.0);\n return;\n }\n int pointBase = int(luxarSortedIndex()) * 3;\n int pointTexW = LUXAR_POINT_TEX_W;\n ivec2 texel0 = ivec2(pointBase % pointTexW, pointBase / pointTexW);\n vec4 pointT0 = texelFetch(uPointTex, texel0, 0);\n vec4 pointT1 = texelFetch(uPointTex, ivec2(texel0.x + 1, texel0.y), 0);\n vec3 aCenter = pointT0.xyz;\n float aRadius = pointT0.w;\n float aSharpness = pointT1.w;\n\n // Mirror visual point shader sanitization (shader-glsl.ts) so the\n // pick footprint can't diverge from the visible footprint: sharpness\n // in [0, 1] -> super-Gaussian exponent beta = 2^(6s - 2).\n float s = clamp(sanitizeNonNegative(aSharpness, 0.5), 0.0, 1.0);\n vBeta = exp2(6.0 * s - 2.0);\n\n float normalizedRadius = sanitizeNonNegative(aRadius * radiusScale, 0.0);\n vRadius = normalizedRadius;\n\n vec4 mvPosition = modelViewMatrix * vec4(aCenter, 1.0);\n\n // Unified near handling — keep in sync with the visual point\n // shader and the line/gsplat pick guards: pickability must track\n // what is actually visible (behind-camera fade 0; smooth\n // [nearCull, 2*nearCull] fade; ortho = 1, NDC clip authority).\n // 1e-20 floor = degenerate-smoothstep guard only; uNearCull is\n // scene-bounds-scaled (see the visual point shader).\n vNearFade = perspectiveNearFade(luxarIsOrthoProjection(), mvPosition.z, max(uNearCull, 1e-20));\n if (vNearFade < 0.01) {\n gl_Position = vec4(0.0, 0.0, -2.0, 1.0); // off-screen → no fragments\n return;\n }\n\n vec4 projCenter = projectionMatrix * mvPosition;\n\n // View-space depth, matching the visual point shader (B9a) so the\n // pick footprint stays congruent with the visible sprite. 1e-20 =\n // pure INF guard (near-fade reject bounds surviving depths at the\n // scene-relative ~uNearCull; the size clamp bounds the output).\n float invDistance = (luxarIsOrthoProjection() == 1) ? 1.0 : 1.0 / max(-mvPosition.z, 1e-20);\n float sizeFactor = 2.0 * uResolution.y * luxarProjectionSizeScale();\n float basePointSize = normalizedRadius * sizeFactor * invDistance;\n\n // Picking footprint: 80% of the visual radius (the 0.8 factor below).\n // Slightly tighter than the visible disc so dense/overlapping point\n // clouds still resolve to the point whose core you're over, but\n // forgiving enough that hovering a sparse point doesn't require\n // pixel-perfect aim. Keep in sync with pick.tsl.ts.\n // No sharpness size compensation: the shifted-truncated super-Gaussian\n // truncates at the sprite edge, so basePointSize IS the visible extent\n // (matches shader-glsl.ts).\n float pointSize = basePointSize * 0.8;\n vPickSize = pointSize; // raw, pre-clamp — fragment applies sizeScale²\n pointSize = clamp(pointSize, 1.5 * max(uPixelRatio, 1.0), maxPointSize); // CSS-pixel floor tracks the VISUAL sprite floor; below 1× keep the historical framebuffer floor\n\n // Instanced quad expansion (matches shader-glsl.ts approach, including\n // the behind-camera guard above).\n vec2 offsetClip = aQuadCorner * (pointSize / uResolution) * projCenter.w;\n gl_Position = projCenter + vec4(offsetClip, 0.0, 0.0);\n\n vSpriteCoord = (aQuadCorner + 1.0) * 0.5;\n\n vNodeId = uNodeId;\n // Storage slot, NOT gl_InstanceID (the draw slot): identical\n // under Phase-1 identity ordering, and stays correct once the\n // sort worker permutes draw order (Phase 2+).\n vElementId = luxarElementIdParts();\n }\n" = ...
Picking vertex shader for points. Adds uNodeId uniform and vNodeId/vElementId outputs.