UNPKG

playcanvas

Version:

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

181 lines (180 loc) 6.71 kB
import { hashCode } from "../../core/hash.js"; import { SEMANTIC_POSITION, getGlslShaderType } from "../../platform/graphics/constants.js"; import { BlendState } from "../../platform/graphics/blend-state.js"; import { RenderTarget } from "../../platform/graphics/render-target.js"; import { QuadRender } from "../../scene/graphics/quad-render.js"; import { RenderPassShaderQuad } from "../../scene/graphics/render-pass-shader-quad.js"; import { ShaderUtils } from "../../scene/shader-lib/shader-utils.js"; import { GSPLAT_STREAM_INSTANCE } from "../../scene/constants.js"; import glslGsplatProcess from "../../scene/shader-lib/glsl/chunks/gsplat/frag/gsplatProcess.js"; import wgslGsplatProcess from "../../scene/shader-lib/wgsl/chunks/gsplat/frag/gsplatProcess.js"; class GSplatProcessor { _device; _source; _destination; _srcResource; _dstResource; _dstStreamDescriptors; _dstStreamNames; _useAllInputStreams; _srcTextures = []; _renderTarget = null; _quadRender = null; _renderPass = null; _parameters = /* @__PURE__ */ new Map(); blendState = BlendState.NOBLEND; constructor(device, source, destination, options) { this._device = device; this._source = source; this._destination = destination; this._srcResource = source.resource ?? source.component?.resource; this._dstResource = destination.resource ?? destination.component?.resource; this._dstStreamDescriptors = []; this._dstStreamNames = /* @__PURE__ */ new Set(); for (const streamName of destination.streams) { const stream = this._dstResource.format.getStream(streamName); if (stream) { this._dstStreamDescriptors.push(stream); this._dstStreamNames.add(stream.name); } } this._useAllInputStreams = !source.streams?.length; const srcFormat = this._srcResource.format; const srcStreams = this._useAllInputStreams ? [...srcFormat.streams, ...srcFormat.extraStreams] : source.streams.map((name) => ({ name })); for (const stream of srcStreams) { const texture = this._resolveTexture(source, stream.name, this._srcResource); this._srcTextures.push({ name: stream.name, texture }); } this._createRenderTarget(); this._createShader(options); this._renderPass = new RenderPassShaderQuad(device); this._renderPass.quadRender = this._quadRender; this._renderPass.init(this._renderTarget); this._renderPass.colorOps.clear = false; this._renderPass.depthStencilOps.clearDepth = false; } destroy() { this._renderTarget?.destroy(); this._renderTarget = null; this._quadRender?.destroy(); this._quadRender = null; this._renderPass?.destroy(); this._renderPass = null; this._parameters.clear(); } _resolveTexture(binding, name, resource) { if (binding.component) { const stream = resource.format.getStream(name); if (stream?.storage === GSPLAT_STREAM_INSTANCE) { const texture2 = binding.component.getInstanceTexture(name); if (texture2) { return texture2; } } } const texture = resource.getTexture(name); if (!texture) { } return texture; } _createRenderTarget() { const colorBuffers = []; for (const stream of this._dstStreamDescriptors) { const texture = this._resolveTexture(this._destination, stream.name, this._dstResource); if (texture) { colorBuffers.push(texture); } else { } } if (colorBuffers.length > 0) { this._renderTarget = new RenderTarget({ name: "GSplatProcessor-MRT", colorBuffers, depth: false, flipY: true }); } } _createShader(options) { const { processGLSL = "", processWGSL = "" } = options; const device = this._device; const srcFormat = this._srcResource.format; const dstFormat = this._dstResource.format; let inputDeclarations = ""; let readCode = ""; if (this._useAllInputStreams) { const allStreams = [...srcFormat.streams, ...srcFormat.extraStreams]; const sameResource = this._srcResource === this._dstResource; const inputStreamNames = allStreams.filter((s) => !sameResource || !this._dstStreamNames.has(s.name)).map((s) => s.name); inputDeclarations = srcFormat.getInputDeclarations(inputStreamNames); readCode = srcFormat.getReadCode(); } else { inputDeclarations = srcFormat.getInputDeclarations(this._source.streams); } const outputDeclarations = dstFormat.getOutputDeclarations(this._dstStreamDescriptors); const fragmentOutputTypes = this._dstStreamDescriptors.map((stream) => { const info = getGlslShaderType(stream.format); return info.returnType; }); const defines = /* @__PURE__ */ new Map(); this._srcResource.configureMaterialDefines(defines); defines.set("SH_BANDS", "0"); const isWebGPU = device.isWebGPU; const includes = /* @__PURE__ */ new Map(); includes.set("gsplatProcessInputVS", inputDeclarations); includes.set("gsplatProcessOutputVS", outputDeclarations); includes.set("gsplatProcessReadVS", readCode); includes.set("gsplatProcessChunk", isWebGPU ? processWGSL : processGLSL); const hash = hashCode([ isWebGPU ? processWGSL : processGLSL, this._useAllInputStreams ? "1" : "0" ].join("|")); const outputStreams = this._dstStreamDescriptors.map((s) => s.name).join(","); const shader = ShaderUtils.createShader(device, { uniqueName: `GSplatProcessor:${srcFormat.hash};${hash};out=${outputStreams}`, attributes: { vertex_position: SEMANTIC_POSITION }, vertexDefines: defines, fragmentDefines: defines, vertexChunk: "fullscreenQuadVS", fragmentGLSL: glslGsplatProcess, fragmentWGSL: wgslGsplatProcess, fragmentIncludes: includes, fragmentOutputTypes }); this._quadRender = new QuadRender(shader); } setParameter(name, data) { const scopeId = this._device.scope.resolve(name); this._parameters.set(name, { scopeId, data }); } getParameter(name) { return this._parameters.get(name)?.data; } deleteParameter(name) { this._parameters.delete(name); } process() { if (!this._renderPass) { return; } const device = this._device; for (const { name, texture } of this._srcTextures) { device.scope.resolve(name).setValue(texture); } device.scope.resolve("splatTextureSize").setValue(this._srcResource.textureDimensions.x); device.scope.resolve("dstTextureSize").setValue(this._dstResource.textureDimensions.x); device.scope.resolve("srcNumSplats").setValue(this._srcResource.numSplats); device.scope.resolve("dstNumSplats").setValue(this._dstResource.numSplats); for (const [name, value] of this._srcResource.parameters) { device.scope.resolve(name).setValue(value); } for (const [, param] of this._parameters) { param.scopeId.setValue(param.data); } this._renderPass.blendState = this.blendState; this._renderPass.render(); } } export { GSplatProcessor };