Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/framework/components/gsplat/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class GSplatComponent extends Component {
*/
_materialTmp = null;

/** @private */
_fastSH = true;

/**
* @type {BoundingBox|null}
* @private
Expand Down Expand Up @@ -206,6 +209,37 @@ class GSplatComponent extends Component {
return this._instance?.material ?? this._materialTmp ?? null;
}

/**
* Sets whether to use the fast (but approximate) spherical-harmonic calculation when rendering SOGS data.
*
* The fast 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 SOGS datasets, especially on mobile devices.
*
* Defaults to true.
*
* @type {boolean}
*/
set fastSH(value) {
if (value !== this._fastSH) {
this._fastSH = value;
if (this._instance) {
this._instance.setFastSH(value);
}
}
}

/**
* Gets whether the fast spherical-harmonic calculation is used when rendering SOGS data.
*
* @type {boolean}
*/
get fastSH() {
return this._fastSH;
}

/**
* Sets whether gsplat will cast shadows for lights that have shadow casting enabled. Defaults
* to false.
Expand Down Expand Up @@ -469,7 +503,10 @@ class GSplatComponent extends Component {
// create new instance
const asset = this._assetReference.asset;
if (asset) {
this.instance = new GSplatInstance(asset.resource, this._materialTmp);
this.instance = new GSplatInstance(asset.resource, {
material: this._materialTmp,
fastSH: this._fastSH
});
this._materialTmp = null;
this.customAabb = this.instance.resource.aabb.clone();
}
Expand Down
5 changes: 3 additions & 2 deletions src/framework/components/gsplat/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ const _schema = [
// order matters here
const _properties = [
'castShadows',
'material',
'fastSH',
'asset',
'layers',
'material'
'layers'
];

/**
Expand Down
41 changes: 36 additions & 5 deletions src/scene/gsplat/gsplat-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Mat4 } from '../../core/math/mat4.js';
import { Vec3 } from '../../core/math/vec3.js';
import { CULLFACE_NONE, SEMANTIC_ATTR13, SEMANTIC_POSITION, PIXELFORMAT_R32U } from '../../platform/graphics/constants.js';
import { MeshInstance } from '../mesh-instance.js';
import { GSplatResolveSH } from './gsplat-resolve-sh.js';
import { GSplatSorter } from './gsplat-sorter.js';
import { GSplatSogsData } from './gsplat-sogs-data.js';
import { ShaderMaterial } from '../materials/shader-material.js';
import { BLEND_NONE, BLEND_PREMULTIPLIED } from '../constants.js';

Expand Down Expand Up @@ -34,13 +36,16 @@ class GSplatInstance {

options = {};

/** @type {GSplatSorter | null} */
/** @type {GSplatSorter|null} */
sorter = null;

lastCameraPosition = new Vec3();

lastCameraDirection = new Vec3();

/** @type {GSplatResolveSH|null} */
resolveSH = null;

/**
* List of cameras this instance is visible for. Updated every frame by the renderer.
*
Expand All @@ -51,9 +56,11 @@ class GSplatInstance {

/**
* @param {GSplatResourceBase} resource - The splat instance.
* @param {ShaderMaterial|null} material - The material instance.
* @param {object} [options] - Options for the instance.
* @param {ShaderMaterial|null} [options.material] - The material instance.
* @param {boolean} [options.fastSH] - Whether to use the fast, approximate spherical harmonic calculation. Only applies to SOGS data.
*/
constructor(resource, material) {
constructor(resource, options = {}) {
this.resource = resource;

// create the order texture
Expand All @@ -63,9 +70,9 @@ class GSplatInstance {
resource.evalTextureSize(resource.numSplats)
);

if (material) {
if (options.material) {
// material is provided
this._material = material;
this._material = options.material;

// patch splat order
this._material.setParameter('splatOrder', this.orderTexture);
Expand Down Expand Up @@ -111,9 +118,13 @@ class GSplatInstance {
// update splat count on the material
this.material.setParameter('numSplats', count);
});

// configure sogs sh resolve
this.setFastSH(options.fastSH ?? false);
}

destroy() {
this.resolveSH?.destroy();
this.material?.destroy();
this.meshInstance?.destroy();
this.sorter?.destroy();
Expand Down Expand Up @@ -212,10 +223,30 @@ class GSplatInstance {
const camera = this.cameras[0];
this.sort(camera._node);

// resolve spherical harmonics
this.resolveSH?.render(camera._node, this.meshInstance.node.getWorldTransform());

// we get new list of cameras each frame
this.cameras.length = 0;
}
}

setFastSH(value) {
const { resource } = this;
const { gsplatData } = resource;

if (gsplatData instanceof GSplatSogsData &&
gsplatData.shBands > 0 &&
value !== !!this.resolveSH) {

if (this.resolveSH) {
this.resolveSH.destroy();
this.resolveSH = null;
} else {
this.resolveSH = new GSplatResolveSH(resource.device, this);
}
}
}
}

export { GSplatInstance };
Loading