@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
326 lines • 13.2 kB
JavaScript
import { SerializationHelper } from "../../Misc/decorators.serialization.js";
import { VertexBuffer } from "../../Buffers/buffer.js";
import { MaterialDefines } from "../../Materials/materialDefines.js";
import { PushMaterial } from "../../Materials/pushMaterial.js";
import { RegisterClass } from "../../Misc/typeStore.js";
import { AddClipPlaneUniforms, BindClipPlane } from "../clipPlaneMaterialHelper.js";
import { Camera } from "../../Cameras/camera.js";
import "../../Shaders/gaussianSplatting.fragment.js";
import "../../Shaders/gaussianSplatting.vertex.js";
import "../../ShadersWGSL/gaussianSplatting.fragment.js";
import "../../ShadersWGSL/gaussianSplatting.vertex.js";
import { BindFogParameters, BindLogDepth, PrepareAttributesForInstances, PrepareDefinesForAttributes, PrepareDefinesForFrameBoundValues, PrepareDefinesForMisc, PrepareUniformsAndSamplersList, } from "../materialHelper.functions.js";
/**
* @internal
*/
class GaussianSplattingMaterialDefines extends MaterialDefines {
/**
* Constructor of the defines.
*/
constructor() {
super();
this.FOG = false;
this.THIN_INSTANCES = true;
this.LOGARITHMICDEPTH = false;
this.CLIPPLANE = false;
this.CLIPPLANE2 = false;
this.CLIPPLANE3 = false;
this.CLIPPLANE4 = false;
this.CLIPPLANE5 = false;
this.CLIPPLANE6 = false;
this.SH_DEGREE = 0;
this.COMPENSATION = false;
this.rebuild();
}
}
/**
* GaussianSplattingMaterial material used to render Gaussian Splatting
* @experimental
*/
export class GaussianSplattingMaterial extends PushMaterial {
/**
* Instantiates a Gaussian Splatting Material in the given scene
* @param name The friendly name of the material
* @param scene The scene to add the material to
*/
constructor(name, scene) {
super(name, scene);
/**
* Point spread function (default 0.3). Can be overriden per GS material, otherwise, using default static `KernelSize` value
*/
this.kernelSize = GaussianSplattingMaterial.KernelSize;
this._compensation = GaussianSplattingMaterial.Compensation;
// set to true when material defines are dirty
this._isDirty = false;
this.backFaceCulling = false;
}
/**
* Set compensation default value is `GaussianSplattingMaterial.Compensation`
*/
set compensation(value) {
this._isDirty = this._isDirty != value;
this._compensation = value;
}
/**
* Get compensation
*/
get compensation() {
return this._compensation;
}
/**
* Gets a boolean indicating that current material needs to register RTT
*/
get hasRenderTargetTextures() {
return false;
}
/**
* Specifies whether or not this material should be rendered in alpha test mode.
* @returns false
*/
needAlphaTesting() {
return false;
}
/**
* Specifies whether or not this material should be rendered in alpha blend mode.
* @returns true
*/
needAlphaBlending() {
return true;
}
/**
* Checks whether the material is ready to be rendered for a given mesh.
* @param mesh The mesh to render
* @param subMesh The submesh to check against
* @returns true if all the dependencies are ready (Textures, Effects...)
*/
isReadyForSubMesh(mesh, subMesh) {
const useInstances = true;
const drawWrapper = subMesh._drawWrapper;
let defines = subMesh.materialDefines;
if (defines && this._isDirty) {
defines.markAsUnprocessed();
}
if (drawWrapper.effect && this.isFrozen) {
if (drawWrapper._wasPreviouslyReady && drawWrapper._wasPreviouslyUsingInstances === useInstances) {
return true;
}
}
if (!subMesh.materialDefines) {
defines = subMesh.materialDefines = new GaussianSplattingMaterialDefines();
}
const scene = this.getScene();
if (this._isReadyForSubMesh(subMesh)) {
return true;
}
const engine = scene.getEngine();
const gsMesh = mesh;
// Misc.
PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, false, defines);
// Values that need to be evaluated on every frame
PrepareDefinesForFrameBoundValues(scene, engine, this, defines, useInstances, null, true);
// Attribs
PrepareDefinesForAttributes(mesh, defines, false, false);
// SH is disabled for webGL1
if (engine.version > 1 || engine.isWebGPU) {
defines["SH_DEGREE"] = gsMesh.shDegree;
}
// Compensation
const splatMaterial = gsMesh.material;
defines["COMPENSATION"] = splatMaterial && splatMaterial.compensation ? splatMaterial.compensation : GaussianSplattingMaterial.Compensation;
// Get correct effect
if (defines.isDirty) {
defines.markAsProcessed();
scene.resetCachedMaterial();
//Attributes
const attribs = [VertexBuffer.PositionKind, "splatIndex"];
PrepareAttributesForInstances(attribs, defines);
const uniforms = [
"world",
"view",
"projection",
"vFogInfos",
"vFogColor",
"logarithmicDepthConstant",
"invViewport",
"dataTextureSize",
"focal",
"eyePosition",
"kernelSize",
"viewDirectionFactor",
];
const samplers = ["covariancesATexture", "covariancesBTexture", "centersTexture", "colorsTexture", "shTexture0", "shTexture1", "shTexture2"];
const uniformBuffers = ["Scene", "Mesh"];
PrepareUniformsAndSamplersList({
uniformsNames: uniforms,
uniformBuffersNames: uniformBuffers,
samplers: samplers,
defines: defines,
});
AddClipPlaneUniforms(uniforms);
const join = defines.toString();
const effect = scene.getEngine().createEffect("gaussianSplatting", {
attributes: attribs,
uniformsNames: uniforms,
uniformBuffersNames: uniformBuffers,
samplers: samplers,
defines: join,
onCompiled: this.onCompiled,
onError: this.onError,
indexParameters: {},
shaderLanguage: this._shaderLanguage,
extraInitializationsAsync: async () => {
if (this._shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
await Promise.all([import("../../ShadersWGSL/gaussianSplatting.fragment.js"), import("../../ShadersWGSL/gaussianSplatting.vertex.js")]);
}
else {
await Promise.all([import("../../Shaders/gaussianSplatting.fragment.js"), import("../../Shaders/gaussianSplatting.vertex.js")]);
}
},
}, engine);
subMesh.setEffect(effect, defines, this._materialContext);
}
if (!subMesh.effect || !subMesh.effect.isReady()) {
return false;
}
defines._renderId = scene.getRenderId();
drawWrapper._wasPreviouslyReady = true;
drawWrapper._wasPreviouslyUsingInstances = useInstances;
this._isDirty = false;
return true;
}
/**
* Bind material effect for a specific Gaussian Splatting mesh
* @param mesh Gaussian splatting mesh
* @param effect Splatting material or node material
* @param scene scene that contains mesh and camera used for rendering
*/
static BindEffect(mesh, effect, scene) {
const engine = scene.getEngine();
const camera = scene.activeCamera;
const renderWidth = engine.getRenderWidth();
const renderHeight = engine.getRenderHeight();
const gsMesh = mesh;
const gsMaterial = gsMesh.material;
// check if rigcamera, get number of rigs
const numberOfRigs = camera?.rigParent?.rigCameras.length || 1;
effect.setFloat2("invViewport", 1 / (renderWidth / numberOfRigs), 1 / renderHeight);
let focal = 1000;
if (camera) {
/*
more explicit version:
const t = camera.getProjectionMatrix().m[5];
const FovY = Math.atan(1.0 / t) * 2.0;
focal = renderHeight / 2.0 / Math.tan(FovY / 2.0);
Using a shorter version here to not have tan(atan) and 2.0 factor
*/
const t = camera.getProjectionMatrix().m[5];
if (camera.fovMode == Camera.FOVMODE_VERTICAL_FIXED) {
focal = (renderHeight * t) / 2.0;
}
else {
focal = (renderWidth * t) / 2.0;
}
}
effect.setFloat2("focal", focal, focal);
effect.setVector3("viewDirectionFactor", gsMesh.viewDirectionFactor);
effect.setFloat("kernelSize", gsMaterial && gsMaterial.kernelSize ? gsMaterial.kernelSize : GaussianSplattingMaterial.KernelSize);
scene.bindEyePosition(effect, "eyePosition", true);
if (gsMesh.covariancesATexture) {
const textureSize = gsMesh.covariancesATexture.getSize();
effect.setFloat2("dataTextureSize", textureSize.width, textureSize.height);
effect.setTexture("covariancesATexture", gsMesh.covariancesATexture);
effect.setTexture("covariancesBTexture", gsMesh.covariancesBTexture);
effect.setTexture("centersTexture", gsMesh.centersTexture);
effect.setTexture("colorsTexture", gsMesh.colorsTexture);
if (gsMesh.shTextures) {
for (let i = 0; i < gsMesh.shTextures?.length; i++) {
effect.setTexture(`shTexture${i}`, gsMesh.shTextures[i]);
}
}
}
}
/**
* Binds the submesh to this material by preparing the effect and shader to draw
* @param world defines the world transformation matrix
* @param mesh defines the mesh containing the submesh
* @param subMesh defines the submesh to bind the material to
*/
bindForSubMesh(world, mesh, subMesh) {
const scene = this.getScene();
const defines = subMesh.materialDefines;
if (!defines) {
return;
}
const effect = subMesh.effect;
if (!effect) {
return;
}
this._activeEffect = effect;
// Matrices Mesh.
mesh.getMeshUniformBuffer().bindToEffect(effect, "Mesh");
mesh.transferToEffect(world);
// Bind data
const mustRebind = this._mustRebind(scene, effect, subMesh, mesh.visibility);
if (mustRebind) {
this.bindView(effect);
this.bindViewProjection(effect);
GaussianSplattingMaterial.BindEffect(mesh, this._activeEffect, scene);
// Clip plane
BindClipPlane(effect, this, scene);
}
else if (scene.getEngine()._features.needToAlwaysBindUniformBuffers) {
this._needToBindSceneUbo = true;
}
// Fog
BindFogParameters(scene, mesh, effect);
// Log. depth
if (this.useLogarithmicDepth) {
BindLogDepth(defines, effect, scene);
}
this._afterBind(mesh, this._activeEffect, subMesh);
}
/**
* Clones the material.
* @param name The cloned name.
* @returns The cloned material.
*/
clone(name) {
return SerializationHelper.Clone(() => new GaussianSplattingMaterial(name, this.getScene()), this);
}
/**
* Serializes the current material to its JSON representation.
* @returns The JSON representation.
*/
serialize() {
const serializationObject = super.serialize();
serializationObject.customType = "BABYLON.GaussianSplattingMaterial";
return serializationObject;
}
/**
* Gets the class name of the material
* @returns "GaussianSplattingMaterial"
*/
getClassName() {
return "GaussianSplattingMaterial";
}
/**
* Parse a JSON input to create back a Gaussian Splatting material.
* @param source The JSON data to parse
* @param scene The scene to create the parsed material in
* @param rootUrl The root url of the assets the material depends upon
* @returns the instantiated GaussianSplattingMaterial.
*/
static Parse(source, scene, rootUrl) {
return SerializationHelper.Parse(() => new GaussianSplattingMaterial(source.name, scene), source, scene, rootUrl);
}
}
/**
* Point spread function (default 0.3). Can be overriden per GS material
*/
GaussianSplattingMaterial.KernelSize = 0.3;
/**
* Compensation
*/
GaussianSplattingMaterial.Compensation = false;
RegisterClass("BABYLON.GaussianSplattingMaterial", GaussianSplattingMaterial);
//# sourceMappingURL=gaussianSplattingMaterial.js.map