UNPKG

@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.

389 lines 16.7 kB
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 { ShadowDepthWrapper } from "../../Materials/shadowDepthWrapper.js"; import { ShaderMaterial } from "../../Materials/shaderMaterial.js"; import "../../Shaders/gaussianSplatting.fragment.js"; import "../../Shaders/gaussianSplatting.vertex.js"; import "../../ShadersWGSL/gaussianSplatting.fragment.js"; import "../../ShadersWGSL/gaussianSplatting.vertex.js"; import "../../Shaders/gaussianSplattingDepth.fragment.js"; import "../../Shaders/gaussianSplattingDepth.vertex.js"; import "../../ShadersWGSL/gaussianSplattingDepth.fragment.js"; import "../../ShadersWGSL/gaussianSplattingDepth.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._sourceMesh = null; this.backFaceCulling = false; this.shadowDepthWrapper = GaussianSplattingMaterial._MakeGaussianSplattingShadowDepthWrapper(scene, this.shaderLanguage); } /** * 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; } if (!this._sourceMesh) { return false; } const engine = scene.getEngine(); const gsMesh = this._sourceMesh; // Misc. PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, false, defines, undefined, undefined, undefined, this._isVertexOutputInvariant); // 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 PrepareAttributesForInstances(GaussianSplattingMaterial._Attribs, defines); PrepareUniformsAndSamplersList({ uniformsNames: GaussianSplattingMaterial._Uniforms, uniformBuffersNames: GaussianSplattingMaterial._UniformBuffers, samplers: GaussianSplattingMaterial._Samplers, defines: defines, }); AddClipPlaneUniforms(GaussianSplattingMaterial._Uniforms); const join = defines.toString(); const effect = scene.getEngine().createEffect("gaussianSplatting", { attributes: GaussianSplattingMaterial._Attribs, uniformsNames: GaussianSplattingMaterial._Uniforms, uniformBuffersNames: GaussianSplattingMaterial._UniformBuffers, samplers: GaussianSplattingMaterial._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; } /** * GaussianSplattingMaterial belongs to a single mesh * @param mesh mesh this material belongs to */ setSourceMesh(mesh) { this._sourceMesh = mesh; } /** * 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() * camera.viewport.width; const renderHeight = engine.getRenderHeight() * camera.viewport.height; const gsMaterial = mesh.material; if (!gsMaterial._sourceMesh) { return; } const gsMesh = gsMaterial._sourceMesh; // 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); effect.setFloat("alpha", gsMaterial.alpha); 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); } static _MakeGaussianSplattingShadowDepthWrapper(scene, shaderLanguage) { const shaderMaterial = new ShaderMaterial("gaussianSplattingDepth", scene, { vertex: "gaussianSplattingDepth", fragment: "gaussianSplattingDepth", }, { attributes: GaussianSplattingMaterial._Attribs, uniforms: GaussianSplattingMaterial._Uniforms, samplers: GaussianSplattingMaterial._Samplers, uniformBuffers: GaussianSplattingMaterial._UniformBuffers, shaderLanguage: shaderLanguage, }); const shadowDepthWrapper = new ShadowDepthWrapper(shaderMaterial, scene, { standalone: true, }); shaderMaterial.onBindObservable.add((mesh) => { const effect = shaderMaterial.getEffect(); const gsMaterial = mesh.material; const gsMesh = mesh; mesh.getMeshUniformBuffer().bindToEffect(effect, "Mesh"); shaderMaterial.bindView(effect); shaderMaterial.bindViewProjection(effect); const shadowmapWidth = scene.getEngine().getRenderWidth(); const shadowmapHeight = scene.getEngine().getRenderHeight(); effect.setFloat2("invViewport", 1 / shadowmapWidth, 1 / shadowmapHeight); const projection = scene.getProjectionMatrix(); const t = projection.m[5]; const focal = (shadowmapWidth * t) / 2.0; effect.setFloat2("focal", focal, focal); effect.setFloat("kernelSize", gsMaterial && gsMaterial.kernelSize ? gsMaterial.kernelSize : GaussianSplattingMaterial.KernelSize); 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); } }); return shadowDepthWrapper; } /** * 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; GaussianSplattingMaterial._Attribs = [VertexBuffer.PositionKind, "splatIndex0", "splatIndex1", "splatIndex2", "splatIndex3"]; GaussianSplattingMaterial._Samplers = ["covariancesATexture", "covariancesBTexture", "centersTexture", "colorsTexture", "shTexture0", "shTexture1", "shTexture2"]; GaussianSplattingMaterial._UniformBuffers = ["Scene", "Mesh"]; GaussianSplattingMaterial._Uniforms = [ "world", "view", "projection", "vFogInfos", "vFogColor", "logarithmicDepthConstant", "invViewport", "dataTextureSize", "focal", "eyePosition", "kernelSize", "viewDirectionFactor", "alpha", ]; RegisterClass("BABYLON.GaussianSplattingMaterial", GaussianSplattingMaterial); //# sourceMappingURL=gaussianSplattingMaterial.js.map