playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
258 lines (257 loc) • 12.2 kB
TypeScript
/**
* @import { StorageBuffer } from '../../platform/graphics/storage-buffer.js'
* @import { GraphNode } from '../graph-node.js'
* @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js'
* @import { Layer } from '../layer.js'
* @import { GSplatWorkBuffer } from './gsplat-work-buffer.js'
* @import { GSplatWorld } from './gsplat-world.js'
* @import { GSplatWorldState } from './gsplat-world-state.js'
* @import { GSplatRenderViewParams } from './gsplat-renderer.js'
*/
/**
* Renders splats from a pre-projected cache built by the projector compute pass
* (see {@link GSplatProjector}) and a globally radix-sorted index array. The
* vertex shader is `gsplatHybridVS`; the fragment is the existing `gsplatPS`.
*
* Supports forward rendering and explicit pick/prepass paths. Shadow rendering is
* intentionally not supported because the projection cache is camera-specific.
*
* @ignore
*/
export class GSplatHybridRenderer extends GSplatRenderer {
/**
* @param {GraphicsDevice} device - The graphics device.
* @param {GraphNode} node - The graph node.
* @param {GraphNode} cameraNode - The camera node.
* @param {Layer} layer - The layer to add mesh instances to.
* @param {GSplatWorkBuffer} workBuffer - The work buffer (kept for parent compatibility;
* the hybrid renderer does not bind work-buffer textures itself).
* @param {import('./gsplat-hybrid-renderer-scratch.js').GSplatHybridRendererScratch|null} [scratch] -
* Manager-owned shared scratch; forwarded to the interval compaction (shared with the shadow cull).
*/
constructor(device: GraphicsDevice, node: GraphNode, cameraNode: GraphNode, layer: Layer, workBuffer: GSplatWorkBuffer, scratch?: import("./gsplat-hybrid-renderer-scratch.js").GSplatHybridRendererScratch | null);
/** @type {ShaderMaterial} */
_material: ShaderMaterial;
/** @type {MeshInstance} */
meshInstance: MeshInstance;
/** @type {ShaderMaterial|null} */
_pickMaterial: ShaderMaterial | null;
/** @type {MeshInstance|null} */
_pickMeshInstance: MeshInstance | null;
/**
* Per-camera `clipToViewZ` value for the forward material. Persistent: the GPU
* upload happens at draw time, so the buffer must outlive `setHybridSortedRendering`.
*
* @type {Float32Array}
*/
_clipToViewZ: Float32Array;
/**
* Per-camera `clipToViewZ` value for the pick material. Allocated on first
* `prepareForPicking` call and reused thereafter.
*
* @type {Float32Array|null}
*/
_clipToViewZPick: Float32Array | null;
/** @type {number} */
originalBlendType: number;
/** @type {Set<string>} */
_internalDefines: Set<string>;
/** @type {boolean} */
forceCopyMaterial: boolean;
/** @type {string} */
_lastSourceChunksKey: string;
/**
* The projection cache stride in u32 words: the base layout plus user varying stream words.
*
* @type {number}
*/
_cacheStride: number;
/**
* GPU radix sorter for the projected cache indices.
*
* @type {ComputeRadixSort|null}
*/
gpuSorter: ComputeRadixSort | null;
/**
* Compute projector that builds the per-camera projection cache + sort keys.
*
* @type {GSplatProjector|null}
*/
projector: GSplatProjector | null;
/**
* Interval-based GPU culling + compaction (lazily created on first sort).
*
* @type {GSplatIntervalCompaction|null}
*/
intervalCompaction: GSplatIntervalCompaction | null;
/**
* Manager-owned shared scratch injected at construction; forwarded to the interval compaction so
* its compacted index list is shared with the directional-shadow cull. Null when not provided.
*
* @type {import('./gsplat-hybrid-renderer-scratch.js').GSplatHybridRendererScratch|null}
* @private
*/
private _scratch;
/**
* Per-frame indirect draw slot index (-1 when unallocated).
*
* @type {number}
*/
indirectDrawSlot: number;
/**
* Per-frame indirect dispatch slot index (projector + radix sort passes).
*
* @type {number}
*/
indirectDispatchSlot: number;
/**
* Total intervals from the last interval-compaction dispatch (index into the prefix sum
* for the visible count).
*
* @type {number}
*/
lastCompactedNumIntervals: number;
configureMaterial(): void;
/**
* Toggles the XR stereo (GSPLAT_XR) variant of the forward material. The vertex shader then
* reads the per-eye stereo projection-cache layout and selects the eye via `view_index`. Only
* recompiles when the stereo state changes, so it is cheap to call every frame. Must stay in
* sync with the projector's stereo variant (both driven by the same isStereo value).
*
* @param {boolean} enabled - Whether stereo (2-view) rendering is active.
*/
setStereo(enabled: boolean): void;
update(count: any, textureSize: any): void;
/**
* Lazily creates the GPU sort pipeline resources on first forward use. Kept out of the
* constructor so a hybrid renderer that never renders a forward pass (e.g. one owned by a
* shadow-only manager) allocates none of them.
*
* @private
*/
private _ensureGpuPipeline;
/**
* Runs interval cull + compaction, the projector, and the indirect radix sort for a specific
* camera/view. Shared by the forward render and the picker. Assumes the work buffer is baked and
* render-ready (the manager's version lifecycle marks it before delegating).
*
* @param {GSplatWorld} world - The world providing the work buffer, bounds and states.
* @param {GSplatWorldState} worldState - The world state to sort.
* @param {GraphNode} cameraNode - Camera node used for projection and sort keys.
* @param {number} viewportWidth - Projection viewport width in pixels.
* @param {number} viewportHeight - Projection viewport height in pixels.
* @param {number} alphaClip - Projector producer alpha threshold.
* @param {boolean} pickMode - Whether the projector writes pcId into the cache.
* @param {boolean} isStereo - Whether to project both XR eyes in one pass (forward only).
* @param {GSplatRenderViewParams} params - Per-call gsplat params.
* @returns {StorageBuffer|null} The sorted cache indices, or null if no work was dispatched.
* @private
*/
private sortAndProjectForCamera;
/**
* Allocates per-frame indirect draw and dispatch slots and writes the interval-compaction
* indirect args.
*
* @param {number} numIntervals - Total interval count (index into prefix sum for visible count).
* @private
*/
private allocateAndWriteIntervalIndirectArgs;
/**
* Prepares frustum culling data: updates the GPU transform buffers and computes frustum planes
* from the camera. The actual culling test runs inline in the interval compaction compute shader.
*
* @param {object} world - The {@link GSplatWorld} owning the frustum culler.
* @param {object} worldState - The world state whose splats provide transforms.
* @param {GraphNode} cameraNode - Camera node to cull against.
* @param {object} params - Per-call gsplat params (for fisheye).
* @private
*/
private _runFrustumCulling;
/**
* Computes the min/max effective distances for the current world state (radial or linear).
*
* @param {object} worldState - The world state.
* @param {GraphNode} cameraNode - Camera node to measure distances from.
* @param {boolean} radialSort - Whether radial sorting is enabled.
* @returns {{minDist: number, maxDist: number}} The distance range.
* @private
*/
private computeDistanceRange;
/**
* Configures the renderer to draw from the projector's caches.
*
* @param {number} drawSlot - The indirect draw slot index.
* @param {StorageBuffer} sortedIndices - Globally-sorted indices into projCache.
* @param {StorageBuffer} projCache - Per-splat projection cache produced by the projector.
* @param {StorageBuffer} numSplatsBuffer - GPU-written visible-splat count.
*/
setHybridSortedRendering(drawSlot: number, sortedIndices: StorageBuffer, projCache: StorageBuffer, numSplatsBuffer: StorageBuffer): void;
/**
* Configures and returns a transient pick mesh instance for the picker render pass.
*
* @param {number} drawSlot - The indirect draw slot index.
* @param {StorageBuffer} sortedIndices - Globally-sorted indices into projCache.
* @param {StorageBuffer} projCache - Per-splat projection cache produced by the projector.
* @param {StorageBuffer} numSplatsBuffer - GPU-written visible-splat count.
* @param {number} alphaClip - Fragment alpha threshold for picking.
* @param {number} alphaClipForward - Forward alpha floor (must match {@link GSplatRenderer#frameUpdate}).
* @param {GraphNode} cameraNode - The picker camera node, used to derive the
* `clipToViewZ` reconstruction uniform.
* @returns {MeshInstance} The pick mesh instance.
*/
prepareForPicking(drawSlot: number, sortedIndices: StorageBuffer, projCache: StorageBuffer, numSplatsBuffer: StorageBuffer, alphaClip: number, alphaClipForward: number, cameraNode: GraphNode): MeshInstance;
/**
* Computes the per-camera `clipToViewZ` value into `dst`. The hybrid VS dot-products
* this with the cached `clipPos` to recover linear view depth, used by fog / overdraw
* / prepass.
*
* - Perspective + orthographic: `dst = -inverse(matrix_projection)[row 2]`. The
* projector stores `clipPos.w` in slot [3] and the dot-product yields `-view.z`.
* - Fisheye: `dst = (0, 0, far - near, near)`. The projector stores depthNdc in
* slot [2] and `1.0` in slot [3], so the dot-product reduces to
* `depthNdc * (far - near) + near`, which equals linear `-view.z`.
*
* The destination buffer must be retained by the caller (typically a per-material
* instance field) because the GPU upload happens at draw time.
*
* @param {GraphNode} cameraNode - Camera node to derive the uniform from.
* @param {Float32Array} dst - 4-element destination, written in place.
* @private
*/
private _computeClipToViewZ;
frameUpdate(params: any): void;
_lastNoFog: any;
/**
* Copies material settings from a source material to the internal material.
* Preserves internal defines while copying user defines, parameters, and shader chunks.
* This delivers user customizations (e.g. the `gsplatModifyPS` fragment chunk and its
* parameters) set on `app.scene.gsplat.material` to the hybrid render material. Note that
* the `gsplatModifyVS` chunk is handled by the projector compute instead, and even when
* copied here it is not referenced by the hybrid vertex shader.
*
* @param {ShaderMaterial} sourceMaterial - The source material to copy settings from.
* @private
*/
private copyMaterialSettings;
/**
* Updates pick ID defines to match the work-buffer format.
*
* @param {ShaderMaterial} material - Material to update.
* @returns {boolean} True if the material defines changed.
* @private
*/
private _updateIdDefines;
updateOverdrawMode(params: any): void;
createMeshInstance(): MeshInstance;
}
import { GSplatRenderer } from './gsplat-renderer.js';
import { ShaderMaterial } from '../materials/shader-material.js';
import { MeshInstance } from '../mesh-instance.js';
import { ComputeRadixSort } from '../graphics/radix-sort/compute-radix-sort.js';
import { GSplatProjector } from './gsplat-projector.js';
import { GSplatIntervalCompaction } from './gsplat-interval-compaction.js';
import type { StorageBuffer } from '../../platform/graphics/storage-buffer.js';
import type { GraphNode } from '../graph-node.js';
import type { GraphicsDevice } from '../../platform/graphics/graphics-device.js';
import type { Layer } from '../layer.js';
import type { GSplatWorkBuffer } from './gsplat-work-buffer.js';