UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

507 lines (506 loc) 18.9 kB
/** * @import { BoundingBox } from '../../../core/shape/bounding-box.js' * @import { Entity } from '../../entity.js' * @import { EventHandle } from '../../../core/event-handle.js' * @import { GSplatComponentSystem } from './system.js' * @import { GSplatResourceBase } from '../../../scene/gsplat/gsplat-resource-base.js' * @import { ScopeId } from '../../../platform/graphics/scope-id.js' * @import { ShaderMaterial } from '../../../scene/materials/shader-material.js' * @import { StorageBuffer } from '../../../platform/graphics/storage-buffer.js' * @import { Texture } from '../../../platform/graphics/texture.js' */ /** * The GSplatComponent enables an {@link Entity} to render 3D Gaussian Splats. Splats are always * loaded from {@link Asset}s rather than being created programmatically. The asset type is * `gsplat` which supports multiple file formats including `.ply`, `.sog`, `.meta.json` (SOG * format), and `.lod-meta.json` (streaming LOD format). * * You should never need to use the GSplatComponent constructor directly. To add an * GSplatComponent to an {@link Entity}, use {@link Entity#addComponent}: * * ```javascript * const entity = pc.Entity(); * entity.addComponent('gsplat', { * asset: asset * }); * ``` * * Once the GSplatComponent is added to the entity, you can access it via the {@link Entity#gsplat} * property: * * ```javascript * entity.gsplat.customAabb = new pc.BoundingBox(new pc.Vec3(), new pc.Vec3(10, 10, 10)); * * console.log(entity.gsplat.customAabb); * ``` * * ## Unified Rendering * * The {@link GSplatComponent#unified} property enables unified rendering mode, which provides * advanced features for Gaussian Splats: * * - **Global Sorting**: Multiple splat components are sorted together in a single unified sort, * eliminating visibility artifacts and popping effects when splat components overlap. * - **LOD Streaming**: Dynamically loads and renders appropriate levels of detail based on camera * distance, enabling efficient rendering of massive splat scenes. * * ```javascript * // Enable unified rendering for advanced features * entity.gsplat.unified = true; * ``` * * Relevant Engine API examples: * * - [Simple Splat Loading](https://playcanvas.github.io/#/gaussian-splatting/simple) * - [Global Sorting](https://playcanvas.github.io/#/gaussian-splatting/global-sorting) * - [LOD](https://playcanvas.github.io/#/gaussian-splatting/lod) * - [LOD Instances](https://playcanvas.github.io/#/gaussian-splatting/lod-instances) * - [LOD Streaming](https://playcanvas.github.io/#/gaussian-splatting/lod-streaming) * - [LOD Streaming with Spherical Harmonics](https://playcanvas.github.io/#/gaussian-splatting/lod-streaming-sh) * - [Multi-Splat](https://playcanvas.github.io/#/gaussian-splatting/multi-splat) * - [Multi-View](https://playcanvas.github.io/#/gaussian-splatting/multi-view) * - [Picking](https://playcanvas.github.io/#/gaussian-splatting/picking) * - [Reveal Effect](https://playcanvas.github.io/#/gaussian-splatting/reveal) * - [Shader Effects](https://playcanvas.github.io/#/gaussian-splatting/shader-effects) * - [Spherical Harmonics](https://playcanvas.github.io/#/gaussian-splatting/spherical-harmonics) * * @hideconstructor * @category Graphics */ export class GSplatComponent extends Component { /** * Create a new GSplatComponent. * * @param {GSplatComponentSystem} system - The ComponentSystem that created this Component. * @param {Entity} entity - The Entity that this Component is attached to. */ constructor(system: GSplatComponentSystem, entity: Entity); /** @private */ private _layers; /** * @type {GSplatInstance|null} * @private */ private _instance; /** * @type {GSplatPlacement|null} * @private */ private _placement; /** * Unique identifier for this component, used by the picking system. * * @type {number} * @private */ private _id; /** * @type {ShaderMaterial|null} * @private */ private _materialTmp; /** @private */ private _highQualitySH; /** * Base distance for the first LOD transition (LOD 0 to LOD 1). * * @type {number} * @private */ private _lodBaseDistance; /** * Geometric multiplier between successive LOD distance thresholds. * * @type {number} * @private */ private _lodMultiplier; /** * @type {BoundingBox|null} * @private */ private _customAabb; /** * @type {AssetReference} * @private */ private _assetReference; /** * Direct resource reference (for container splats). * * @type {GSplatResourceBase|null} * @private */ private _resource; /** * @type {EventHandle|null} * @private */ private _evtLayersChanged; /** * @type {EventHandle|null} * @private */ private _evtLayerAdded; /** * @type {EventHandle|null} * @private */ private _evtLayerRemoved; /** @private */ private _castShadows; /** * Whether to use the unified gsplat rendering. * * @type {boolean} * @private */ private _unified; /** * Per-instance shader parameters. Stores objects with scopeId and data. * * @type {Map<string, {scopeId: ScopeId, data: *}>} * @private */ private _parameters; /** * Render mode for work buffer updates. * * @type {number} * @private */ private _workBufferUpdate; /** * Custom shader modify code for this component (object with code and pre-computed hash). * * @type {{ code: string, hash: number }|null} * @private */ private _workBufferModifier; /** * Sets a custom object space bounding box for visibility culling of the attached gsplat. * * @type {BoundingBox|null} */ set customAabb(value: BoundingBox | null); /** * Gets the custom object space bounding box for visibility culling of the attached gsplat. * Returns the custom AABB if set, otherwise falls back to the resource's AABB. * * @type {BoundingBox|null} */ get customAabb(): BoundingBox | null; /** * Sets a {@link GSplatInstance} on the component. If not set or loaded, it returns null. * * @type {GSplatInstance|null} * @ignore */ set instance(value: GSplatInstance | null); /** * Gets the {@link GSplatInstance} on the component. * * @type {GSplatInstance|null} * @ignore */ get instance(): GSplatInstance | null; /** * Sets the material used to render the gsplat. * * **Note:** This setter is only supported when {@link unified} is `false`. When it's true, multiple * gsplat components share a single material per camera/layer combination. To access materials in * unified mode, use {@link GSplatComponentSystem#getMaterial}. * * @param {ShaderMaterial} value - The material instance. */ set material(value: ShaderMaterial); /** * Gets the material used to render the gsplat. * * **Note:** This getter returns `null` when {@link unified} is `true`. In unified mode, materials are * organized per camera/layer combination rather than per component. To access materials in * unified mode, use {@link GSplatComponentSystem#getMaterial}. * * @type {ShaderMaterial|null} */ get material(): ShaderMaterial | null; /** * Sets whether to use the high quality or the approximate (but fast) spherical-harmonic calculation when rendering SOG data. * * The low quality approximation evaluates the scene's spherical harmonic contributions * along the camera's Z-axis instead of using each gaussian's view vector. This results * in gaussians being accurate at the center of the screen and becoming less accurate * as they appear further from the center. This is a good trade-off for performance * when rendering large SOG datasets, especially on mobile devices. * * Defaults to false. * * @type {boolean} */ set highQualitySH(value: boolean); /** * Gets whether the high quality (true) or the fast approximate (false) spherical-harmonic calculation is used when rendering SOG data. * * @type {boolean} */ get highQualitySH(): boolean; /** * Sets whether gsplat will cast shadows for lights that have shadow casting enabled. Defaults * to false. * * @type {boolean} */ set castShadows(value: boolean); /** * Gets whether gsplat will cast shadows for lights that have shadow casting enabled. * * @type {boolean} */ get castShadows(): boolean; /** * Sets the base distance for the first LOD transition (LOD 0 to LOD 1). Objects closer * than this distance use the highest quality LOD. Each subsequent LOD level transitions * at a progressively larger distance, controlled by {@link lodMultiplier}. Clamped to a * minimum of 0.1. Defaults to 5. * * @type {number} */ set lodBaseDistance(value: number); /** * Gets the base distance for the first LOD transition. * * @type {number} */ get lodBaseDistance(): number; /** * Sets the multiplier between successive LOD distance thresholds. Each LOD level * transitions at this factor times the previous level's distance, creating a geometric * progression. Lower values keep higher quality at distance; higher values switch to * coarser LODs sooner. Clamped to a minimum of 1.2 to avoid degenerate logarithmic LOD * computation. LOD distances are automatically compensated for the camera's field of * view — a wider FOV makes objects appear smaller on screen, so LOD switches to coarser * levels sooner to match the reduced screen-space detail. Defaults to 3. * * @type {number} */ set lodMultiplier(value: number); /** * Gets the geometric multiplier between successive LOD distance thresholds. * * @type {number} */ get lodMultiplier(): number; /** * @deprecated Use {@link lodBaseDistance} and {@link lodMultiplier} instead. * @type {number[]|null} */ set lodDistances(value: number[]); /** * @deprecated Use {@link lodBaseDistance} and {@link lodMultiplier} instead. * @type {number[]} */ get lodDistances(): number[]; /** * @deprecated Use app.scene.gsplat.splatBudget instead for global budget control. * @type {number} */ set splatBudget(value: number); get splatBudget(): number; /** * Sets whether to use the unified gsplat rendering. Default is false. * * Note: Material handling differs between modes. When unified is false, use * {@link GSplatComponent#material}. When unified is true, materials are shared per * camera/layer - use {@link GSplatComponentSystem#getMaterial} instead. * * @type {boolean} */ set unified(value: boolean); /** * Gets whether to use the unified gsplat rendering. * * @type {boolean} * @alpha */ get unified(): boolean; /** * Gets the unique identifier for this component. This ID is used by the picking system * and is also written to the work buffer when `app.scene.gsplat.enableIds` is enabled, making * it available to custom shaders for effects like highlighting or animation. * * @type {number} */ get id(): number; /** * Sets the work buffer update mode. Only applicable in unified rendering mode. * * In unified mode, splat data is rendered to a work buffer only when needed (e.g., when * transforms change). Can be: * - {@link WORKBUFFER_UPDATE_AUTO}: Update only when needed (default). * - {@link WORKBUFFER_UPDATE_ONCE}: Force update this frame, then switch to AUTO. * - {@link WORKBUFFER_UPDATE_ALWAYS}: Update every frame. * * This is typically useful when using custom shader code via {@link workBufferModifier} that * depends on external factors like time or animated uniforms. * * Note: {@link WORKBUFFER_UPDATE_ALWAYS} has a performance impact as it re-renders * all splat data to the work buffer every frame. Where possible, consider using shader * customization on the unified gsplat material (`app.scene.gsplat.material`) which is * applied during final rendering without re-rendering the work buffer. * * @type {number} */ set workBufferUpdate(value: number); /** * Gets the work buffer update mode. * * @type {number} */ get workBufferUpdate(): number; /** * Sets custom shader code for modifying splats when written to the work buffer. Only * applicable in unified rendering mode. * * Must provide all three functions: * - `modifySplatCenter`: Modify the splat center position * - `modifySplatRotationScale`: Modify the splat rotation and scale * - `modifySplatColor`: Modify the splat color * * Calling this method automatically triggers a work buffer re-render. * * @param {{ glsl?: string, wgsl?: string }|null} value - The modifier code for GLSL and/or WGSL. * @example * entity.gsplat.setWorkBufferModifier({ * glsl: ` * void modifySplatCenter(inout vec3 center) {} * void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {} * void modifySplatColor(vec3 center, inout vec4 color) { color.rgb *= vec3(1.0, 0.0, 0.0); } * `, * wgsl: ` * fn modifySplatCenter(center: ptr<function, vec3f>) {} * fn modifySplatRotationScale(originalCenter: vec3f, modifiedCenter: vec3f, rotation: ptr<function, vec4f>, scale: ptr<function, vec3f>) {} * fn modifySplatColor(center: vec3f, color: ptr<function, vec4f>) { (*color).r = 1.0; (*color).g = 0.0; (*color).b = 0.0; } * ` * }); */ setWorkBufferModifier(value: { glsl?: string; wgsl?: string; } | null): void; /** * Sets an array of layer IDs ({@link Layer#id}) to which this gsplat should belong. Don't * push, pop, splice or modify this array. If you want to change it, set a new one instead. * * @type {number[]} */ set layers(value: number[]); /** * Gets the array of layer IDs ({@link Layer#id}) to which this gsplat belongs. * * @type {number[]} */ get layers(): number[]; /** * Sets the gsplat asset for this gsplat component. Can also be an asset id. * * @type {Asset|number} */ set asset(value: Asset | number); /** * Gets the gsplat asset id for this gsplat component. * * @type {Asset|number} */ get asset(): Asset | number; /** * Sets a GSplat resource directly (for procedural/container splats). * When set, this takes precedence over the asset property. * * @type {GSplatResourceBase|null} */ set resource(value: GSplatResourceBase | null); /** * Gets the GSplat resource. Returns the directly set resource if available, * otherwise returns the resource from the assigned asset. * * @type {GSplatResourceBase|null} */ get resource(): GSplatResourceBase | null; /** @private */ private destroyInstance; /** @private */ private addToLayers; removeFromLayers(): void; /** @private */ private onRemoveChild; /** @private */ private onInsertChild; onRemove(): void; onLayersChanged(oldComp: any, newComp: any): void; onLayerAdded(layer: any): void; onLayerRemoved(layer: any): void; /** * Stop rendering this component without removing its mesh instance from the scene hierarchy. */ hide(): void; /** * Enable rendering of the component if hidden using {@link GSplatComponent#hide}. */ show(): void; /** * Sets a shader parameter for this gsplat instance. Parameters set here are applied * during unified rendering. * * @param {string} name - The name of the parameter (uniform name in shader). * @param {number|number[]|ArrayBufferView|Texture|StorageBuffer} data - The value for the parameter. */ setParameter(name: string, data: number | number[] | ArrayBufferView | Texture | StorageBuffer): void; /** * Gets a shader parameter value previously set with {@link setParameter}. * * @param {string} name - The name of the parameter. * @returns {number|number[]|ArrayBufferView|undefined} The parameter value, or undefined if not set. */ getParameter(name: string): number | number[] | ArrayBufferView | undefined; /** * Deletes a shader parameter previously set with {@link setParameter}. * * @param {string} name - The name of the parameter to delete. */ deleteParameter(name: string): void; /** * Gets an instance texture by name. Instance textures are per-component textures defined * in the resource's format with `storage: GSPLAT_STREAM_INSTANCE`. Only available in unified mode. * * @param {string} name - The name of the texture. * @returns {Texture|null} The texture, or null if not found or not in unified mode. * @example * // Add an instance stream to the resource format * resource.format.addExtraStreams([ * { name: 'instanceTint', format: pc.PIXELFORMAT_RGBA8, storage: pc.GSPLAT_STREAM_INSTANCE } * ]); * * // Get the instance texture and fill it with data * const texture = entity.gsplat.getInstanceTexture('instanceTint'); * if (texture) { * const data = texture.lock(); * // Fill texture data... * texture.unlock(); * } */ getInstanceTexture(name: string): Texture | null; _onGSplatAssetAdded(): void; _onGSplatAssetLoad(): void; _onGSplatAssetUnload(): void; _onGSplatAssetRemove(): void; } import { Component } from '../component.js'; import type { BoundingBox } from '../../../core/shape/bounding-box.js'; import { GSplatInstance } from '../../../scene/gsplat/gsplat-instance.js'; import type { ShaderMaterial } from '../../../scene/materials/shader-material.js'; import { Asset } from '../../asset/asset.js'; import type { GSplatResourceBase } from '../../../scene/gsplat/gsplat-resource-base.js'; import type { Texture } from '../../../platform/graphics/texture.js'; import type { StorageBuffer } from '../../../platform/graphics/storage-buffer.js'; import type { GSplatComponentSystem } from './system.js'; import type { Entity } from '../../entity.js';