diff --git a/examples/src/examples/loaders/splat.mjs b/examples/src/examples/loaders/splat.mjs index f4cff4da45f..45bd94d96c6 100644 --- a/examples/src/examples/loaders/splat.mjs +++ b/examples/src/examples/loaders/splat.mjs @@ -78,7 +78,8 @@ async function example({ canvas, deviceType, assetPath, scriptsPath, glslangPath cameraEntity: camera, debugRender: false, fragment: fragment, - vertex: vertex + vertex: vertex, + dither: false }); splat.setLocalPosition(px, py, pz); splat.setLocalScale(scale, scale, scale); diff --git a/extras/splat/shader-generator-splat.js b/extras/splat/shader-generator-splat.js index 8f3b9dad302..5d45b5e30df 100644 --- a/extras/splat/shader-generator-splat.js +++ b/extras/splat/shader-generator-splat.js @@ -260,6 +260,10 @@ const splatCoreFS = /* glsl_ */ ` diffuse = toneMap(diffuse); diffuse = gammaCorrectOutput(diffuse); + #ifdef DITHER + opacityDither(B); + #endif + return vec4(diffuse, B); #endif @@ -270,15 +274,19 @@ class ShaderGeneratorSplat { generateKey(options) { const vsHash = hashCode(options.vertex); const fsHash = hashCode(options.fragment); - return `splat-${options.pass}-${options.gamma}-${options.toneMapping}-${vsHash}-${fsHash}-${options.debugRender}`; + return `splat-${options.pass}-${options.gamma}-${options.toneMapping}-${vsHash}-${fsHash}-${options.debugRender}-${options.dither}}`; } createShaderDefinition(device, options) { - const defines = (options.debugRender ? '#define DEBUG_RENDER\n' : '') + - (device.isWebGL1 ? '' : '#define INT_INDICES\n'); + const defines = + (options.debugRender ? '#define DEBUG_RENDER\n' : '') + + (device.isWebGL1 ? '' : '#define INT_INDICES\n') + + (options.dither ? '#define DITHER\n' : ''); + const vs = defines + splatCoreVS + options.vertex; const fs = defines + shaderChunks.decodePS + + (options.dither ? shaderChunks.bayerPS + shaderChunks.opacityDitherPS : '') + ShaderGenerator.tonemapCode(options.toneMapping) + ShaderGenerator.gammaCode(options.gamma) + splatCoreFS + options.fragment; diff --git a/extras/splat/splat-instance.js b/extras/splat/splat-instance.js index eb63580801c..0991bd42a90 100644 --- a/extras/splat/splat-instance.js +++ b/extras/splat/splat-instance.js @@ -96,15 +96,17 @@ class SplatInstance { // clone centers to allow multiple instancing of sorter this.centers = new Float32Array(splat.centers); - this.sorter = new SplatSorter(); - this.sorter.init(this.vb, this.centers, !this.splat.device.isWebGL1); - - // if camera entity is provided, automatically use it to sort splats - const cameraEntity = options.cameraEntity; - if (cameraEntity) { - this.callbackHandle = cameraEntity._app.on('prerender', () => { - this.sort(cameraEntity); - }); + if (!options.dither) { + this.sorter = new SplatSorter(); + this.sorter.init(this.vb, this.centers, !this.splat.device.isWebGL1); + + // if camera entity is provided, automatically use it to sort splats + const cameraEntity = options.cameraEntity; + if (cameraEntity) { + this.callbackHandle = cameraEntity._app.on('prerender', () => { + this.sort(cameraEntity); + }); + } } this.updateViewport(); @@ -114,7 +116,7 @@ class SplatInstance { this.material.destroy(); this.vb.destroy(); this.meshInstance.destroy(); - this.sorter.destroy(); + this.sorter?.destroy(); this.callbackHandle?.off(); } diff --git a/extras/splat/splat-material.js b/extras/splat/splat-material.js index be3c3ccccf9..db5d9cd891e 100644 --- a/extras/splat/splat-material.js +++ b/extras/splat/splat-material.js @@ -1,5 +1,5 @@ import { - BLEND_NORMAL, + BLEND_NORMAL, BLEND_NONE, ShaderProcessorOptions, getProgramLibrary, CULLFACE_BACK, @@ -34,6 +34,7 @@ const splatMainFS = ` * @property {boolean} [debugRender] - Adds #define DEBUG_RENDER for shader. * @property {string} [vertex] - Custom vertex shader, see SPLAT MANY example. * @property {string} [fragment] - Custom fragment shader, see SPLAT MANY example. + * @property {boolean} [dither] - True if opacity dithering should be used instead of opacity. */ /** @@ -44,13 +45,13 @@ const splatMainFS = ` */ const createSplatMaterial = (options = {}) => { - const debugRender = options.debugRender; + const { debugRender, dither } = options; const material = new Material(); material.name = 'splatMaterial'; material.cull = debugRender ? CULLFACE_BACK : CULLFACE_NONE; - material.blendType = BLEND_NORMAL; - material.depthWrite = false; + material.blendType = dither ? BLEND_NONE : BLEND_NORMAL; + material.depthWrite = dither; material.getShaderVariant = function (device, scene, defs, unused, pass, sortedLights, viewUniformFormat, viewBindGroupFormat) { @@ -60,7 +61,8 @@ const createSplatMaterial = (options = {}) => { toneMapping: (pass === SHADER_FORWARDHDR ? TONEMAP_LINEAR : scene.toneMapping), vertex: options.vertex ?? splatMainVS, fragment: options.fragment ?? splatMainFS, - debugRender: debugRender + debugRender: debugRender, + dither: !!dither }; const processingOptions = new ShaderProcessorOptions(viewUniformFormat, viewBindGroupFormat);