@babylonjs/loaders 9.12.0 → 9.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FBX/fbxFileLoader.d.ts +194 -0
- package/FBX/fbxFileLoader.js +2440 -0
- package/FBX/fbxFileLoader.js.map +1 -0
- package/FBX/fbxFileLoader.metadata.d.ts +11 -0
- package/FBX/fbxFileLoader.metadata.js +11 -0
- package/FBX/fbxFileLoader.metadata.js.map +1 -0
- package/FBX/index.d.ts +3 -0
- package/FBX/index.js +3 -0
- package/FBX/index.js.map +1 -0
- package/FBX/interpreter/animation.d.ts +122 -0
- package/FBX/interpreter/animation.js +648 -0
- package/FBX/interpreter/animation.js.map +1 -0
- package/FBX/interpreter/blendShapes.d.ts +44 -0
- package/FBX/interpreter/blendShapes.js +192 -0
- package/FBX/interpreter/blendShapes.js.map +1 -0
- package/FBX/interpreter/connections.d.ts +95 -0
- package/FBX/interpreter/connections.js +233 -0
- package/FBX/interpreter/connections.js.map +1 -0
- package/FBX/interpreter/fbxInterpreter.d.ts +149 -0
- package/FBX/interpreter/fbxInterpreter.js +496 -0
- package/FBX/interpreter/fbxInterpreter.js.map +1 -0
- package/FBX/interpreter/geometry.d.ts +55 -0
- package/FBX/interpreter/geometry.js +573 -0
- package/FBX/interpreter/geometry.js.map +1 -0
- package/FBX/interpreter/materials.d.ts +50 -0
- package/FBX/interpreter/materials.js +144 -0
- package/FBX/interpreter/materials.js.map +1 -0
- package/FBX/interpreter/propertyTemplates.d.ts +22 -0
- package/FBX/interpreter/propertyTemplates.js +125 -0
- package/FBX/interpreter/propertyTemplates.js.map +1 -0
- package/FBX/interpreter/rig.d.ts +20 -0
- package/FBX/interpreter/rig.js +259 -0
- package/FBX/interpreter/rig.js.map +1 -0
- package/FBX/interpreter/sceneDiagnostics.d.ts +14 -0
- package/FBX/interpreter/sceneDiagnostics.js +55 -0
- package/FBX/interpreter/sceneDiagnostics.js.map +1 -0
- package/FBX/interpreter/skeleton.d.ts +93 -0
- package/FBX/interpreter/skeleton.js +515 -0
- package/FBX/interpreter/skeleton.js.map +1 -0
- package/FBX/interpreter/transform.d.ts +21 -0
- package/FBX/interpreter/transform.js +92 -0
- package/FBX/interpreter/transform.js.map +1 -0
- package/FBX/parsers/fbxAsciiParser.d.ts +5 -0
- package/FBX/parsers/fbxAsciiParser.js +330 -0
- package/FBX/parsers/fbxAsciiParser.js.map +1 -0
- package/FBX/parsers/fbxBinaryParser.d.ts +6 -0
- package/FBX/parsers/fbxBinaryParser.js +255 -0
- package/FBX/parsers/fbxBinaryParser.js.map +1 -0
- package/FBX/parsers/zlibInflate.d.ts +7 -0
- package/FBX/parsers/zlibInflate.js +350 -0
- package/FBX/parsers/zlibInflate.js.map +1 -0
- package/FBX/types/fbxTypes.d.ts +54 -0
- package/FBX/types/fbxTypes.js +66 -0
- package/FBX/types/fbxTypes.js.map +1 -0
- package/SPLAT/gaussianSplattingStream.d.ts +404 -0
- package/SPLAT/gaussianSplattingStream.js +1159 -0
- package/SPLAT/gaussianSplattingStream.js.map +1 -0
- package/SPLAT/gaussianSplattingWorkBuffer.d.ts +71 -0
- package/SPLAT/gaussianSplattingWorkBuffer.js +249 -0
- package/SPLAT/gaussianSplattingWorkBuffer.js.map +1 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.d.ts +25 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.js +255 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.js.map +1 -0
- package/SPLAT/index.d.ts +1 -0
- package/SPLAT/index.js +1 -0
- package/SPLAT/index.js.map +1 -1
- package/SPLAT/sog.d.ts +6 -1
- package/SPLAT/sog.js +111 -25
- package/SPLAT/sog.js.map +1 -1
- package/SPLAT/splatFileLoader.d.ts +8 -0
- package/SPLAT/splatFileLoader.js +49 -0
- package/SPLAT/splatFileLoader.js.map +1 -1
- package/dynamic.js +9 -0
- package/dynamic.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** This file must only contain pure code and pure imports */
|
|
2
|
+
/**
|
|
3
|
+
* Shared shader names for the SOG -> decoded work-buffer copy pass.
|
|
4
|
+
*/
|
|
5
|
+
export declare const GaussianSplattingWorkBufferShaderName = "gsSogDecodeToWorkBuffer";
|
|
6
|
+
/**
|
|
7
|
+
* Pass-through vertex shader (GLSL): the geometry is a fullscreen triangle already in NDC.
|
|
8
|
+
*/
|
|
9
|
+
export declare const GaussianSplattingWorkBufferVertexShaderGLSL = "precision highp float;\nattribute vec3 position;\nvoid main() {\n gl_Position = vec4(position.xy, 0.0, 1.0);\n}\n";
|
|
10
|
+
/**
|
|
11
|
+
* Fragment shader (GLSL/WebGL2): decodes one SOG source file into the decoded GS work-buffer layout,
|
|
12
|
+
* writing each splat into its allocated pixel. Mirrors the USE_SOG decode in ShadersInclude/gaussianSplatting.fx
|
|
13
|
+
* but outputs the decoded MRT (center, covA, covB, color) consumed by the standard (non-SOG) draw path.
|
|
14
|
+
*
|
|
15
|
+
* MRT layout: 0 = center (x,y,z,1), 1 = covA (Sigma00,01,02,11), 2 = covB (Sigma12,22,0,0), 3 = color (rgba).
|
|
16
|
+
*/
|
|
17
|
+
export declare const GaussianSplattingWorkBufferFragmentShaderGLSL = "precision highp float;\nprecision highp int;\n\nuniform sampler2D sogMeansLTex;\nuniform sampler2D sogMeansUTex;\nuniform sampler2D sogScalesTex;\nuniform sampler2D sogQuatsTex;\nuniform sampler2D sogSh0Tex;\nuniform sampler2D sogCodebookTex;\n\nuniform vec3 sogMeansMin;\nuniform vec3 sogMeansMax;\nuniform vec3 sogScalesMin;\nuniform vec3 sogScalesMax;\nuniform vec4 sogSh0Min;\nuniform vec4 sogSh0Max;\nuniform int uVersion;\nuniform int uOffset;\nuniform int uCount;\nuniform int uDestWidth;\nuniform int uSrcWidth;\n\nlayout(location = 0) out vec4 glFragData[4];\n\nmat3 transposeM(mat3 m) {\n return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);\n}\n\nvoid main() {\n ivec2 p = ivec2(gl_FragCoord.xy);\n int global = p.y * uDestWidth + p.x;\n if (global < uOffset || global >= uOffset + uCount) {\n discard;\n }\n int k = global - uOffset;\n ivec2 src = ivec2(k - (k / uSrcWidth) * uSrcWidth, k / uSrcWidth);\n\n vec3 mL = texelFetch(sogMeansLTex, src, 0).xyz;\n vec3 mU = texelFetch(sogMeansUTex, src, 0).xyz;\n vec3 sRaw = texelFetch(sogScalesTex, src, 0).xyz;\n vec4 qRaw = texelFetch(sogQuatsTex, src, 0);\n vec4 c0 = texelFetch(sogSh0Tex, src, 0);\n\n // Position: q16 = (u<<8)|l normalized; n = lerp(min,max,q16); pos = sign(n)*(exp(|n|)-1)\n vec3 q16 = (mU * 256.0 + mL) * (255.0 / 65535.0);\n vec3 nPos = mix(sogMeansMin, sogMeansMax, q16);\n vec3 center = sign(nPos) * (exp(abs(nPos)) - vec3(1.0));\n\n // Scale (v1: lerp+exp ; v2: codebook lookup)\n vec3 splatScale;\n if (uVersion == 2) {\n vec3 sIdx = floor(sRaw * 255.0 + 0.5);\n splatScale.x = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.x), 0), 0).r);\n splatScale.y = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.y), 0), 0).r);\n splatScale.z = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.z), 0), 0).r);\n } else {\n splatScale = exp(mix(sogScalesMin, sogScalesMax, sRaw));\n }\n\n // Quaternion (largest-omitted, mode in alpha as 252 + omitted-index)\n const float invSqrt2 = 0.70710678118;\n vec3 qabc = (qRaw.xyz - vec3(0.5)) * 2.0 * invSqrt2;\n int qMode = int(qRaw.w * 255.0 + 0.5) - 252;\n float qd = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));\n vec4 quat;\n if (qMode == 0) {\n quat = vec4(qd, qabc.x, qabc.y, qabc.z);\n } else if (qMode == 1) {\n quat = vec4(qabc.x, qd, qabc.y, qabc.z);\n } else if (qMode == 2) {\n quat = vec4(qabc.x, qabc.y, qd, qabc.z);\n } else {\n quat = vec4(qabc.x, qabc.y, qabc.z, qd);\n }\n\n float qw = quat.x, qx = quat.y, qy = quat.z, qz = quat.w;\n mat3 R = mat3(\n 1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),\n 2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),\n 2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)\n );\n mat3 S2 = mat3(\n 4.0 * splatScale.x * splatScale.x, 0.0, 0.0,\n 0.0, 4.0 * splatScale.y * splatScale.y, 0.0,\n 0.0, 0.0, 4.0 * splatScale.z * splatScale.z\n );\n mat3 Sigma = R * S2 * transposeM(R);\n\n // Color (sh0)\n const float SH_C0 = 0.28209479177387814;\n vec3 colRgb;\n float colA;\n if (uVersion == 2) {\n vec3 c3;\n c3.x = texelFetch(sogCodebookTex, ivec2(256 + int(c0.x * 255.0 + 0.5), 0), 0).r;\n c3.y = texelFetch(sogCodebookTex, ivec2(256 + int(c0.y * 255.0 + 0.5), 0), 0).r;\n c3.z = texelFetch(sogCodebookTex, ivec2(256 + int(c0.z * 255.0 + 0.5), 0), 0).r;\n colRgb = vec3(0.5) + c3 * SH_C0;\n colA = c0.w;\n } else {\n vec4 cLerp = mix(sogSh0Min, sogSh0Max, c0);\n colRgb = vec3(0.5) + cLerp.xyz * SH_C0;\n colA = 1.0 / (1.0 + exp(-cLerp.w));\n }\n\n glFragData[0] = vec4(center, 1.0);\n glFragData[1] = vec4(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);\n glFragData[2] = vec4(Sigma[1][2], Sigma[2][2], 0.0, 0.0);\n glFragData[3] = vec4(colRgb, colA);\n}\n";
|
|
18
|
+
/**
|
|
19
|
+
* Pass-through vertex shader (WGSL).
|
|
20
|
+
*/
|
|
21
|
+
export declare const GaussianSplattingWorkBufferVertexShaderWGSL = "\nattribute position : vec3<f32>;\n@vertex\nfn main(input : VertexInputs) -> FragmentInputs {\n vertexOutputs.position = vec4<f32>(input.position.xy, 0.0, 1.0);\n}\n";
|
|
22
|
+
/**
|
|
23
|
+
* Fragment shader (WGSL/WebGPU) — same decode as the GLSL variant, writing 4 MRT attachments.
|
|
24
|
+
*/
|
|
25
|
+
export declare const GaussianSplattingWorkBufferFragmentShaderWGSL = "\nvar sogMeansLTexSampler : sampler;\nvar sogMeansLTex : texture_2d<f32>;\nvar sogMeansUTexSampler : sampler;\nvar sogMeansUTex : texture_2d<f32>;\nvar sogScalesTexSampler : sampler;\nvar sogScalesTex : texture_2d<f32>;\nvar sogQuatsTexSampler : sampler;\nvar sogQuatsTex : texture_2d<f32>;\nvar sogSh0TexSampler : sampler;\nvar sogSh0Tex : texture_2d<f32>;\nvar sogCodebookTexSampler : sampler;\nvar sogCodebookTex : texture_2d<f32>;\n\nuniform sogMeansMin : vec3<f32>;\nuniform sogMeansMax : vec3<f32>;\nuniform sogScalesMin : vec3<f32>;\nuniform sogScalesMax : vec3<f32>;\nuniform sogSh0Min : vec4<f32>;\nuniform sogSh0Max : vec4<f32>;\nuniform uVersion : i32;\nuniform uOffset : i32;\nuniform uCount : i32;\nuniform uDestWidth : i32;\nuniform uSrcWidth : i32;\n\n@fragment\nfn main(input : FragmentInputs) -> FragmentOutputs {\n let p : vec2<i32> = vec2<i32>(i32(fragmentInputs.position.x), i32(fragmentInputs.position.y));\n let global : i32 = p.y * uniforms.uDestWidth + p.x;\n if (global < uniforms.uOffset || global >= uniforms.uOffset + uniforms.uCount) {\n discard;\n }\n let k : i32 = global - uniforms.uOffset;\n let src : vec2<i32> = vec2<i32>(k - (k / uniforms.uSrcWidth) * uniforms.uSrcWidth, k / uniforms.uSrcWidth);\n\n let mL : vec3<f32> = textureLoad(sogMeansLTex, src, 0).xyz;\n let mU : vec3<f32> = textureLoad(sogMeansUTex, src, 0).xyz;\n let sRaw : vec3<f32> = textureLoad(sogScalesTex, src, 0).xyz;\n let qRaw : vec4<f32> = textureLoad(sogQuatsTex, src, 0);\n let c0 : vec4<f32> = textureLoad(sogSh0Tex, src, 0);\n\n let q16 : vec3<f32> = (mU * 256.0 + mL) * (255.0 / 65535.0);\n let nPos : vec3<f32> = mix(uniforms.sogMeansMin, uniforms.sogMeansMax, q16);\n let center : vec3<f32> = sign(nPos) * (exp(abs(nPos)) - vec3<f32>(1.0));\n\n var splatScale : vec3<f32>;\n if (uniforms.uVersion == 2) {\n let sIdx : vec3<f32> = floor(sRaw * 255.0 + 0.5);\n splatScale.x = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.x), 0), 0).r);\n splatScale.y = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.y), 0), 0).r);\n splatScale.z = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.z), 0), 0).r);\n } else {\n splatScale = exp(mix(uniforms.sogScalesMin, uniforms.sogScalesMax, sRaw));\n }\n\n let invSqrt2 : f32 = 0.70710678118;\n let qabc : vec3<f32> = (qRaw.xyz - vec3<f32>(0.5)) * 2.0 * invSqrt2;\n let qMode : i32 = i32(qRaw.w * 255.0 + 0.5) - 252;\n let qd : f32 = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));\n var quat : vec4<f32>;\n if (qMode == 0) {\n quat = vec4<f32>(qd, qabc.x, qabc.y, qabc.z);\n } else if (qMode == 1) {\n quat = vec4<f32>(qabc.x, qd, qabc.y, qabc.z);\n } else if (qMode == 2) {\n quat = vec4<f32>(qabc.x, qabc.y, qd, qabc.z);\n } else {\n quat = vec4<f32>(qabc.x, qabc.y, qabc.z, qd);\n }\n\n let qw : f32 = quat.x;\n let qx : f32 = quat.y;\n let qy : f32 = quat.z;\n let qz : f32 = quat.w;\n let R : mat3x3<f32> = mat3x3<f32>(\n 1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),\n 2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),\n 2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)\n );\n let S2 : mat3x3<f32> = mat3x3<f32>(\n 4.0 * splatScale.x * splatScale.x, 0.0, 0.0,\n 0.0, 4.0 * splatScale.y * splatScale.y, 0.0,\n 0.0, 0.0, 4.0 * splatScale.z * splatScale.z\n );\n let Sigma : mat3x3<f32> = R * S2 * transpose(R);\n\n let SH_C0 : f32 = 0.28209479177387814;\n var colRgb : vec3<f32>;\n var colA : f32;\n if (uniforms.uVersion == 2) {\n var c3 : vec3<f32>;\n c3.x = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.x * 255.0 + 0.5), 0), 0).r;\n c3.y = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.y * 255.0 + 0.5), 0), 0).r;\n c3.z = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.z * 255.0 + 0.5), 0), 0).r;\n colRgb = vec3<f32>(0.5) + c3 * SH_C0;\n colA = c0.w;\n } else {\n let cLerp : vec4<f32> = mix(uniforms.sogSh0Min, uniforms.sogSh0Max, c0);\n colRgb = vec3<f32>(0.5) + cLerp.xyz * SH_C0;\n colA = 1.0 / (1.0 + exp(-cLerp.w));\n }\n\n fragmentOutputs.fragData0 = vec4<f32>(center, 1.0);\n fragmentOutputs.fragData1 = vec4<f32>(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);\n fragmentOutputs.fragData2 = vec4<f32>(Sigma[1][2], Sigma[2][2], 0.0, 0.0);\n fragmentOutputs.fragData3 = vec4<f32>(colRgb, colA);\n}\n";
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/** This file must only contain pure code and pure imports */
|
|
2
|
+
/**
|
|
3
|
+
* Shared shader names for the SOG -> decoded work-buffer copy pass.
|
|
4
|
+
*/
|
|
5
|
+
export const GaussianSplattingWorkBufferShaderName = "gsSogDecodeToWorkBuffer";
|
|
6
|
+
/**
|
|
7
|
+
* Pass-through vertex shader (GLSL): the geometry is a fullscreen triangle already in NDC.
|
|
8
|
+
*/
|
|
9
|
+
export const GaussianSplattingWorkBufferVertexShaderGLSL = `precision highp float;
|
|
10
|
+
attribute vec3 position;
|
|
11
|
+
void main() {
|
|
12
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
/**
|
|
16
|
+
* Fragment shader (GLSL/WebGL2): decodes one SOG source file into the decoded GS work-buffer layout,
|
|
17
|
+
* writing each splat into its allocated pixel. Mirrors the USE_SOG decode in ShadersInclude/gaussianSplatting.fx
|
|
18
|
+
* but outputs the decoded MRT (center, covA, covB, color) consumed by the standard (non-SOG) draw path.
|
|
19
|
+
*
|
|
20
|
+
* MRT layout: 0 = center (x,y,z,1), 1 = covA (Sigma00,01,02,11), 2 = covB (Sigma12,22,0,0), 3 = color (rgba).
|
|
21
|
+
*/
|
|
22
|
+
export const GaussianSplattingWorkBufferFragmentShaderGLSL = `precision highp float;
|
|
23
|
+
precision highp int;
|
|
24
|
+
|
|
25
|
+
uniform sampler2D sogMeansLTex;
|
|
26
|
+
uniform sampler2D sogMeansUTex;
|
|
27
|
+
uniform sampler2D sogScalesTex;
|
|
28
|
+
uniform sampler2D sogQuatsTex;
|
|
29
|
+
uniform sampler2D sogSh0Tex;
|
|
30
|
+
uniform sampler2D sogCodebookTex;
|
|
31
|
+
|
|
32
|
+
uniform vec3 sogMeansMin;
|
|
33
|
+
uniform vec3 sogMeansMax;
|
|
34
|
+
uniform vec3 sogScalesMin;
|
|
35
|
+
uniform vec3 sogScalesMax;
|
|
36
|
+
uniform vec4 sogSh0Min;
|
|
37
|
+
uniform vec4 sogSh0Max;
|
|
38
|
+
uniform int uVersion;
|
|
39
|
+
uniform int uOffset;
|
|
40
|
+
uniform int uCount;
|
|
41
|
+
uniform int uDestWidth;
|
|
42
|
+
uniform int uSrcWidth;
|
|
43
|
+
|
|
44
|
+
layout(location = 0) out vec4 glFragData[4];
|
|
45
|
+
|
|
46
|
+
mat3 transposeM(mat3 m) {
|
|
47
|
+
return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void main() {
|
|
51
|
+
ivec2 p = ivec2(gl_FragCoord.xy);
|
|
52
|
+
int global = p.y * uDestWidth + p.x;
|
|
53
|
+
if (global < uOffset || global >= uOffset + uCount) {
|
|
54
|
+
discard;
|
|
55
|
+
}
|
|
56
|
+
int k = global - uOffset;
|
|
57
|
+
ivec2 src = ivec2(k - (k / uSrcWidth) * uSrcWidth, k / uSrcWidth);
|
|
58
|
+
|
|
59
|
+
vec3 mL = texelFetch(sogMeansLTex, src, 0).xyz;
|
|
60
|
+
vec3 mU = texelFetch(sogMeansUTex, src, 0).xyz;
|
|
61
|
+
vec3 sRaw = texelFetch(sogScalesTex, src, 0).xyz;
|
|
62
|
+
vec4 qRaw = texelFetch(sogQuatsTex, src, 0);
|
|
63
|
+
vec4 c0 = texelFetch(sogSh0Tex, src, 0);
|
|
64
|
+
|
|
65
|
+
// Position: q16 = (u<<8)|l normalized; n = lerp(min,max,q16); pos = sign(n)*(exp(|n|)-1)
|
|
66
|
+
vec3 q16 = (mU * 256.0 + mL) * (255.0 / 65535.0);
|
|
67
|
+
vec3 nPos = mix(sogMeansMin, sogMeansMax, q16);
|
|
68
|
+
vec3 center = sign(nPos) * (exp(abs(nPos)) - vec3(1.0));
|
|
69
|
+
|
|
70
|
+
// Scale (v1: lerp+exp ; v2: codebook lookup)
|
|
71
|
+
vec3 splatScale;
|
|
72
|
+
if (uVersion == 2) {
|
|
73
|
+
vec3 sIdx = floor(sRaw * 255.0 + 0.5);
|
|
74
|
+
splatScale.x = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.x), 0), 0).r);
|
|
75
|
+
splatScale.y = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.y), 0), 0).r);
|
|
76
|
+
splatScale.z = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.z), 0), 0).r);
|
|
77
|
+
} else {
|
|
78
|
+
splatScale = exp(mix(sogScalesMin, sogScalesMax, sRaw));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Quaternion (largest-omitted, mode in alpha as 252 + omitted-index)
|
|
82
|
+
const float invSqrt2 = 0.70710678118;
|
|
83
|
+
vec3 qabc = (qRaw.xyz - vec3(0.5)) * 2.0 * invSqrt2;
|
|
84
|
+
int qMode = int(qRaw.w * 255.0 + 0.5) - 252;
|
|
85
|
+
float qd = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));
|
|
86
|
+
vec4 quat;
|
|
87
|
+
if (qMode == 0) {
|
|
88
|
+
quat = vec4(qd, qabc.x, qabc.y, qabc.z);
|
|
89
|
+
} else if (qMode == 1) {
|
|
90
|
+
quat = vec4(qabc.x, qd, qabc.y, qabc.z);
|
|
91
|
+
} else if (qMode == 2) {
|
|
92
|
+
quat = vec4(qabc.x, qabc.y, qd, qabc.z);
|
|
93
|
+
} else {
|
|
94
|
+
quat = vec4(qabc.x, qabc.y, qabc.z, qd);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
float qw = quat.x, qx = quat.y, qy = quat.z, qz = quat.w;
|
|
98
|
+
mat3 R = mat3(
|
|
99
|
+
1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),
|
|
100
|
+
2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),
|
|
101
|
+
2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)
|
|
102
|
+
);
|
|
103
|
+
mat3 S2 = mat3(
|
|
104
|
+
4.0 * splatScale.x * splatScale.x, 0.0, 0.0,
|
|
105
|
+
0.0, 4.0 * splatScale.y * splatScale.y, 0.0,
|
|
106
|
+
0.0, 0.0, 4.0 * splatScale.z * splatScale.z
|
|
107
|
+
);
|
|
108
|
+
mat3 Sigma = R * S2 * transposeM(R);
|
|
109
|
+
|
|
110
|
+
// Color (sh0)
|
|
111
|
+
const float SH_C0 = 0.28209479177387814;
|
|
112
|
+
vec3 colRgb;
|
|
113
|
+
float colA;
|
|
114
|
+
if (uVersion == 2) {
|
|
115
|
+
vec3 c3;
|
|
116
|
+
c3.x = texelFetch(sogCodebookTex, ivec2(256 + int(c0.x * 255.0 + 0.5), 0), 0).r;
|
|
117
|
+
c3.y = texelFetch(sogCodebookTex, ivec2(256 + int(c0.y * 255.0 + 0.5), 0), 0).r;
|
|
118
|
+
c3.z = texelFetch(sogCodebookTex, ivec2(256 + int(c0.z * 255.0 + 0.5), 0), 0).r;
|
|
119
|
+
colRgb = vec3(0.5) + c3 * SH_C0;
|
|
120
|
+
colA = c0.w;
|
|
121
|
+
} else {
|
|
122
|
+
vec4 cLerp = mix(sogSh0Min, sogSh0Max, c0);
|
|
123
|
+
colRgb = vec3(0.5) + cLerp.xyz * SH_C0;
|
|
124
|
+
colA = 1.0 / (1.0 + exp(-cLerp.w));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
glFragData[0] = vec4(center, 1.0);
|
|
128
|
+
glFragData[1] = vec4(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);
|
|
129
|
+
glFragData[2] = vec4(Sigma[1][2], Sigma[2][2], 0.0, 0.0);
|
|
130
|
+
glFragData[3] = vec4(colRgb, colA);
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
/**
|
|
134
|
+
* Pass-through vertex shader (WGSL).
|
|
135
|
+
*/
|
|
136
|
+
export const GaussianSplattingWorkBufferVertexShaderWGSL = `
|
|
137
|
+
attribute position : vec3<f32>;
|
|
138
|
+
@vertex
|
|
139
|
+
fn main(input : VertexInputs) -> FragmentInputs {
|
|
140
|
+
vertexOutputs.position = vec4<f32>(input.position.xy, 0.0, 1.0);
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
/**
|
|
144
|
+
* Fragment shader (WGSL/WebGPU) — same decode as the GLSL variant, writing 4 MRT attachments.
|
|
145
|
+
*/
|
|
146
|
+
export const GaussianSplattingWorkBufferFragmentShaderWGSL = `
|
|
147
|
+
var sogMeansLTexSampler : sampler;
|
|
148
|
+
var sogMeansLTex : texture_2d<f32>;
|
|
149
|
+
var sogMeansUTexSampler : sampler;
|
|
150
|
+
var sogMeansUTex : texture_2d<f32>;
|
|
151
|
+
var sogScalesTexSampler : sampler;
|
|
152
|
+
var sogScalesTex : texture_2d<f32>;
|
|
153
|
+
var sogQuatsTexSampler : sampler;
|
|
154
|
+
var sogQuatsTex : texture_2d<f32>;
|
|
155
|
+
var sogSh0TexSampler : sampler;
|
|
156
|
+
var sogSh0Tex : texture_2d<f32>;
|
|
157
|
+
var sogCodebookTexSampler : sampler;
|
|
158
|
+
var sogCodebookTex : texture_2d<f32>;
|
|
159
|
+
|
|
160
|
+
uniform sogMeansMin : vec3<f32>;
|
|
161
|
+
uniform sogMeansMax : vec3<f32>;
|
|
162
|
+
uniform sogScalesMin : vec3<f32>;
|
|
163
|
+
uniform sogScalesMax : vec3<f32>;
|
|
164
|
+
uniform sogSh0Min : vec4<f32>;
|
|
165
|
+
uniform sogSh0Max : vec4<f32>;
|
|
166
|
+
uniform uVersion : i32;
|
|
167
|
+
uniform uOffset : i32;
|
|
168
|
+
uniform uCount : i32;
|
|
169
|
+
uniform uDestWidth : i32;
|
|
170
|
+
uniform uSrcWidth : i32;
|
|
171
|
+
|
|
172
|
+
@fragment
|
|
173
|
+
fn main(input : FragmentInputs) -> FragmentOutputs {
|
|
174
|
+
let p : vec2<i32> = vec2<i32>(i32(fragmentInputs.position.x), i32(fragmentInputs.position.y));
|
|
175
|
+
let global : i32 = p.y * uniforms.uDestWidth + p.x;
|
|
176
|
+
if (global < uniforms.uOffset || global >= uniforms.uOffset + uniforms.uCount) {
|
|
177
|
+
discard;
|
|
178
|
+
}
|
|
179
|
+
let k : i32 = global - uniforms.uOffset;
|
|
180
|
+
let src : vec2<i32> = vec2<i32>(k - (k / uniforms.uSrcWidth) * uniforms.uSrcWidth, k / uniforms.uSrcWidth);
|
|
181
|
+
|
|
182
|
+
let mL : vec3<f32> = textureLoad(sogMeansLTex, src, 0).xyz;
|
|
183
|
+
let mU : vec3<f32> = textureLoad(sogMeansUTex, src, 0).xyz;
|
|
184
|
+
let sRaw : vec3<f32> = textureLoad(sogScalesTex, src, 0).xyz;
|
|
185
|
+
let qRaw : vec4<f32> = textureLoad(sogQuatsTex, src, 0);
|
|
186
|
+
let c0 : vec4<f32> = textureLoad(sogSh0Tex, src, 0);
|
|
187
|
+
|
|
188
|
+
let q16 : vec3<f32> = (mU * 256.0 + mL) * (255.0 / 65535.0);
|
|
189
|
+
let nPos : vec3<f32> = mix(uniforms.sogMeansMin, uniforms.sogMeansMax, q16);
|
|
190
|
+
let center : vec3<f32> = sign(nPos) * (exp(abs(nPos)) - vec3<f32>(1.0));
|
|
191
|
+
|
|
192
|
+
var splatScale : vec3<f32>;
|
|
193
|
+
if (uniforms.uVersion == 2) {
|
|
194
|
+
let sIdx : vec3<f32> = floor(sRaw * 255.0 + 0.5);
|
|
195
|
+
splatScale.x = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.x), 0), 0).r);
|
|
196
|
+
splatScale.y = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.y), 0), 0).r);
|
|
197
|
+
splatScale.z = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.z), 0), 0).r);
|
|
198
|
+
} else {
|
|
199
|
+
splatScale = exp(mix(uniforms.sogScalesMin, uniforms.sogScalesMax, sRaw));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let invSqrt2 : f32 = 0.70710678118;
|
|
203
|
+
let qabc : vec3<f32> = (qRaw.xyz - vec3<f32>(0.5)) * 2.0 * invSqrt2;
|
|
204
|
+
let qMode : i32 = i32(qRaw.w * 255.0 + 0.5) - 252;
|
|
205
|
+
let qd : f32 = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));
|
|
206
|
+
var quat : vec4<f32>;
|
|
207
|
+
if (qMode == 0) {
|
|
208
|
+
quat = vec4<f32>(qd, qabc.x, qabc.y, qabc.z);
|
|
209
|
+
} else if (qMode == 1) {
|
|
210
|
+
quat = vec4<f32>(qabc.x, qd, qabc.y, qabc.z);
|
|
211
|
+
} else if (qMode == 2) {
|
|
212
|
+
quat = vec4<f32>(qabc.x, qabc.y, qd, qabc.z);
|
|
213
|
+
} else {
|
|
214
|
+
quat = vec4<f32>(qabc.x, qabc.y, qabc.z, qd);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let qw : f32 = quat.x;
|
|
218
|
+
let qx : f32 = quat.y;
|
|
219
|
+
let qy : f32 = quat.z;
|
|
220
|
+
let qz : f32 = quat.w;
|
|
221
|
+
let R : mat3x3<f32> = mat3x3<f32>(
|
|
222
|
+
1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),
|
|
223
|
+
2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),
|
|
224
|
+
2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)
|
|
225
|
+
);
|
|
226
|
+
let S2 : mat3x3<f32> = mat3x3<f32>(
|
|
227
|
+
4.0 * splatScale.x * splatScale.x, 0.0, 0.0,
|
|
228
|
+
0.0, 4.0 * splatScale.y * splatScale.y, 0.0,
|
|
229
|
+
0.0, 0.0, 4.0 * splatScale.z * splatScale.z
|
|
230
|
+
);
|
|
231
|
+
let Sigma : mat3x3<f32> = R * S2 * transpose(R);
|
|
232
|
+
|
|
233
|
+
let SH_C0 : f32 = 0.28209479177387814;
|
|
234
|
+
var colRgb : vec3<f32>;
|
|
235
|
+
var colA : f32;
|
|
236
|
+
if (uniforms.uVersion == 2) {
|
|
237
|
+
var c3 : vec3<f32>;
|
|
238
|
+
c3.x = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.x * 255.0 + 0.5), 0), 0).r;
|
|
239
|
+
c3.y = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.y * 255.0 + 0.5), 0), 0).r;
|
|
240
|
+
c3.z = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.z * 255.0 + 0.5), 0), 0).r;
|
|
241
|
+
colRgb = vec3<f32>(0.5) + c3 * SH_C0;
|
|
242
|
+
colA = c0.w;
|
|
243
|
+
} else {
|
|
244
|
+
let cLerp : vec4<f32> = mix(uniforms.sogSh0Min, uniforms.sogSh0Max, c0);
|
|
245
|
+
colRgb = vec3<f32>(0.5) + cLerp.xyz * SH_C0;
|
|
246
|
+
colA = 1.0 / (1.0 + exp(-cLerp.w));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fragmentOutputs.fragData0 = vec4<f32>(center, 1.0);
|
|
250
|
+
fragmentOutputs.fragData1 = vec4<f32>(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);
|
|
251
|
+
fragmentOutputs.fragData2 = vec4<f32>(Sigma[1][2], Sigma[2][2], 0.0, 0.0);
|
|
252
|
+
fragmentOutputs.fragData3 = vec4<f32>(colRgb, colA);
|
|
253
|
+
}
|
|
254
|
+
`;
|
|
255
|
+
//# sourceMappingURL=gaussianSplattingWorkBufferShaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gaussianSplattingWorkBufferShaders.js","sourceRoot":"","sources":["../../../../dev/loaders/src/SPLAT/gaussianSplattingWorkBufferShaders.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,yBAAyB,CAAC;AAE/E;;GAEG;AACH,MAAM,CAAC,MAAM,2CAA2C,GAAG;;;;;CAK1D,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,6CAA6C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8G5D,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,2CAA2C,GAAG;;;;;;CAM1D,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,6CAA6C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4G5D,CAAC","sourcesContent":["/** This file must only contain pure code and pure imports */\r\n\r\n/**\r\n * Shared shader names for the SOG -> decoded work-buffer copy pass.\r\n */\r\nexport const GaussianSplattingWorkBufferShaderName = \"gsSogDecodeToWorkBuffer\";\r\n\r\n/**\r\n * Pass-through vertex shader (GLSL): the geometry is a fullscreen triangle already in NDC.\r\n */\r\nexport const GaussianSplattingWorkBufferVertexShaderGLSL = `precision highp float;\r\nattribute vec3 position;\r\nvoid main() {\r\n gl_Position = vec4(position.xy, 0.0, 1.0);\r\n}\r\n`;\r\n\r\n/**\r\n * Fragment shader (GLSL/WebGL2): decodes one SOG source file into the decoded GS work-buffer layout,\r\n * writing each splat into its allocated pixel. Mirrors the USE_SOG decode in ShadersInclude/gaussianSplatting.fx\r\n * but outputs the decoded MRT (center, covA, covB, color) consumed by the standard (non-SOG) draw path.\r\n *\r\n * MRT layout: 0 = center (x,y,z,1), 1 = covA (Sigma00,01,02,11), 2 = covB (Sigma12,22,0,0), 3 = color (rgba).\r\n */\r\nexport const GaussianSplattingWorkBufferFragmentShaderGLSL = `precision highp float;\r\nprecision highp int;\r\n\r\nuniform sampler2D sogMeansLTex;\r\nuniform sampler2D sogMeansUTex;\r\nuniform sampler2D sogScalesTex;\r\nuniform sampler2D sogQuatsTex;\r\nuniform sampler2D sogSh0Tex;\r\nuniform sampler2D sogCodebookTex;\r\n\r\nuniform vec3 sogMeansMin;\r\nuniform vec3 sogMeansMax;\r\nuniform vec3 sogScalesMin;\r\nuniform vec3 sogScalesMax;\r\nuniform vec4 sogSh0Min;\r\nuniform vec4 sogSh0Max;\r\nuniform int uVersion;\r\nuniform int uOffset;\r\nuniform int uCount;\r\nuniform int uDestWidth;\r\nuniform int uSrcWidth;\r\n\r\nlayout(location = 0) out vec4 glFragData[4];\r\n\r\nmat3 transposeM(mat3 m) {\r\n return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);\r\n}\r\n\r\nvoid main() {\r\n ivec2 p = ivec2(gl_FragCoord.xy);\r\n int global = p.y * uDestWidth + p.x;\r\n if (global < uOffset || global >= uOffset + uCount) {\r\n discard;\r\n }\r\n int k = global - uOffset;\r\n ivec2 src = ivec2(k - (k / uSrcWidth) * uSrcWidth, k / uSrcWidth);\r\n\r\n vec3 mL = texelFetch(sogMeansLTex, src, 0).xyz;\r\n vec3 mU = texelFetch(sogMeansUTex, src, 0).xyz;\r\n vec3 sRaw = texelFetch(sogScalesTex, src, 0).xyz;\r\n vec4 qRaw = texelFetch(sogQuatsTex, src, 0);\r\n vec4 c0 = texelFetch(sogSh0Tex, src, 0);\r\n\r\n // Position: q16 = (u<<8)|l normalized; n = lerp(min,max,q16); pos = sign(n)*(exp(|n|)-1)\r\n vec3 q16 = (mU * 256.0 + mL) * (255.0 / 65535.0);\r\n vec3 nPos = mix(sogMeansMin, sogMeansMax, q16);\r\n vec3 center = sign(nPos) * (exp(abs(nPos)) - vec3(1.0));\r\n\r\n // Scale (v1: lerp+exp ; v2: codebook lookup)\r\n vec3 splatScale;\r\n if (uVersion == 2) {\r\n vec3 sIdx = floor(sRaw * 255.0 + 0.5);\r\n splatScale.x = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.x), 0), 0).r);\r\n splatScale.y = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.y), 0), 0).r);\r\n splatScale.z = exp(texelFetch(sogCodebookTex, ivec2(int(sIdx.z), 0), 0).r);\r\n } else {\r\n splatScale = exp(mix(sogScalesMin, sogScalesMax, sRaw));\r\n }\r\n\r\n // Quaternion (largest-omitted, mode in alpha as 252 + omitted-index)\r\n const float invSqrt2 = 0.70710678118;\r\n vec3 qabc = (qRaw.xyz - vec3(0.5)) * 2.0 * invSqrt2;\r\n int qMode = int(qRaw.w * 255.0 + 0.5) - 252;\r\n float qd = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));\r\n vec4 quat;\r\n if (qMode == 0) {\r\n quat = vec4(qd, qabc.x, qabc.y, qabc.z);\r\n } else if (qMode == 1) {\r\n quat = vec4(qabc.x, qd, qabc.y, qabc.z);\r\n } else if (qMode == 2) {\r\n quat = vec4(qabc.x, qabc.y, qd, qabc.z);\r\n } else {\r\n quat = vec4(qabc.x, qabc.y, qabc.z, qd);\r\n }\r\n\r\n float qw = quat.x, qx = quat.y, qy = quat.z, qz = quat.w;\r\n mat3 R = mat3(\r\n 1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),\r\n 2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),\r\n 2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)\r\n );\r\n mat3 S2 = mat3(\r\n 4.0 * splatScale.x * splatScale.x, 0.0, 0.0,\r\n 0.0, 4.0 * splatScale.y * splatScale.y, 0.0,\r\n 0.0, 0.0, 4.0 * splatScale.z * splatScale.z\r\n );\r\n mat3 Sigma = R * S2 * transposeM(R);\r\n\r\n // Color (sh0)\r\n const float SH_C0 = 0.28209479177387814;\r\n vec3 colRgb;\r\n float colA;\r\n if (uVersion == 2) {\r\n vec3 c3;\r\n c3.x = texelFetch(sogCodebookTex, ivec2(256 + int(c0.x * 255.0 + 0.5), 0), 0).r;\r\n c3.y = texelFetch(sogCodebookTex, ivec2(256 + int(c0.y * 255.0 + 0.5), 0), 0).r;\r\n c3.z = texelFetch(sogCodebookTex, ivec2(256 + int(c0.z * 255.0 + 0.5), 0), 0).r;\r\n colRgb = vec3(0.5) + c3 * SH_C0;\r\n colA = c0.w;\r\n } else {\r\n vec4 cLerp = mix(sogSh0Min, sogSh0Max, c0);\r\n colRgb = vec3(0.5) + cLerp.xyz * SH_C0;\r\n colA = 1.0 / (1.0 + exp(-cLerp.w));\r\n }\r\n\r\n glFragData[0] = vec4(center, 1.0);\r\n glFragData[1] = vec4(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);\r\n glFragData[2] = vec4(Sigma[1][2], Sigma[2][2], 0.0, 0.0);\r\n glFragData[3] = vec4(colRgb, colA);\r\n}\r\n`;\r\n\r\n/**\r\n * Pass-through vertex shader (WGSL).\r\n */\r\nexport const GaussianSplattingWorkBufferVertexShaderWGSL = `\r\nattribute position : vec3<f32>;\r\n@vertex\r\nfn main(input : VertexInputs) -> FragmentInputs {\r\n vertexOutputs.position = vec4<f32>(input.position.xy, 0.0, 1.0);\r\n}\r\n`;\r\n\r\n/**\r\n * Fragment shader (WGSL/WebGPU) — same decode as the GLSL variant, writing 4 MRT attachments.\r\n */\r\nexport const GaussianSplattingWorkBufferFragmentShaderWGSL = `\r\nvar sogMeansLTexSampler : sampler;\r\nvar sogMeansLTex : texture_2d<f32>;\r\nvar sogMeansUTexSampler : sampler;\r\nvar sogMeansUTex : texture_2d<f32>;\r\nvar sogScalesTexSampler : sampler;\r\nvar sogScalesTex : texture_2d<f32>;\r\nvar sogQuatsTexSampler : sampler;\r\nvar sogQuatsTex : texture_2d<f32>;\r\nvar sogSh0TexSampler : sampler;\r\nvar sogSh0Tex : texture_2d<f32>;\r\nvar sogCodebookTexSampler : sampler;\r\nvar sogCodebookTex : texture_2d<f32>;\r\n\r\nuniform sogMeansMin : vec3<f32>;\r\nuniform sogMeansMax : vec3<f32>;\r\nuniform sogScalesMin : vec3<f32>;\r\nuniform sogScalesMax : vec3<f32>;\r\nuniform sogSh0Min : vec4<f32>;\r\nuniform sogSh0Max : vec4<f32>;\r\nuniform uVersion : i32;\r\nuniform uOffset : i32;\r\nuniform uCount : i32;\r\nuniform uDestWidth : i32;\r\nuniform uSrcWidth : i32;\r\n\r\n@fragment\r\nfn main(input : FragmentInputs) -> FragmentOutputs {\r\n let p : vec2<i32> = vec2<i32>(i32(fragmentInputs.position.x), i32(fragmentInputs.position.y));\r\n let global : i32 = p.y * uniforms.uDestWidth + p.x;\r\n if (global < uniforms.uOffset || global >= uniforms.uOffset + uniforms.uCount) {\r\n discard;\r\n }\r\n let k : i32 = global - uniforms.uOffset;\r\n let src : vec2<i32> = vec2<i32>(k - (k / uniforms.uSrcWidth) * uniforms.uSrcWidth, k / uniforms.uSrcWidth);\r\n\r\n let mL : vec3<f32> = textureLoad(sogMeansLTex, src, 0).xyz;\r\n let mU : vec3<f32> = textureLoad(sogMeansUTex, src, 0).xyz;\r\n let sRaw : vec3<f32> = textureLoad(sogScalesTex, src, 0).xyz;\r\n let qRaw : vec4<f32> = textureLoad(sogQuatsTex, src, 0);\r\n let c0 : vec4<f32> = textureLoad(sogSh0Tex, src, 0);\r\n\r\n let q16 : vec3<f32> = (mU * 256.0 + mL) * (255.0 / 65535.0);\r\n let nPos : vec3<f32> = mix(uniforms.sogMeansMin, uniforms.sogMeansMax, q16);\r\n let center : vec3<f32> = sign(nPos) * (exp(abs(nPos)) - vec3<f32>(1.0));\r\n\r\n var splatScale : vec3<f32>;\r\n if (uniforms.uVersion == 2) {\r\n let sIdx : vec3<f32> = floor(sRaw * 255.0 + 0.5);\r\n splatScale.x = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.x), 0), 0).r);\r\n splatScale.y = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.y), 0), 0).r);\r\n splatScale.z = exp(textureLoad(sogCodebookTex, vec2<i32>(i32(sIdx.z), 0), 0).r);\r\n } else {\r\n splatScale = exp(mix(uniforms.sogScalesMin, uniforms.sogScalesMax, sRaw));\r\n }\r\n\r\n let invSqrt2 : f32 = 0.70710678118;\r\n let qabc : vec3<f32> = (qRaw.xyz - vec3<f32>(0.5)) * 2.0 * invSqrt2;\r\n let qMode : i32 = i32(qRaw.w * 255.0 + 0.5) - 252;\r\n let qd : f32 = sqrt(max(0.0, 1.0 - dot(qabc, qabc)));\r\n var quat : vec4<f32>;\r\n if (qMode == 0) {\r\n quat = vec4<f32>(qd, qabc.x, qabc.y, qabc.z);\r\n } else if (qMode == 1) {\r\n quat = vec4<f32>(qabc.x, qd, qabc.y, qabc.z);\r\n } else if (qMode == 2) {\r\n quat = vec4<f32>(qabc.x, qabc.y, qd, qabc.z);\r\n } else {\r\n quat = vec4<f32>(qabc.x, qabc.y, qabc.z, qd);\r\n }\r\n\r\n let qw : f32 = quat.x;\r\n let qx : f32 = quat.y;\r\n let qy : f32 = quat.z;\r\n let qz : f32 = quat.w;\r\n let R : mat3x3<f32> = mat3x3<f32>(\r\n 1.0 - 2.0 * (qy * qy + qz * qz), 2.0 * (qx * qy + qw * qz), 2.0 * (qx * qz - qw * qy),\r\n 2.0 * (qx * qy - qw * qz), 1.0 - 2.0 * (qx * qx + qz * qz), 2.0 * (qy * qz + qw * qx),\r\n 2.0 * (qx * qz + qw * qy), 2.0 * (qy * qz - qw * qx), 1.0 - 2.0 * (qx * qx + qy * qy)\r\n );\r\n let S2 : mat3x3<f32> = mat3x3<f32>(\r\n 4.0 * splatScale.x * splatScale.x, 0.0, 0.0,\r\n 0.0, 4.0 * splatScale.y * splatScale.y, 0.0,\r\n 0.0, 0.0, 4.0 * splatScale.z * splatScale.z\r\n );\r\n let Sigma : mat3x3<f32> = R * S2 * transpose(R);\r\n\r\n let SH_C0 : f32 = 0.28209479177387814;\r\n var colRgb : vec3<f32>;\r\n var colA : f32;\r\n if (uniforms.uVersion == 2) {\r\n var c3 : vec3<f32>;\r\n c3.x = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.x * 255.0 + 0.5), 0), 0).r;\r\n c3.y = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.y * 255.0 + 0.5), 0), 0).r;\r\n c3.z = textureLoad(sogCodebookTex, vec2<i32>(256 + i32(c0.z * 255.0 + 0.5), 0), 0).r;\r\n colRgb = vec3<f32>(0.5) + c3 * SH_C0;\r\n colA = c0.w;\r\n } else {\r\n let cLerp : vec4<f32> = mix(uniforms.sogSh0Min, uniforms.sogSh0Max, c0);\r\n colRgb = vec3<f32>(0.5) + cLerp.xyz * SH_C0;\r\n colA = 1.0 / (1.0 + exp(-cLerp.w));\r\n }\r\n\r\n fragmentOutputs.fragData0 = vec4<f32>(center, 1.0);\r\n fragmentOutputs.fragData1 = vec4<f32>(Sigma[0][0], Sigma[0][1], Sigma[0][2], Sigma[1][1]);\r\n fragmentOutputs.fragData2 = vec4<f32>(Sigma[1][2], Sigma[2][2], 0.0, 0.0);\r\n fragmentOutputs.fragData3 = vec4<f32>(colRgb, colA);\r\n}\r\n`;\r\n"]}
|
package/SPLAT/index.d.ts
CHANGED
package/SPLAT/index.js
CHANGED
package/SPLAT/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../dev/loaders/src/SPLAT/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC","sourcesContent":["/* eslint-disable @typescript-eslint/no-restricted-imports */\r\nexport * from \"./splatLoadingOptions\";\r\nexport * from \"./splatFileLoader\";\r\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../dev/loaders/src/SPLAT/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC","sourcesContent":["/* eslint-disable @typescript-eslint/no-restricted-imports */\r\nexport * from \"./splatLoadingOptions\";\r\nexport * from \"./splatFileLoader\";\r\nexport * from \"./gaussianSplattingStream\";\r\n"]}
|
package/SPLAT/sog.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Scene } from "@babylonjs/core/scene.js";
|
|
2
2
|
import { type IParsedSplat } from "./splatDefs.js";
|
|
3
|
+
import "@babylonjs/core/Engines/Extensions/engine.dynamicTexture.js";
|
|
3
4
|
/**
|
|
4
5
|
* Definition of a SOG data file
|
|
5
6
|
*/
|
|
@@ -88,6 +89,10 @@ export declare function ParseSogMeta(dataOrFiles: SOGRootData | Map<string, Uint
|
|
|
88
89
|
* @param dataOrFiles Either the SOGRootData or a Map of filenames to Uint8Array file data (including meta.json)
|
|
89
90
|
* @param rootUrl Base URL to load webp files from (if dataOrFiles is SOGRootData)
|
|
90
91
|
* @param scene The Babylon.js scene
|
|
92
|
+
* @param computeCpuPositions When true (default), means_l/means_u are read back on the CPU and `pack.positions`
|
|
93
|
+
* is decoded for the sort worker / bounding box. Pass false when the caller will instead read the decoded
|
|
94
|
+
* centers back from the GPU work buffer — then every attribute (including means) uses the fast direct
|
|
95
|
+
* ImageBitmap upload (no `getImageData` readback) and `pack.positions` is left empty.
|
|
91
96
|
* @returns Parsed splat info with `sogTextures` populated.
|
|
92
97
|
*/
|
|
93
|
-
export declare function ParseSogMetaAsTextures(dataOrFiles: SOGRootData | Map<string, Uint8Array>, rootUrl: string, scene: Scene): Promise<IParsedSplat>;
|
|
98
|
+
export declare function ParseSogMetaAsTextures(dataOrFiles: SOGRootData | Map<string, Uint8Array>, rootUrl: string, scene: Scene, computeCpuPositions?: boolean): Promise<IParsedSplat>;
|
package/SPLAT/sog.js
CHANGED
|
@@ -2,6 +2,10 @@ import { AllocateShBuffers } from "@babylonjs/core/Meshes/GaussianSplatting/gaus
|
|
|
2
2
|
import { Scalar } from "@babylonjs/core/Maths/math.scalar.js";
|
|
3
3
|
import { RawTexture } from "@babylonjs/core/Materials/Textures/rawTexture.js";
|
|
4
4
|
import { Constants } from "@babylonjs/core/Engines/constants.js";
|
|
5
|
+
import { Tools } from "@babylonjs/core/Misc/tools.js";
|
|
6
|
+
// updateDynamicTexture is a prototype-augmented engine extension; import its side effect so the ImageBitmap
|
|
7
|
+
// fast path in LoadSogTextureDirectAsync doesn't hit an undefined method under tree-shaken/pure engine builds.
|
|
8
|
+
import "@babylonjs/core/Engines/Extensions/engine.dynamicTexture.js";
|
|
5
9
|
const SH_C0 = 0.28209479177387814;
|
|
6
10
|
async function LoadWebpImageData(rootUrlOrData, filename, engine) {
|
|
7
11
|
const promise = new Promise((resolve, reject) => {
|
|
@@ -23,7 +27,7 @@ async function LoadWebpImageData(rootUrlOrData, filename, engine) {
|
|
|
23
27
|
ctx.drawImage(image, 0, 0);
|
|
24
28
|
// Extract pixel data (RGBA per pixel)
|
|
25
29
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
26
|
-
resolve({ bits: new Uint8Array(imageData.data.buffer), width: imageData.width });
|
|
30
|
+
resolve({ bits: new Uint8Array(imageData.data.buffer), width: imageData.width, height: imageData.height });
|
|
27
31
|
}
|
|
28
32
|
catch (error) {
|
|
29
33
|
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
@@ -296,6 +300,59 @@ function CreateSogTexture(scene, bits, width, height) {
|
|
|
296
300
|
tex.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
297
301
|
return tex;
|
|
298
302
|
}
|
|
303
|
+
function CreateSogTextureFromImage(scene, image) {
|
|
304
|
+
return CreateSogTexture(scene, image.bits, image.width, image.height);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Loads a SOG attribute image straight onto a GPU texture using `createImageBitmap` (decoded off the main
|
|
308
|
+
* thread, with `premultiplyAlpha`/`colorSpaceConversion` set to "none" so the raw data bytes are preserved)
|
|
309
|
+
* and a direct `texImage2D` upload — avoiding the `<img>` + 2D-canvas `getImageData` readback that stalls the
|
|
310
|
+
* frame. Falls back to the canvas path when `createImageBitmap` is unavailable or fails. Use only for textures
|
|
311
|
+
* whose pixels are never read back on the CPU (scales, quats, sh0, shN); means_l/means_u still go through the
|
|
312
|
+
* readback path because their bytes are needed to decode positions for the sort worker.
|
|
313
|
+
* @param rootUrlOrData base URL (string) or the raw file bytes (Uint8Array)
|
|
314
|
+
* @param filename file name (used only with a URL)
|
|
315
|
+
* @param scene hosting scene
|
|
316
|
+
* @returns a GPU texture holding the raw image bytes
|
|
317
|
+
*/
|
|
318
|
+
async function LoadSogTextureDirectAsync(rootUrlOrData, filename, scene) {
|
|
319
|
+
const engine = scene.getEngine();
|
|
320
|
+
if (typeof createImageBitmap === "function") {
|
|
321
|
+
try {
|
|
322
|
+
// A typed blob is required: createImageBitmap can fail to decode a typeless blob (and the
|
|
323
|
+
// content-type is lost when loading via LoadFileAsync), which would force the slow canvas fallback.
|
|
324
|
+
const mimeType = filename.toLowerCase().endsWith(".png") ? "image/png" : "image/webp";
|
|
325
|
+
let blob;
|
|
326
|
+
if (typeof rootUrlOrData === "string") {
|
|
327
|
+
const buffer = (await Tools.LoadFileAsync(rootUrlOrData + filename, true));
|
|
328
|
+
blob = new Blob([buffer], { type: mimeType });
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
blob = new Blob([rootUrlOrData], { type: mimeType });
|
|
332
|
+
}
|
|
333
|
+
const bitmap = await createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" });
|
|
334
|
+
try {
|
|
335
|
+
const tex = new RawTexture(null, bitmap.width, bitmap.height, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
|
|
336
|
+
tex.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
337
|
+
tex.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
|
|
338
|
+
const internal = tex.getInternalTexture();
|
|
339
|
+
if (internal) {
|
|
340
|
+
// invertY=false / premulAlpha=false keep the byte layout identical to the canvas path.
|
|
341
|
+
engine.updateDynamicTexture(internal, bitmap, false, false);
|
|
342
|
+
}
|
|
343
|
+
return tex;
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
bitmap.close();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Fall through to the canvas readback path below.
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const image = await LoadWebpImageData(rootUrlOrData, filename, engine);
|
|
354
|
+
return CreateSogTextureFromImage(scene, image);
|
|
355
|
+
}
|
|
299
356
|
function DecodeSogPositions(data, meansl, meansu, splatCount) {
|
|
300
357
|
const unlog = (n) => Math.sign(n) * (Math.exp(Math.abs(n)) - 1);
|
|
301
358
|
if (!Array.isArray(data.means.mins) || !Array.isArray(data.means.maxs)) {
|
|
@@ -320,10 +377,14 @@ function DecodeSogPositions(data, meansl, meansu, splatCount) {
|
|
|
320
377
|
* @param dataOrFiles Either the SOGRootData or a Map of filenames to Uint8Array file data (including meta.json)
|
|
321
378
|
* @param rootUrl Base URL to load webp files from (if dataOrFiles is SOGRootData)
|
|
322
379
|
* @param scene The Babylon.js scene
|
|
380
|
+
* @param computeCpuPositions When true (default), means_l/means_u are read back on the CPU and `pack.positions`
|
|
381
|
+
* is decoded for the sort worker / bounding box. Pass false when the caller will instead read the decoded
|
|
382
|
+
* centers back from the GPU work buffer — then every attribute (including means) uses the fast direct
|
|
383
|
+
* ImageBitmap upload (no `getImageData` readback) and `pack.positions` is left empty.
|
|
323
384
|
* @returns Parsed splat info with `sogTextures` populated.
|
|
324
385
|
*/
|
|
325
386
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
|
326
|
-
export async function ParseSogMetaAsTextures(dataOrFiles, rootUrl, scene) {
|
|
387
|
+
export async function ParseSogMetaAsTextures(dataOrFiles, rootUrl, scene, computeCpuPositions = true) {
|
|
327
388
|
let data;
|
|
328
389
|
let files;
|
|
329
390
|
if (dataOrFiles instanceof Map) {
|
|
@@ -337,37 +398,62 @@ export async function ParseSogMetaAsTextures(dataOrFiles, rootUrl, scene) {
|
|
|
337
398
|
else {
|
|
338
399
|
data = dataOrFiles;
|
|
339
400
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const
|
|
401
|
+
// Attribute textures (scales/quats/sh0/shN) are only sampled on the GPU, so they always upload straight
|
|
402
|
+
// from a decoded ImageBitmap (no getImageData readback). means_l/means_u additionally need their CPU bytes
|
|
403
|
+
// when computeCpuPositions is set (to decode positions for the sort worker) — those go through the
|
|
404
|
+
// <img>+canvas path; otherwise means also use the fast direct upload. All loads run in parallel.
|
|
405
|
+
const loadMeansImageAsync = async (fileName) => {
|
|
345
406
|
if (files && files.has(fileName)) {
|
|
346
407
|
return await LoadWebpImageData(files.get(fileName), fileName, scene.getEngine());
|
|
347
408
|
}
|
|
348
409
|
return await LoadWebpImageData(rootUrl, fileName, scene.getEngine());
|
|
349
|
-
}
|
|
410
|
+
};
|
|
411
|
+
const loadGpuTextureAsync = async (fileName) => {
|
|
412
|
+
if (files && files.has(fileName)) {
|
|
413
|
+
return await LoadSogTextureDirectAsync(files.get(fileName), fileName, scene);
|
|
414
|
+
}
|
|
415
|
+
return await LoadSogTextureDirectAsync(rootUrl, fileName, scene);
|
|
416
|
+
};
|
|
417
|
+
const gpuFiles = [...data.scales.files, ...data.quats.files, ...data.sh0.files, ...(data.shN?.files ?? [])];
|
|
418
|
+
let meansL;
|
|
419
|
+
let meansU;
|
|
420
|
+
let meansWidth;
|
|
421
|
+
let meansHeight;
|
|
422
|
+
let meansImages = null;
|
|
423
|
+
let gpuTextures;
|
|
424
|
+
if (computeCpuPositions) {
|
|
425
|
+
const [images, gpu] = await Promise.all([Promise.all(data.means.files.map(loadMeansImageAsync)), Promise.all(gpuFiles.map(loadGpuTextureAsync))]);
|
|
426
|
+
meansImages = [images[0], images[1]];
|
|
427
|
+
gpuTextures = gpu;
|
|
428
|
+
meansL = CreateSogTextureFromImage(scene, images[0]);
|
|
429
|
+
meansU = CreateSogTextureFromImage(scene, images[1]);
|
|
430
|
+
meansWidth = images[0].width;
|
|
431
|
+
meansHeight = images[0].height;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
const [meansTex, gpu] = await Promise.all([Promise.all(data.means.files.map(loadGpuTextureAsync)), Promise.all(gpuFiles.map(loadGpuTextureAsync))]);
|
|
435
|
+
gpuTextures = gpu;
|
|
436
|
+
meansL = meansTex[0];
|
|
437
|
+
meansU = meansTex[1];
|
|
438
|
+
const size = meansL.getSize();
|
|
439
|
+
meansWidth = size.width;
|
|
440
|
+
meansHeight = size.height;
|
|
441
|
+
}
|
|
350
442
|
const splatCount = data.count ?? data.means.shape[0];
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
const quats = CreateSogTexture(scene, images[3].bits, splatTextureWidth, splatTextureHeight);
|
|
359
|
-
const sh0 = CreateSogTexture(scene, images[4].bits, splatTextureWidth, splatTextureHeight);
|
|
443
|
+
const splatTexelCount = meansWidth * meansHeight;
|
|
444
|
+
if (splatTexelCount < splatCount) {
|
|
445
|
+
throw new Error(`SOG texture contains ${splatTexelCount} texels, but metadata references ${splatCount} splats.`);
|
|
446
|
+
}
|
|
447
|
+
const scales = gpuTextures[0];
|
|
448
|
+
const quats = gpuTextures[1];
|
|
449
|
+
const sh0 = gpuTextures[2];
|
|
360
450
|
let shCentroids;
|
|
361
451
|
let shLabels;
|
|
362
452
|
let shCoeffCount = 0;
|
|
363
453
|
let shDegree = 0;
|
|
364
|
-
if (data.shN &&
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const centroidsHeight = centroidsImage.bits.length / 4 / centroidsImage.width;
|
|
368
|
-
shCentroids = CreateSogTexture(scene, centroidsImage.bits, centroidsImage.width, centroidsHeight);
|
|
369
|
-
const labelsHeight = labelsImage.bits.length / 4 / labelsImage.width;
|
|
370
|
-
shLabels = CreateSogTexture(scene, labelsImage.bits, labelsImage.width, labelsHeight);
|
|
454
|
+
if (data.shN && gpuTextures.length >= 5) {
|
|
455
|
+
shCentroids = gpuTextures[3];
|
|
456
|
+
shLabels = gpuTextures[4];
|
|
371
457
|
shCoeffCount = data.shN.bands ? (data.shN.bands + 1) ** 2 - 1 : data.shN.shape[1] / 3;
|
|
372
458
|
shDegree = data.shN.bands ?? Math.round(Math.sqrt(shCoeffCount + 1) - 1);
|
|
373
459
|
}
|
|
@@ -412,7 +498,7 @@ export async function ParseSogMetaAsTextures(dataOrFiles, rootUrl, scene) {
|
|
|
412
498
|
shnMin: typeof data.shN?.mins === "number" ? data.shN.mins : undefined,
|
|
413
499
|
shnMax: typeof data.shN?.maxs === "number" ? data.shN.maxs : undefined,
|
|
414
500
|
shCoeffCount,
|
|
415
|
-
positions: DecodeSogPositions(data,
|
|
501
|
+
positions: meansImages ? DecodeSogPositions(data, meansImages[0].bits, meansImages[1].bits, splatCount) : new Float32Array(0),
|
|
416
502
|
};
|
|
417
503
|
return { mode: 0 /* Mode.Splat */, data: new ArrayBuffer(0), hasVertexColors: false, shDegree, sogTextures: pack };
|
|
418
504
|
}
|