diff --git a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js index 3dd9e206203..d5e0da3e8ec 100644 --- a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js +++ b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js @@ -46,16 +46,16 @@ import gammaPS from './common/frag/gamma.js'; import glossPS from './standard/frag/gloss.js'; // import gsplatCenterVS from './gsplat/vert/gsplatCenter.js'; // import gsplatColorVS from './gsplat/vert/gsplatColor.js'; -// import gsplatCommonVS from './gsplat/vert/gsplatCommon.js'; +import gsplatCommonVS from './gsplat/vert/gsplatCommon.js'; // import gsplatCompressedDataVS from './gsplat/vert/gsplatCompressedData.js'; // import gsplatCompressedSHVS from './gsplat/vert/gsplatCompressedSH.js'; // import gsplatCornerVS from './gsplat/vert/gsplatCorner.js'; // import gsplatDataVS from './gsplat/vert/gsplatData.js'; // import gsplatOutputVS from './gsplat/vert/gsplatOutput.js'; -// import gsplatPS from './gsplat/frag/gsplat.js'; +import gsplatPS from './gsplat/frag/gsplat.js'; // import gsplatSHVS from './gsplat/vert/gsplatSH.js'; // import gsplatSourceVS from './gsplat/vert/gsplatSource.js'; -// import gsplatVS from './gsplat/vert/gsplat.js'; +import gsplatVS from './gsplat/vert/gsplat.js'; import immediateLinePS from './internal/frag/immediateLine.js'; import immediateLineVS from './internal/vert/immediateLine.js'; import iridescenceDiffractionPS from './lit/frag/iridescenceDiffraction.js'; @@ -262,15 +262,15 @@ const shaderChunksWGSL = { // gsplatCenterVS, // gsplatCornerVS, // gsplatColorVS, - // gsplatCommonVS, + gsplatCommonVS, // gsplatCompressedDataVS, // gsplatCompressedSHVS, // gsplatDataVS, // gsplatOutputVS, - // gsplatPS, + gsplatPS, // gsplatSHVS, // gsplatSourceVS, - // gsplatVS, + gsplatVS, immediateLinePS, immediateLineVS, iridescenceDiffractionPS, diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/frag/gsplat.js b/src/scene/shader-lib/chunks-wgsl/gsplat/frag/gsplat.js new file mode 100644 index 00000000000..ef18310f20e --- /dev/null +++ b/src/scene/shader-lib/chunks-wgsl/gsplat/frag/gsplat.js @@ -0,0 +1,72 @@ +export default /* wgsl */` + +#ifndef DITHER_NONE + #include "bayerPS" + #include "opacityDitherPS" + varying id: f32; +#endif + +#ifdef PICK_PASS + #include "pickPS" +#endif + +#if defined(SHADOW_PASS) || defined(PICK_PASS) || defined(PREPASS_PASS) + uniform alphaClip: f32; +#endif + +#ifdef PREPASS_PASS + varying vLinearDepth: f32; + #include "floatAsUintPS" +#endif + +varying gaussianUV: vec2f; +varying gaussianColor: vec4f; + +@fragment +fn fragmentMain(input: FragmentInput) -> FragmentOutput { + var output: FragmentOutput; + + let A: f32 = dot(gaussianUV, gaussianUV); + if (A > 1.0) { + discard; + return output; + } + + // evaluate alpha + var alpha: f32 = exp(-A * 4.0) * gaussianColor.a; + + #if defined(SHADOW_PASS) || defined(PICK_PASS) || defined(PREPASS_PASS) + if (alpha < uniform.alphaClip) { + discard; + return output; + } + #endif + + #ifdef PICK_PASS + + output.color = getPickOutput(); + + #elif SHADOW_PASS + + output.color = vec4f(0.0, 0.0, 0.0, 1.0); + + #elif PREPASS_PASS + + output.color = float2vec4(vLinearDepth); + + #else + + if (alpha < (1.0 / 255.0)) { + discard; + return output; + } + + #ifndef DITHER_NONE + opacityDither(&alpha, id * 0.013); + #endif + + output.color = vec4f(input.gaussianColor.xyz * alpha, alpha); + #endif + + return output; +}`; diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplat.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplat.js new file mode 100644 index 00000000000..133681931ba --- /dev/null +++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplat.js @@ -0,0 +1,76 @@ +export default /* wgsl */` +#include "gsplatCommonVS" + +varying gaussianUV: vec2f; +varying gaussianColor: vec4f; + +#ifndef DITHER_NONE + varying id: f32; +#endif + +const discardVec: vec4f = vec4f(0.0, 0.0, 2.0, 1.0); + +#ifdef PREPASS_PASS + varying vLinearDepth: f32; +#endif + +@vertex +fn vertexMain(input: VertexInput) -> VertexOutput { + var output: VertexOutput; + + // read gaussian details + var source: SplatSource; + if (!initSource(&source)) { + output.position = discardVec; + return output; + } + + let modelCenter: vec3f = readCenter(&source); + + var center: SplatCenter; + if (!initCenter(modelCenter, ¢er)) { + output.position = discardVec; + return output; + } + + // project center to screen space + var corner: SplatCorner; + if (!initCorner(&source, ¢er, &corner)) { + output.position = discardVec; + return output; + } + + // read color + var clr: vec4f = readColor(&source); + + #if GSPLAT_AA + // apply AA compensation + clr.a = clr.a * corner.aaFactor; + #endif + + // evaluate spherical harmonics + #if SH_BANDS > 0 + // calculate the model-space view direction + let modelView3x3 = mat3x3f(center.modelView[0].xyz, center.modelView[1].xyz, center.modelView[2].xyz); + let dir = normalize(modelView3x3 * center.view); + clr = vec4f(clr.xyz + evalSH(&source, dir), clr.a); + #endif + + clipCorner(&corner, clr.w); + + // write output + output.position = center.proj + vec4f(corner.offset, 0.0, 0.0); + output.gaussianUV = corner.uv; + output.gaussianColor = vec4f(prepareOutputFromGamma(max(clr.xyz, vec3f(0.0))), clr.w); + + #ifndef DITHER_NONE + output.id = f32(source.id); + #endif + + #ifdef PREPASS_PASS + output.vLinearDepth = -center.view.z; + #endif + + return output; +} +`; diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js new file mode 100644 index 00000000000..d580949570d --- /dev/null +++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js @@ -0,0 +1,133 @@ +export default /* wgsl */` + +// stores the source UV and order of the splat +struct SplatSource { + order: u32, // render order + id: u32, // splat id + uv: vec2, // splat uv + cornerUV: vec2f, // corner coordinates for this vertex of the gaussian (-1, -1)..(1, 1) +} + +// stores the camera and clip space position of the gaussian center +struct SplatCenter { + view: vec3f, // center in view space + proj: vec4f, // center in clip space + modelView: mat4x4f, // model-view matrix + projMat00: f32, // elememt [0][0] of the projection matrix +} + +// stores the offset from center for the current gaussian +struct SplatCorner { + offset: vec2f, // corner offset from center in clip space + uv: vec2f, // corner uv + #if GSPLAT_AA + aaFactor: f32, // for scenes generated with antialiasing + #endif +} + +#if GSPLAT_COMPRESSED_DATA == true + #include "gsplatCompressedDataVS" + #include "gsplatCompressedSHVS" +#elif GSPLAT_SOGS_DATA == true + #include "gsplatSogsDataVS" + #include "gsplatSogsColorVS" + #include "gsplatSogsSHVS" +#else + #include "gsplatDataVS" + #include "gsplatColorVS" + #include "gsplatSHVS" +#endif + +#include "gsplatSourceVS" +#include "gsplatCenterVS" +#include "gsplatCornerVS" +#include "gsplatOutputVS" + +// modify the gaussian corner so it excludes gaussian regions with alpha less than 1/255 +fn clipCorner(corner: ptr, alpha: f32) { + let clip: f32 = min(1.0, sqrt(-log(1.0 / (255.0 * alpha))) / 2.0); + corner.offset = corner.offset * clip; + corner.uv = corner.uv * clip; +} + +// spherical Harmonics + +#if SH_BANDS > 0 + const SH_C1: f32 = 0.4886025119029199; + + #if SH_BANDS > 1 + const SH_C2_0: f32 = 1.0925484305920792; + const SH_C2_1: f32 = -1.0925484305920792; + const SH_C2_2: f32 = 0.31539156525252005; + const SH_C2_3: f32 = -1.0925484305920792; + const SH_C2_4: f32 = 0.5462742152960396; + #endif + + #if SH_BANDS > 2 + const SH_C3_0: f32 = -0.5900435899266435; + const SH_C3_1: f32 = 2.890611442640554; + const SH_C3_2: f32 = -0.4570457994644658; + const SH_C3_3: f32 = 0.3731763325901154; + const SH_C3_4: f32 = -0.4570457994644658; + const SH_C3_5: f32 = 1.445305721320277; + const SH_C3_6: f32 = -0.5900435899266435; + #endif + + // see https://github.com/graphdeco-inria/gaussian-splatting/blob/main/utils/sh_utils.py + fn evalSH(source: ptr, dir: vec3f) -> vec3f { + + #if SH_BANDS > 0 + #if SH_BANDS == 1 + var sh: array; + #elif SH_BANDS == 2 + var sh: array; + #elif SH_BANDS == 3 + var sh: array; + #endif + #endif + + var scale: f32; + readSHData(source, &sh, &scale); + + let x = dir.x; + let y = dir.y; + let z = dir.z; + + // 1st degree + var result = SH_C1 * (-sh[0] * y + sh[1] * z - sh[2] * x); + + #if SH_BANDS > 1 + // 2nd degree + let xx = x * x; + let yy = y * y; + let zz = z * z; + let xy = x * y; + let yz = y * z; + let xz = x * z; + + result = result + ( + sh[3] * (SH_C2_0 * xy) + + sh[4] * (SH_C2_1 * yz) + + sh[5] * (SH_C2_2 * (2.0 * zz - xx - yy)) + + sh[6] * (SH_C2_3 * xz) + + sh[7] * (SH_C2_4 * (xx - yy)) + ); + #endif + + #if SH_BANDS > 2 + // 3rd degree + result = result + ( + sh[8] * (SH_C3_0 * y * (3.0 * xx - yy)) + + sh[9] * (SH_C3_1 * xy * z) + + sh[10] * (SH_C3_2 * y * (4.0 * zz - xx - yy)) + + sh[11] * (SH_C3_3 * z * (2.0 * zz - 3.0 * xx - 3.0 * yy)) + + sh[12] * (SH_C3_4 * x * (4.0 * zz - xx - yy)) + + sh[13] * (SH_C3_5 * z * (xx - yy)) + + sh[14] * (SH_C3_6 * x * (xx - 3.0 * yy)) + ); + #endif + + return result * scale; + } +#endif +`; diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js b/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js index 3c0804d5a48..1a7322693d8 100644 --- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js +++ b/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js @@ -25,16 +25,6 @@ struct SplatCorner { #endif }; -#if SH_BANDS > 0 - #if SH_BANDS == 1 - #define SH_COEFFS 3 - #elif SH_BANDS == 2 - #define SH_COEFFS 8 - #elif SH_BANDS == 3 - #define SH_COEFFS 15 - #endif -#endif - #if GSPLAT_COMPRESSED_DATA == true #include "gsplatCompressedDataVS" #include "gsplatCompressedSHVS" @@ -53,8 +43,7 @@ struct SplatCorner { #include "gsplatCornerVS" #include "gsplatOutputVS" -// modify the gaussian corner so it excludes gaussian regions with alpha -// less than 1/255 +// modify the gaussian corner so it excludes gaussian regions with alpha less than 1/255 void clipCorner(inout SplatCorner corner, float alpha) { float clip = min(1.0, sqrt(-log(1.0 / 255.0 / alpha)) / 2.0); corner.offset *= clip; @@ -65,71 +54,80 @@ void clipCorner(inout SplatCorner corner, float alpha) { #if SH_BANDS > 0 -#define SH_C1 0.4886025119029199f + #define SH_C1 0.4886025119029199f -#if SH_BANDS > 1 - #define SH_C2_0 1.0925484305920792f - #define SH_C2_1 -1.0925484305920792f - #define SH_C2_2 0.31539156525252005f - #define SH_C2_3 -1.0925484305920792f - #define SH_C2_4 0.5462742152960396f -#endif - -#if SH_BANDS > 2 - #define SH_C3_0 -0.5900435899266435f - #define SH_C3_1 2.890611442640554f - #define SH_C3_2 -0.4570457994644658f - #define SH_C3_3 0.3731763325901154f - #define SH_C3_4 -0.4570457994644658f - #define SH_C3_5 1.445305721320277f - #define SH_C3_6 -0.5900435899266435f -#endif - -// see https://github.com/graphdeco-inria/gaussian-splatting/blob/main/utils/sh_utils.py -vec3 evalSH(in SplatSource source, in vec3 dir) { - - // read sh coefficients - vec3 sh[SH_COEFFS]; - float scale; - readSHData(source, sh, scale); - - float x = dir.x; - float y = dir.y; - float z = dir.z; - - // 1st degree - vec3 result = SH_C1 * (-sh[0] * y + sh[1] * z - sh[2] * x); - -#if SH_BANDS > 1 - // 2nd degree - float xx = x * x; - float yy = y * y; - float zz = z * z; - float xy = x * y; - float yz = y * z; - float xz = x * z; - - result += - sh[3] * (SH_C2_0 * xy) * + - sh[4] * (SH_C2_1 * yz) + - sh[5] * (SH_C2_2 * (2.0 * zz - xx - yy)) + - sh[6] * (SH_C2_3 * xz) + - sh[7] * (SH_C2_4 * (xx - yy)); -#endif + #if SH_BANDS > 1 + #define SH_C2_0 1.0925484305920792f + #define SH_C2_1 -1.0925484305920792f + #define SH_C2_2 0.31539156525252005f + #define SH_C2_3 -1.0925484305920792f + #define SH_C2_4 0.5462742152960396f + #endif -#if SH_BANDS > 2 - // 3rd degree - result += - sh[8] * (SH_C3_0 * y * (3.0 * xx - yy)) + - sh[9] * (SH_C3_1 * xy * z) + - sh[10] * (SH_C3_2 * y * (4.0 * zz - xx - yy)) + - sh[11] * (SH_C3_3 * z * (2.0 * zz - 3.0 * xx - 3.0 * yy)) + - sh[12] * (SH_C3_4 * x * (4.0 * zz - xx - yy)) + - sh[13] * (SH_C3_5 * z * (xx - yy)) + - sh[14] * (SH_C3_6 * x * (xx - 3.0 * yy)); -#endif + #if SH_BANDS > 2 + #define SH_C3_0 -0.5900435899266435f + #define SH_C3_1 2.890611442640554f + #define SH_C3_2 -0.4570457994644658f + #define SH_C3_3 0.3731763325901154f + #define SH_C3_4 -0.4570457994644658f + #define SH_C3_5 1.445305721320277f + #define SH_C3_6 -0.5900435899266435f + #endif - return result * scale; -} + // see https://github.com/graphdeco-inria/gaussian-splatting/blob/main/utils/sh_utils.py + vec3 evalSH(in SplatSource source, in vec3 dir) { + + #if SH_BANDS > 0 + #if SH_BANDS == 1 + vec3 sh[3]; + #elif SH_BANDS == 2 + vec3 sh[8]; + #elif SH_BANDS == 3 + vec3 sh[15]; + #endif + #endif + + // read sh coefficients + float scale; + readSHData(source, sh, scale); + + float x = dir.x; + float y = dir.y; + float z = dir.z; + + // 1st degree + vec3 result = SH_C1 * (-sh[0] * y + sh[1] * z - sh[2] * x); + + #if SH_BANDS > 1 + // 2nd degree + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float xz = x * z; + + result += + sh[3] * (SH_C2_0 * xy) + + sh[4] * (SH_C2_1 * yz) + + sh[5] * (SH_C2_2 * (2.0 * zz - xx - yy)) + + sh[6] * (SH_C2_3 * xz) + + sh[7] * (SH_C2_4 * (xx - yy)); + #endif + + #if SH_BANDS > 2 + // 3rd degree + result += + sh[8] * (SH_C3_0 * y * (3.0 * xx - yy)) + + sh[9] * (SH_C3_1 * xy * z) + + sh[10] * (SH_C3_2 * y * (4.0 * zz - xx - yy)) + + sh[11] * (SH_C3_3 * z * (2.0 * zz - 3.0 * xx - 3.0 * yy)) + + sh[12] * (SH_C3_4 * x * (4.0 * zz - xx - yy)) + + sh[13] * (SH_C3_5 * z * (xx - yy)) + + sh[14] * (SH_C3_6 * x * (xx - 3.0 * yy)); + #endif + + return result * scale; + } #endif `;