gaussian-splat-lite 1.1.2 → 1.1.3
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/CHANGELOG.md +9 -1
- package/dist/gaussian-splat-lite.cjs +130 -48
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +29 -27
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +130 -48
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +2087 -2008
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/dist/types/rendering/GaussianSplatRenderer.d.cts +1 -1
- package/dist/types/rendering/GaussianSplatRenderer.d.ts +1 -1
- package/dist/types/rendering/tsl/SplatBackend.d.cts +4 -1
- package/dist/types/rendering/tsl/SplatBackend.d.ts +4 -1
- package/dist/types/rendering/tsl/SplatMaterial.d.cts +4 -1
- package/dist/types/rendering/tsl/SplatMaterial.d.ts +4 -1
- package/dist/types/rendering/webgl/SplatBackend.d.cts +1 -0
- package/dist/types/rendering/webgl/SplatBackend.d.ts +1 -0
- package/dist/types/rendering/webgpu/ProjectedSplats.d.cts +1 -0
- package/dist/types/rendering/webgpu/ProjectedSplats.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.3] - 2026-09-23
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Optimized manual sorted rendering across WebGL, WebGPU, and WebGL fallback by removing unused stochastic and depth branches from the color fragment shader. Auto and stochastic modes retain uniform-based switching; custom GLSL shaders are unchanged.
|
|
15
|
+
- Native WebGPU reuses projection, visibility compaction, and sort results when source data, views, and projection settings are unchanged.
|
|
16
|
+
|
|
10
17
|
## [1.1.2] - 2026-09-21
|
|
11
18
|
|
|
12
19
|
### Added
|
|
@@ -388,7 +395,8 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
388
395
|
|
|
389
396
|
- Initial public release of the Three.js Gaussian Splatting renderer.
|
|
390
397
|
|
|
391
|
-
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.
|
|
398
|
+
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.3...HEAD
|
|
399
|
+
[1.1.3]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.2...v1.1.3
|
|
392
400
|
[1.1.2]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.1...v1.1.2
|
|
393
401
|
[1.1.1]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.0...v1.1.1
|
|
394
402
|
[1.1.0]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.0.4...v1.1.0
|
|
@@ -24456,7 +24456,7 @@ class FullScreenQuad {
|
|
|
24456
24456
|
}
|
|
24457
24457
|
}
|
|
24458
24458
|
var splatDefines_default = "const uint SPLAT_TEX_WIDTH_BITS = 11u;\nconst uint SPLAT_TEX_HEIGHT_BITS = 11u;\nconst uint SPLAT_TEX_LAYER_BITS = SPLAT_TEX_WIDTH_BITS + SPLAT_TEX_HEIGHT_BITS;\n\nconst uint SPLAT_TEX_WIDTH = 1u << SPLAT_TEX_WIDTH_BITS;\nconst uint SPLAT_TEX_HEIGHT = 1u << SPLAT_TEX_HEIGHT_BITS;\n\nconst uint SPLAT_TEX_WIDTH_MASK = SPLAT_TEX_WIDTH - 1u;\nconst uint SPLAT_TEX_HEIGHT_MASK = SPLAT_TEX_HEIGHT - 1u;\n\nconst float PI = 3.1415926535897932384626433832795;\n\nvec3 srgbToLinear(vec3 rgb) {\n \n return mix(\n pow(rgb * 0.9478672986 + vec3(0.0521327014), vec3(2.4)),\n rgb * 0.0773993808, lessThanEqual(rgb, vec3(0.04045)));\n}\n\nuint encodeQuatOctXy1010R12(vec4 q) {\n \n if (q.w < 0.0) {\n q = -q;\n }\n \n float theta = 2.0 * acos(clamp(q.w, 0.0, 1.0));\n\n \n \n \n float sum = abs(q.x) + abs(q.y) + abs(q.z);\n \n vec2 p = (sum < 1e-6) ? vec2(1.0, 0.0) : q.xy / sum;\n \n if (q.z < 0.0) {\n float oldPx = p.x;\n p.x = (1.0 - abs(p.y)) * (p.x >= 0.0 ? 1.0 : -1.0);\n p.y = (1.0 - abs(oldPx)) * (p.y >= 0.0 ? 1.0 : -1.0);\n }\n \n float u_f = p.x * 0.5 + 0.5;\n float v_f = p.y * 0.5 + 0.5;\n \n uint quantU = uint(clamp(round(u_f * 1023.0), 0.0, 1023.0));\n uint quantV = uint(clamp(round(v_f * 1023.0), 0.0, 1023.0));\n \n \n \n uint angleInt = uint(clamp(round((theta / PI) * 4095.0), 0.0, 4095.0));\n \n \n return (angleInt << 20u) | (quantV << 10u) | quantU;\n}\n\nvec4 decodeQuatOctXy1010R12(uint encoded) {\n \n uint quantU = encoded & uint(0x3FFu); \n uint quantV = (encoded >> 10u) & uint(0x3FFu); \n uint angleInt = encoded >> 20u; \n\n \n float u_f = float(quantU) / 1023.0;\n float v_f = float(quantV) / 1023.0;\n vec2 f = vec2(u_f * 2.0 - 1.0, v_f * 2.0 - 1.0);\n\n vec3 axis = vec3(f.xy, 1.0 - abs(f.x) - abs(f.y));\n float t = max(-axis.z, 0.0);\n axis.x += (axis.x >= 0.0) ? -t : t;\n axis.y += (axis.y >= 0.0) ? -t : t;\n axis = normalize(axis);\n \n \n float theta = (float(angleInt) / 4095.0) * PI;\n float halfTheta = theta * 0.5;\n float s = sin(halfTheta);\n float w = cos(halfTheta);\n \n return vec4(axis * s, w);\n}\n\nvec3 decodeSplatCenter(uvec4 splatData) {\n return vec3(\n uintBitsToFloat(splatData.x),\n uintBitsToFloat(splatData.y),\n uintBitsToFloat(splatData.z)\n );\n}\n\nvoid encodeSplatLnScale(\n out uvec4 splatData, out uvec4 splatData2,\n vec3 center, vec3 lnScales, vec4 quaternion, vec4 rgba, float shapeAmount\n) {\n splatData.x = floatBitsToUint(center.x);\n splatData.y = floatBitsToUint(center.y);\n splatData.z = floatBitsToUint(center.z);\n splatData.w = packHalf2x16(clamp(vec2(rgba.a, shapeAmount), 0.0, 1.0));\n\n splatData2.x = packHalf2x16(rgba.rg);\n splatData2.y = packHalf2x16(vec2(rgba.b, lnScales.x));\n splatData2.z = packHalf2x16(lnScales.yz);\n splatData2.w = encodeQuatOctXy1010R12(quaternion);\n}\n\nvec2 decodeSplatAlphaShapeAmount(uvec4 splatData) {\n return unpackHalf2x16(splatData.w);\n}\n\nvoid decodeSplatAttributesLnScale(\n uvec4 splatData2,\n float alpha,\n out vec3 lnScales, out vec4 quaternion, out vec4 rgba\n) {\n rgba.a = alpha;\n\n rgba.rg = unpackHalf2x16(splatData2.x);\n vec2 split = unpackHalf2x16(splatData2.y);\n rgba.b = split.x;\n lnScales.x = split.y;\n lnScales.yz = unpackHalf2x16(splatData2.z);\n quaternion = decodeQuatOctXy1010R12(splatData2.w);\n}\n\nvec3 decodeSplatShRgb(uint encoded) {\n uint biasedBase = (encoded >> 27u) & 0x1fu;\n \n \n float divisor = uintBitsToFloat((biasedBase + 112u) << 23u) / 255.0;\n\n vec3 rgb = vec3(uvec3(encoded & 0xffu, (encoded >> 8u) & 0xffu, (encoded >> 16u) & 0xffu));\n rgb *= divisor;\n\n return vec3(\n ((encoded & 0x1000000u) != 0u) ? -rgb.r : rgb.r,\n ((encoded & 0x2000000u) != 0u) ? -rgb.g : rgb.g,\n ((encoded & 0x4000000u) != 0u) ? -rgb.b : rgb.b\n );\n}\n\nvec3 quatVec(vec4 q, vec3 v) {\n \n vec3 t = 2.0 * cross(q.xyz, v);\n return v + q.w * t + cross(q.xyz, t);\n}\n\nvec4 quatQuat(vec4 q1, vec4 q2) {\n return vec4(\n q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,\n q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x,\n q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w,\n q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z\n );\n}\n\nmat3 scaleQuaternionToMatrix(vec3 s, vec4 q) {\n \n return mat3(\n s.x * (1.0 - 2.0 * (q.y * q.y + q.z * q.z)),\n s.x * (2.0 * (q.x * q.y + q.w * q.z)),\n s.x * (2.0 * (q.x * q.z - q.w * q.y)),\n s.y * (2.0 * (q.x * q.y - q.w * q.z)),\n s.y * (1.0 - 2.0 * (q.x * q.x + q.z * q.z)),\n s.y * (2.0 * (q.y * q.z + q.w * q.x)),\n s.z * (2.0 * (q.x * q.z + q.w * q.y)),\n s.z * (2.0 * (q.y * q.z - q.w * q.x)),\n s.z * (1.0 - 2.0 * (q.x * q.x + q.y * q.y))\n );\n}\n\nivec3 splatTexCoord(int index) {\n uint x = uint(index) & SPLAT_TEX_WIDTH_MASK;\n uint y = (uint(index) >> SPLAT_TEX_WIDTH_BITS) & SPLAT_TEX_HEIGHT_MASK;\n uint z = uint(index) >> SPLAT_TEX_LAYER_BITS;\n return ivec3(x, y, z);\n}";
|
|
24459
|
-
var splatFragment_default = "precision highp float;\nprecision highp int;\n\n#include <splatDefines>\n\nuniform float near;\nuniform float far;\n#ifndef GSL_COLOR_IN_VERTEX\nuniform bool encodeLinear;\n#endif\nuniform float time;\nuniform float minAlpha;\nuniform bool stochastic;\nuniform vec4 stochasticTemporalSample;\nuniform bool stochasticResolve;\nuniform bool depthOnly;\nuniform highp usampler2D stochasticNoise;\nuniform vec2 viewportOrigin;\n\nout vec4 fragColor;\n\nin vec4 vRgba;\nin vec2 vSplatUv;\nflat in uint vStochasticHash;\nflat in float vSupportRadiusSquared;\nflat in float vKernelPower;\n\n#include <logdepthbuf_pars_fragment>\n\nvoid main() {\n vec4 rgba = vRgba;\n\n float z2 = dot(vSplatUv, vSplatUv);\n if (z2 > vSupportRadiusSquared) {\n discard;\n }\n\n float kernelAlpha = exp(-0.5 * z2);\n if (vKernelPower != 0.0) {\n kernelAlpha = 1.0 - pow(1.0 - kernelAlpha, vKernelPower);\n }\n rgba.a *= kernelAlpha;\n\n if (rgba.a < minAlpha) {\n discard;\n }\n if (stochastic || depthOnly) {\n \n uvec2 pixel = uvec2(gl_FragCoord.xy - viewportOrigin);\n uvec2 offset = uvec2(vStochasticHash, vStochasticHash >> 5u);\n if (stochastic && !depthOnly) offset += uvec2(stochasticTemporalSample.zw);\n ivec2 coord = ivec2((pixel + offset) & uvec2(31u));\n float randomValue = (float(texelFetch(stochasticNoise, coord, 0).r) + 0.5) / 1024.0;\n if (randomValue >= rgba.a) {\n discard;\n }\n\n if (depthOnly) {\n fragColor = vec4(0.0);\n #include <logdepthbuf_fragment>\n return;\n }\n }\n #ifndef GSL_COLOR_IN_VERTEX\n if (encodeLinear) {\n rgba.rgb = srgbToLinear(rgba.rgb);\n }\n #endif\n\n if (stochastic) {\n \n \n fragColor = vec4(rgba.rgb, stochasticResolve ? 2.0 : 1.0);\n #include <logdepthbuf_fragment>\n return;\n }\n\n #ifdef PREMULTIPLIED_ALPHA\n fragColor = vec4(rgba.rgb * rgba.a, rgba.a);\n #else\n fragColor = rgba;\n #endif\n\n #include <logdepthbuf_fragment>\n}";
|
|
24459
|
+
var splatFragment_default = "precision highp float;\nprecision highp int;\n\n#include <splatDefines>\n\nuniform float near;\nuniform float far;\n#ifndef GSL_COLOR_IN_VERTEX\nuniform bool encodeLinear;\n#endif\nuniform float time;\nuniform float minAlpha;\nuniform bool stochastic;\nuniform vec4 stochasticTemporalSample;\nuniform bool stochasticResolve;\nuniform bool depthOnly;\nuniform highp usampler2D stochasticNoise;\nuniform vec2 viewportOrigin;\n\nout vec4 fragColor;\n\nin vec4 vRgba;\nin vec2 vSplatUv;\nflat in uint vStochasticHash;\nflat in float vSupportRadiusSquared;\nflat in float vKernelPower;\n\n#include <logdepthbuf_pars_fragment>\n\nvoid main() {\n vec4 rgba = vRgba;\n\n float z2 = dot(vSplatUv, vSplatUv);\n if (z2 > vSupportRadiusSquared) {\n discard;\n }\n\n float kernelAlpha = exp(-0.5 * z2);\n if (vKernelPower != 0.0) {\n kernelAlpha = 1.0 - pow(1.0 - kernelAlpha, vKernelPower);\n }\n rgba.a *= kernelAlpha;\n\n if (rgba.a < minAlpha) {\n discard;\n }\n #if !GSL_SORTED_FRAGMENT\n if (stochastic || depthOnly) {\n \n uvec2 pixel = uvec2(gl_FragCoord.xy - viewportOrigin);\n uvec2 offset = uvec2(vStochasticHash, vStochasticHash >> 5u);\n if (stochastic && !depthOnly) offset += uvec2(stochasticTemporalSample.zw);\n ivec2 coord = ivec2((pixel + offset) & uvec2(31u));\n float randomValue = (float(texelFetch(stochasticNoise, coord, 0).r) + 0.5) / 1024.0;\n if (randomValue >= rgba.a) {\n discard;\n }\n\n if (depthOnly) {\n fragColor = vec4(0.0);\n #include <logdepthbuf_fragment>\n return;\n }\n }\n #ifndef GSL_COLOR_IN_VERTEX\n if (encodeLinear) {\n rgba.rgb = srgbToLinear(rgba.rgb);\n }\n #endif\n\n if (stochastic) {\n \n \n fragColor = vec4(rgba.rgb, stochasticResolve ? 2.0 : 1.0);\n #include <logdepthbuf_fragment>\n return;\n }\n #endif\n\n #ifdef PREMULTIPLIED_ALPHA\n fragColor = vec4(rgba.rgb * rgba.a, rgba.a);\n #else\n fragColor = rgba;\n #endif\n\n #include <logdepthbuf_fragment>\n}";
|
|
24460
24460
|
var splatVertex_default = "precision highp float;\nprecision highp int;\nprecision highp usampler2DArray;\n\n#include <splatDefines>\n\nout vec4 vRgba;\nout vec2 vSplatUv;\nflat out uint vStochasticHash;\nflat out float vSupportRadiusSquared;\nflat out float vKernelPower;\n\nuniform vec2 renderSize;\nuniform vec4 renderToViewQuat;\nuniform vec3 renderToViewPos;\n\nuniform float renderToViewScale;\nuniform float maxStdDev;\nuniform float minPixelRadius;\nuniform float maxPixelRadius;\nuniform float time;\nuniform float deltaTime;\nuniform float minAlpha;\nuniform float blurAmount;\nuniform float preBlurAmount;\nuniform float clipXY;\nuniform float focalAdjustment;\nuniform bool stochastic;\nuniform vec4 stochasticTemporalSample;\n#ifdef GSL_COLOR_IN_VERTEX\nuniform bool encodeLinear;\n#endif\nuniform bool depthOnly;\n\nuniform usampler2D ordering;\nuniform usampler2DArray splats;\nuniform usampler2DArray splats2;\nuniform usampler2DArray stochasticSeeds;\n\nbool isPerspectiveMatrix( mat4 m ) {\n return m[ 2 ][ 3 ] == -1.0;\n}\n\n#include <logdepthbuf_pars_vertex>\n\nuint hashU32(uint value) {\n value ^= value >> 16u;\n value *= 0x7feb352du;\n value ^= value >> 15u;\n value *= 0x846ca68bu;\n value ^= value >> 16u;\n return value;\n}\n\nfloat gaussianSupportRadius(float alpha, float maximumRadius) {\n if (minAlpha <= 0.0) return maximumRadius;\n float radiusSquared = 2.0 * log(alpha / minAlpha);\n return min(maximumRadius, sqrt(max(0.0, radiusSquared)));\n}\n\nfloat wideSupportRadius(float alpha, float power, float maximumRadius) {\n if (minAlpha <= 0.0) return maximumRadius;\n \n \n float radiusSquared = 2.0 * log(alpha * power / minAlpha);\n return min(maximumRadius, sqrt(max(0.0, radiusSquared)));\n}\n\nvoid main() {\n \n gl_Position = vec4(0.0, 0.0, 2.0, 1.0);\n\n uint splatIndex;\n if (stochastic || depthOnly) {\n \n splatIndex = uint(gl_InstanceID);\n } else {\n ivec2 orderingCoord = ivec2((gl_InstanceID >> 2) & 4095, gl_InstanceID >> 14);\n splatIndex = texelFetch(ordering, orderingCoord, 0)[gl_InstanceID & 3];\n }\n if (splatIndex == 0xffffffffu) {\n \n return;\n }\n\n ivec3 texCoord = splatTexCoord(int(splatIndex));\n uvec4 splat1 = texelFetch(splats, texCoord, 0);\n vec2 alphaShapeAmount = decodeSplatAlphaShapeAmount(splat1);\n float alpha = alphaShapeAmount.x;\n if ((alpha == 0.0) || (alpha < minAlpha)) {\n return;\n }\n vec3 center = decodeSplatCenter(splat1);\n \n vec3 viewCenter = renderToViewScale * quatVec(renderToViewQuat, center) + renderToViewPos;\n\n \n if (viewCenter.z >= 0.0) {\n return;\n }\n\n \n vec4 clipCenter = projectionMatrix * vec4(viewCenter, 1.0);\n\n \n if (abs(clipCenter.z) >= clipCenter.w) {\n return;\n }\n\n \n float clip = clipXY * clipCenter.w;\n if (abs(clipCenter.x) > clip || abs(clipCenter.y) > clip) {\n return;\n }\n\n \n \n vec3 lnScales;\n vec4 quaternion, rgba;\n uvec4 splat2 = texelFetch(splats2, texCoord, 0);\n decodeSplatAttributesLnScale(splat2, alpha, lnScales, quaternion, rgba);\n vec3 scales = exp(lnScales);\n if (all(equal(scales, vec3(0.0)))) {\n return;\n }\n\n \n rgba.rgb = max(rgba.rgb, vec3(0.0));\n\n \n \n float kernelShape = 1.0 + 4.0 * min(alphaShapeAmount.y, 1.0);\n \n \n float kernelPower = 0.0;\n if (kernelShape > 1.0) {\n kernelPower = exp(\n (kernelShape * kernelShape - 1.0) / 2.718281828459045\n );\n }\n vKernelPower = kernelPower;\n\n \n float maximumSupportRadius = maxStdDev\n + 0.7 * max(kernelShape - 1.0, 0.0);\n float supportRadius = maximumSupportRadius;\n\n scales *= renderToViewScale;\n\n vRgba = vec4(rgba.rgb, alpha);\n\n \n vec4 viewQuaternion = quatQuat(renderToViewQuat, quaternion);\n\n \n mat3 RS = scaleQuaternionToMatrix(scales, viewQuaternion);\n\n \n vec2 scaledRenderSize = renderSize * focalAdjustment;\n vec2 focal = 0.5 * scaledRenderSize * vec2(projectionMatrix[0][0], projectionMatrix[1][1]);\n\n vec3 j0;\n vec3 j1;\n if (isOrthographic) {\n j0 = vec3(focal.x, 0.0, 0.0);\n j1 = vec3(0.0, focal.y, 0.0);\n } else {\n float invZ = 1.0 / viewCenter.z;\n vec2 J1 = focal * invZ;\n vec2 J2 = -(J1 * viewCenter.xy) * invZ;\n j0 = vec3(J1.x, 0.0, J2.x);\n j1 = vec3(0.0, J1.y, J2.y);\n }\n\n \n \n vec3 p0 = transpose(RS) * j0;\n vec3 p1 = transpose(RS) * j1;\n float a = dot(p0, p0);\n float b = dot(p0, p1);\n float d = dot(p1, p1);\n\n \n a += preBlurAmount;\n d += preBlurAmount;\n\n float detOrig = a * d - b * b;\n \n a += blurAmount;\n d += blurAmount;\n float det = a * d - b * b;\n if (det <= 0.0) return;\n\n \n float blurAdjust = sqrt(max(0.0, detOrig / det));\n alpha *= blurAdjust;\n if (!(alpha > 0.0) || alpha < minAlpha) {\n return;\n }\n vRgba.a = alpha;\n\n \n if (kernelPower == 0.0) {\n supportRadius = gaussianSupportRadius(alpha, supportRadius);\n } else {\n supportRadius = wideSupportRadius(alpha, kernelPower, supportRadius);\n }\n vSupportRadiusSquared = supportRadius * supportRadius;\n vSplatUv = position.xy * supportRadius;\n\n \n float eigenAvg = 0.5 * (a + d);\n float eigenDelta = length(vec2(0.5 * (a - d), b));\n float eigen1 = eigenAvg + eigenDelta;\n \n float eigen2 = max(eigenAvg - eigenDelta, 1e-4);\n\n vec2 eigenVec1 = (abs(b) > 0.001)\n ? normalize(vec2(b, eigen1 - a))\n : ((a >= d) ? vec2(1.0, 0.0) : vec2(0.0, 1.0));\n vec2 eigenVec2 = vec2(eigenVec1.y, -eigenVec1.x);\n\n \n \n float maxProjectedRadius = maxPixelRadius * focalAdjustment;\n float scale1 = min(maxProjectedRadius, maximumSupportRadius * sqrt(eigen1));\n float scale2 = min(maxProjectedRadius, maximumSupportRadius * sqrt(eigen2));\n float supportScale = (maximumSupportRadius > 0.0)\n ? supportRadius / maximumSupportRadius\n : 0.0;\n \n \n float cullScale = (kernelPower == 0.0) ? supportScale : 1.0;\n \n float minProjectedRadius = minPixelRadius * focalAdjustment;\n if (scale1 * cullScale < minProjectedRadius && scale2 * cullScale < minProjectedRadius) {\n return;\n }\n scale1 *= supportScale;\n scale2 *= supportScale;\n\n \n \n vStochasticHash = 0u;\n if (stochastic || depthOnly) {\n vStochasticHash = hashU32(texelFetch(stochasticSeeds, texCoord, 0).r);\n }\n\n \n #ifdef GSL_COLOR_IN_VERTEX\n if (encodeLinear && !depthOnly) {\n vRgba.rgb = srgbToLinear(vRgba.rgb);\n }\n #endif\n\n \n vec2 pixelOffset = position.x * eigenVec1 * scale1 + position.y * eigenVec2 * scale2;\n vec2 ndcOffset = (2.0 / scaledRenderSize) * pixelOffset;\n\n \n vec3 ndcCenter = clipCenter.xyz / clipCenter.w;\n vec3 ndc = vec3(ndcCenter.xy + ndcOffset, ndcCenter.z);\n\n gl_Position = vec4(ndc.xy * clipCenter.w, clipCenter.zw);\n if (stochastic && !depthOnly) {\n gl_Position.xy += stochasticTemporalSample.xy * gl_Position.w;\n }\n\n #include <logdepthbuf_vertex>\n}";
|
|
24461
24461
|
let shaders = null;
|
|
24462
24462
|
function getShaders() {
|
|
@@ -25605,6 +25605,7 @@ const stochasticHash = N.Fn(([input]) => {
|
|
|
25605
25605
|
return value;
|
|
25606
25606
|
});
|
|
25607
25607
|
function createSplatFragment({
|
|
25608
|
+
sorted,
|
|
25608
25609
|
minAlpha,
|
|
25609
25610
|
stochastic,
|
|
25610
25611
|
stochasticResolve,
|
|
@@ -25634,6 +25635,7 @@ function createSplatFragment({
|
|
|
25634
25635
|
});
|
|
25635
25636
|
rgba.a.mulAssign(kernelAlpha);
|
|
25636
25637
|
rgba.a.lessThan(minAlpha).discard();
|
|
25638
|
+
if (sorted) return rgba;
|
|
25637
25639
|
N.If(stochastic.or(depthOnly), () => {
|
|
25638
25640
|
const pixel = N.uvec2(N.screenCoordinate.xy.sub(vViewportOrigin));
|
|
25639
25641
|
const offset = N.uvec2(
|
|
@@ -25678,7 +25680,9 @@ function createSplatNodeMaterial({
|
|
|
25678
25680
|
premultipliedAlpha,
|
|
25679
25681
|
transparent,
|
|
25680
25682
|
depthTest,
|
|
25681
|
-
depthWrite
|
|
25683
|
+
depthWrite,
|
|
25684
|
+
sorted = false,
|
|
25685
|
+
vertexNode: sharedVertexNode
|
|
25682
25686
|
}) {
|
|
25683
25687
|
const orderingNode = providedOrderingNode ?? createDefaultOrderingNode();
|
|
25684
25688
|
const splats = textureBinding(uniforms, "splats", true);
|
|
@@ -25713,6 +25717,7 @@ function createSplatNodeMaterial({
|
|
|
25713
25717
|
vViewportOrigin,
|
|
25714
25718
|
fragmentNode
|
|
25715
25719
|
} = createSplatFragment({
|
|
25720
|
+
sorted,
|
|
25716
25721
|
minAlpha,
|
|
25717
25722
|
stochastic,
|
|
25718
25723
|
stochasticResolve,
|
|
@@ -25721,7 +25726,7 @@ function createSplatNodeMaterial({
|
|
|
25721
25726
|
depthOnly,
|
|
25722
25727
|
premultipliedAlpha: premultipliedAlphaNode
|
|
25723
25728
|
});
|
|
25724
|
-
|
|
25729
|
+
function buildVertex(builder) {
|
|
25725
25730
|
const camera = materialCamera(builder);
|
|
25726
25731
|
const clipPosition = N.vec4(0, 0, 2, 1).toVar();
|
|
25727
25732
|
vRgba.assign(N.vec4(0));
|
|
@@ -25805,34 +25810,51 @@ function createSplatNodeMaterial({
|
|
|
25805
25810
|
clipPosition.xy.addAssign(temporalSample.xy.mul(clipPosition.w));
|
|
25806
25811
|
});
|
|
25807
25812
|
return clipPosition;
|
|
25808
|
-
}
|
|
25809
|
-
const
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25813
|
+
}
|
|
25814
|
+
const vertexNode = sharedVertexNode ?? N.Fn(buildVertex)();
|
|
25815
|
+
return Object.assign(new NodeMaterial(), {
|
|
25816
|
+
uniforms,
|
|
25817
|
+
orderingNode,
|
|
25818
|
+
vertexNode,
|
|
25819
|
+
colorNode: fragmentNode,
|
|
25820
|
+
premultipliedAlpha,
|
|
25821
|
+
transparent,
|
|
25822
|
+
depthTest,
|
|
25823
|
+
depthWrite,
|
|
25824
|
+
side: THREE__namespace.FrontSide,
|
|
25825
|
+
allowOverride: false,
|
|
25826
|
+
fog: false,
|
|
25827
|
+
toneMapped: false
|
|
25828
|
+
});
|
|
25821
25829
|
}
|
|
25822
25830
|
class NodeSplatBackend {
|
|
25823
25831
|
constructor(renderer, uniforms, options, orderingNode, vertexData) {
|
|
25824
25832
|
this.renderer = renderer;
|
|
25825
|
-
this.
|
|
25833
|
+
this.sortedMaterial = createSplatNodeMaterial({
|
|
25826
25834
|
uniforms,
|
|
25827
25835
|
...options,
|
|
25828
25836
|
orderingNode,
|
|
25829
|
-
vertexData
|
|
25837
|
+
vertexData,
|
|
25838
|
+
sorted: true
|
|
25839
|
+
});
|
|
25840
|
+
this.uniformMaterial = createSplatNodeMaterial({
|
|
25841
|
+
uniforms,
|
|
25842
|
+
...options,
|
|
25843
|
+
orderingNode: this.sortedMaterial.orderingNode,
|
|
25844
|
+
vertexNode: this.sortedMaterial.vertexNode
|
|
25830
25845
|
});
|
|
25831
25846
|
}
|
|
25847
|
+
selectMaterial(useUniform) {
|
|
25848
|
+
return useUniform ? this.uniformMaterial : this.sortedMaterial;
|
|
25849
|
+
}
|
|
25850
|
+
dispose() {
|
|
25851
|
+
this.sortedMaterial.dispose();
|
|
25852
|
+
this.uniformMaterial.dispose();
|
|
25853
|
+
}
|
|
25832
25854
|
createDepthMaterial(uniforms) {
|
|
25833
25855
|
return createSplatNodeMaterial({
|
|
25834
25856
|
uniforms,
|
|
25835
|
-
orderingNode: this.
|
|
25857
|
+
orderingNode: this.sortedMaterial.orderingNode,
|
|
25836
25858
|
premultipliedAlpha: false,
|
|
25837
25859
|
transparent: false,
|
|
25838
25860
|
depthTest: true,
|
|
@@ -25922,7 +25944,7 @@ class WebGLFallbackSplatBackend extends NodeSplatBackend {
|
|
|
25922
25944
|
return this.ordering.data;
|
|
25923
25945
|
}
|
|
25924
25946
|
setCPUOrdering(update) {
|
|
25925
|
-
this.
|
|
25947
|
+
this.sortedMaterial.orderingNode.value = this.ordering.update(
|
|
25926
25948
|
update,
|
|
25927
25949
|
(texture2, rows) => {
|
|
25928
25950
|
this.renderer.initTexture(texture2);
|
|
@@ -25936,6 +25958,7 @@ class WebGLFallbackSplatBackend extends NodeSplatBackend {
|
|
|
25936
25958
|
);
|
|
25937
25959
|
}
|
|
25938
25960
|
dispose() {
|
|
25961
|
+
super.dispose();
|
|
25939
25962
|
this.ordering.dispose();
|
|
25940
25963
|
}
|
|
25941
25964
|
bindOrdering(material, _uniforms) {
|
|
@@ -25947,6 +25970,7 @@ function createWebGLSplatMaterial(uniforms, options) {
|
|
|
25947
25970
|
const defines2 = {};
|
|
25948
25971
|
if (options.vertexShader === void 0 && options.fragmentShader === void 0) {
|
|
25949
25972
|
defines2.GSL_COLOR_IN_VERTEX = 1;
|
|
25973
|
+
defines2.GSL_SORTED_FRAGMENT = 0;
|
|
25950
25974
|
}
|
|
25951
25975
|
return new THREE__namespace.ShaderMaterial({
|
|
25952
25976
|
...options,
|
|
@@ -25968,6 +25992,16 @@ class WebGLSplatBackend {
|
|
|
25968
25992
|
const extension = renderer.getContext().getExtension("WEBGL_provoking_vertex");
|
|
25969
25993
|
extension == null ? void 0 : extension.provokingVertexWEBGL(extension.FIRST_VERTEX_CONVENTION_WEBGL);
|
|
25970
25994
|
}
|
|
25995
|
+
selectMaterial(useUniform) {
|
|
25996
|
+
const { material } = this;
|
|
25997
|
+
const currentSorted = material.defines.GSL_SORTED_FRAGMENT;
|
|
25998
|
+
const sorted = Number(!useUniform);
|
|
25999
|
+
if (currentSorted !== void 0 && currentSorted !== sorted) {
|
|
26000
|
+
material.defines.GSL_SORTED_FRAGMENT = sorted;
|
|
26001
|
+
material.needsUpdate = true;
|
|
26002
|
+
}
|
|
26003
|
+
return material;
|
|
26004
|
+
}
|
|
25971
26005
|
createDepthMaterial(uniforms) {
|
|
25972
26006
|
return createWebGLSplatMaterial(uniforms, {
|
|
25973
26007
|
premultipliedAlpha: false,
|
|
@@ -26012,6 +26046,7 @@ class WebGLSplatBackend {
|
|
|
26012
26046
|
}
|
|
26013
26047
|
dispose() {
|
|
26014
26048
|
this.ordering.dispose();
|
|
26049
|
+
this.material.dispose();
|
|
26015
26050
|
}
|
|
26016
26051
|
}
|
|
26017
26052
|
function makeTexture$1(channels = 4) {
|
|
@@ -26693,6 +26728,7 @@ class ProjectedSplats {
|
|
|
26693
26728
|
this.capacity = 0;
|
|
26694
26729
|
this.viewCapacity = 0;
|
|
26695
26730
|
this.disposed = false;
|
|
26731
|
+
this.projectedInputs = [];
|
|
26696
26732
|
const computeRenderer = renderer;
|
|
26697
26733
|
this.limits = computeRenderer.backend.device.limits;
|
|
26698
26734
|
this.state = {
|
|
@@ -26951,6 +26987,7 @@ class ProjectedSplats {
|
|
|
26951
26987
|
this.state.viewStride.value = capacity;
|
|
26952
26988
|
}
|
|
26953
26989
|
render(accumulator, camera, geometry, radial, fastSort, shrink = false) {
|
|
26990
|
+
var _a2, _b;
|
|
26954
26991
|
if (this.error) throw this.error;
|
|
26955
26992
|
if (this.compiledSlots === 0 || this.disposed) {
|
|
26956
26993
|
geometry.instanceCount = 0;
|
|
@@ -26966,6 +27003,40 @@ class ProjectedSplats {
|
|
|
26966
27003
|
this.cache.ensureOrder(multiView, shrink);
|
|
26967
27004
|
geometry.setIndirect(this.indirect);
|
|
26968
27005
|
geometry.setSplatCount(Math.max(1, accumulator.numSplats));
|
|
27006
|
+
const { uniforms } = this;
|
|
27007
|
+
const inputs = [
|
|
27008
|
+
accumulator,
|
|
27009
|
+
accumulator.version,
|
|
27010
|
+
...accumulator.viewOrigin.toArray(),
|
|
27011
|
+
multiView,
|
|
27012
|
+
cameras.length,
|
|
27013
|
+
radial,
|
|
27014
|
+
fastSort,
|
|
27015
|
+
uniforms.stochastic.value,
|
|
27016
|
+
uniforms.maxStdDev.value,
|
|
27017
|
+
uniforms.minPixelRadius.value,
|
|
27018
|
+
uniforms.maxPixelRadius.value,
|
|
27019
|
+
uniforms.minAlpha.value,
|
|
27020
|
+
uniforms.preBlurAmount.value,
|
|
27021
|
+
uniforms.blurAmount.value,
|
|
27022
|
+
uniforms.clipXY.value,
|
|
27023
|
+
uniforms.focalAdjustment.value
|
|
27024
|
+
];
|
|
27025
|
+
for (const { node } of accumulator.mapping) inputs.push(node.layers.mask);
|
|
27026
|
+
for (const view of cameras) {
|
|
27027
|
+
inputs.push(
|
|
27028
|
+
...view.matrixWorld.elements,
|
|
27029
|
+
...view.projectionMatrix.elements,
|
|
27030
|
+
view.near,
|
|
27031
|
+
view.far,
|
|
27032
|
+
view.layers.mask,
|
|
27033
|
+
((_a2 = view.viewport) == null ? void 0 : _a2.z) ?? uniforms.renderSize.value.x,
|
|
27034
|
+
((_b = view.viewport) == null ? void 0 : _b.w) ?? uniforms.renderSize.value.y
|
|
27035
|
+
);
|
|
27036
|
+
}
|
|
27037
|
+
if (!shrink && inputs.length === this.projectedInputs.length && inputs.every((value, index2) => value === this.projectedInputs[index2]))
|
|
27038
|
+
return;
|
|
27039
|
+
this.projectedInputs = [];
|
|
26969
27040
|
for (let eye = 0; eye < cameras.length; eye++) {
|
|
26970
27041
|
const view = cameras[eye];
|
|
26971
27042
|
this.state.viewIndex.value = eye;
|
|
@@ -27008,10 +27079,12 @@ class ProjectedSplats {
|
|
|
27008
27079
|
}
|
|
27009
27080
|
this.renderer.compute(pending);
|
|
27010
27081
|
}
|
|
27082
|
+
this.projectedInputs = inputs;
|
|
27011
27083
|
}
|
|
27012
27084
|
dispose() {
|
|
27013
27085
|
if (this.disposed) return;
|
|
27014
27086
|
this.disposed = true;
|
|
27087
|
+
this.projectedInputs = [];
|
|
27015
27088
|
void this.compilation.then(() => this.disposeResources());
|
|
27016
27089
|
}
|
|
27017
27090
|
disposeResources() {
|
|
@@ -27061,6 +27134,7 @@ class WebGPUSplatBackend extends NodeSplatBackend {
|
|
|
27061
27134
|
bindOrdering(_material, _uniforms) {
|
|
27062
27135
|
}
|
|
27063
27136
|
dispose() {
|
|
27137
|
+
super.dispose();
|
|
27064
27138
|
this.projection.dispose();
|
|
27065
27139
|
}
|
|
27066
27140
|
}
|
|
@@ -27173,7 +27247,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27173
27247
|
const geometry = new SplatGeometry(
|
|
27174
27248
|
backend.kind === "webgpu" ? WEBGPU_SPLATS_PER_INSTANCE : 1
|
|
27175
27249
|
);
|
|
27176
|
-
const material = backend.
|
|
27250
|
+
const material = backend.selectMaterial(autoStochastic || stochastic);
|
|
27177
27251
|
super(geometry, material);
|
|
27178
27252
|
this.renderSize = new THREE__namespace.Vector2();
|
|
27179
27253
|
this.lastFrame = -1;
|
|
@@ -27207,7 +27281,6 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27207
27281
|
this.renderer = options.renderer;
|
|
27208
27282
|
this.backend = backend;
|
|
27209
27283
|
this.capture = new SplatCapture(this, backend, () => this.beginCapture());
|
|
27210
|
-
this.material = material;
|
|
27211
27284
|
this.uniforms = uniforms;
|
|
27212
27285
|
this.depthPass = new SplatDepthPass(
|
|
27213
27286
|
this,
|
|
@@ -27224,8 +27297,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27224
27297
|
this._stochastic = stochastic;
|
|
27225
27298
|
this._renderDepth = renderDepth;
|
|
27226
27299
|
this.stochasticPhase = stochastic ? "forced" : null;
|
|
27227
|
-
this.
|
|
27228
|
-
this.applyMaterialState(this.stochasticFrame);
|
|
27300
|
+
this.refreshRenderConfiguration();
|
|
27229
27301
|
this.frustumCulled = false;
|
|
27230
27302
|
this.onDirty = options.onDirty;
|
|
27231
27303
|
this.dirty = true;
|
|
@@ -27290,7 +27362,6 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27290
27362
|
this.maxSplats = 0;
|
|
27291
27363
|
this.activeSplats = 0;
|
|
27292
27364
|
this.geometry.dispose();
|
|
27293
|
-
this.material.dispose();
|
|
27294
27365
|
}
|
|
27295
27366
|
/** Native WebGPU sorts on the GPU before drawing; WebGL sorts in a worker. */
|
|
27296
27367
|
get synchronousSort() {
|
|
@@ -27393,25 +27464,27 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27393
27464
|
}
|
|
27394
27465
|
}
|
|
27395
27466
|
applyMaterialState(stochasticActive) {
|
|
27467
|
+
const { material } = this;
|
|
27396
27468
|
const managedAsOpaque = this.usesManagedRenderState;
|
|
27397
27469
|
const transparent = managedAsOpaque ? false : this._transparent;
|
|
27398
27470
|
const blending = stochasticActive ? THREE__namespace.NoBlending : managedAsOpaque && this._transparent ? THREE__namespace.CustomBlending : this.sortedBlending;
|
|
27399
|
-
const wasOpaque = isOpaqueMaterial(
|
|
27400
|
-
|
|
27401
|
-
|
|
27471
|
+
const wasOpaque = isOpaqueMaterial(material);
|
|
27472
|
+
material.transparent = transparent;
|
|
27473
|
+
material.blending = blending;
|
|
27402
27474
|
if (managedAsOpaque && this._transparent) {
|
|
27403
|
-
|
|
27404
|
-
|
|
27405
|
-
|
|
27406
|
-
|
|
27407
|
-
|
|
27408
|
-
|
|
27409
|
-
}
|
|
27410
|
-
|
|
27411
|
-
|
|
27475
|
+
material.blendEquation = THREE__namespace.AddEquation;
|
|
27476
|
+
material.blendSrc = this._premultipliedAlpha ? THREE__namespace.OneFactor : THREE__namespace.SrcAlphaFactor;
|
|
27477
|
+
material.blendDst = THREE__namespace.OneMinusSrcAlphaFactor;
|
|
27478
|
+
material.blendEquationAlpha = THREE__namespace.AddEquation;
|
|
27479
|
+
material.blendSrcAlpha = THREE__namespace.OneFactor;
|
|
27480
|
+
material.blendDstAlpha = THREE__namespace.OneMinusSrcAlphaFactor;
|
|
27481
|
+
}
|
|
27482
|
+
material.depthTest = stochasticActive ? true : this._depthTest;
|
|
27483
|
+
material.depthWrite = stochasticActive ? true : this._depthWrite;
|
|
27412
27484
|
this.depthPass.updateVisibility();
|
|
27413
|
-
if (wasOpaque !== isOpaqueMaterial(
|
|
27414
|
-
|
|
27485
|
+
if (material.premultipliedAlpha !== this._premultipliedAlpha || wasOpaque !== isOpaqueMaterial(material)) {
|
|
27486
|
+
material.premultipliedAlpha = this._premultipliedAlpha;
|
|
27487
|
+
material.needsUpdate = true;
|
|
27415
27488
|
}
|
|
27416
27489
|
}
|
|
27417
27490
|
onBeforeRender(renderer, scene, camera) {
|
|
@@ -27459,7 +27532,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27459
27532
|
gaussianSplatRenderer.updateTimeoutId = setTimeout(() => {
|
|
27460
27533
|
gaussianSplatRenderer.updateTimeoutId = -1;
|
|
27461
27534
|
gaussianSplatRenderer.updateInternal(updateRequest);
|
|
27462
|
-
},
|
|
27535
|
+
}, 0);
|
|
27463
27536
|
}
|
|
27464
27537
|
}
|
|
27465
27538
|
const typedCamera = camera;
|
|
@@ -27527,6 +27600,13 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27527
27600
|
gaussianSplatRenderer.requestMotionFollowup = false;
|
|
27528
27601
|
gaussianSplatRenderer.setDirty();
|
|
27529
27602
|
}
|
|
27603
|
+
if (!this.stochasticModeEnabled && !this.stochasticFrame) {
|
|
27604
|
+
const material = this.backend.selectMaterial(false);
|
|
27605
|
+
if (this.material !== material) {
|
|
27606
|
+
this.material = material;
|
|
27607
|
+
this.applyMaterialState(false);
|
|
27608
|
+
}
|
|
27609
|
+
}
|
|
27530
27610
|
}
|
|
27531
27611
|
clearSplats() {
|
|
27532
27612
|
this.activeSplats = 0;
|
|
@@ -27896,6 +27976,10 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27896
27976
|
);
|
|
27897
27977
|
}
|
|
27898
27978
|
}
|
|
27979
|
+
refreshRenderConfiguration() {
|
|
27980
|
+
this.applyRenderOrder();
|
|
27981
|
+
this.applyMaterialState(this.stochasticFrame);
|
|
27982
|
+
}
|
|
27899
27983
|
refreshStochasticConfiguration() {
|
|
27900
27984
|
this.requestMotionFollowup = false;
|
|
27901
27985
|
if (this._stochastic) {
|
|
@@ -27909,8 +27993,10 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27909
27993
|
this.stochasticPhase = null;
|
|
27910
27994
|
this.stochasticWasForced = false;
|
|
27911
27995
|
}
|
|
27912
|
-
this.
|
|
27913
|
-
|
|
27996
|
+
this.material = this.backend.selectMaterial(
|
|
27997
|
+
this.stochasticModeEnabled || this.stochasticFrame
|
|
27998
|
+
);
|
|
27999
|
+
this.refreshRenderConfiguration();
|
|
27914
28000
|
this.setDirty();
|
|
27915
28001
|
}
|
|
27916
28002
|
get premultipliedAlpha() {
|
|
@@ -27921,9 +28007,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27921
28007
|
if (this._premultipliedAlpha !== nextValue) {
|
|
27922
28008
|
this._premultipliedAlpha = nextValue;
|
|
27923
28009
|
this.uniforms.premultipliedAlpha.value = nextValue;
|
|
27924
|
-
this.material.premultipliedAlpha = nextValue;
|
|
27925
28010
|
this.applyMaterialState(this.stochasticFrame);
|
|
27926
|
-
this.material.needsUpdate = true;
|
|
27927
28011
|
}
|
|
27928
28012
|
}
|
|
27929
28013
|
get transparent() {
|
|
@@ -27988,8 +28072,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
27988
28072
|
if (nextValue === this._renderDepth) return;
|
|
27989
28073
|
this.assertBuiltInSplatShaders(nextValue);
|
|
27990
28074
|
this._renderDepth = nextValue;
|
|
27991
|
-
this.
|
|
27992
|
-
this.applyMaterialState(this.stochasticFrame);
|
|
28075
|
+
this.refreshRenderConfiguration();
|
|
27993
28076
|
this.setDirty();
|
|
27994
28077
|
}
|
|
27995
28078
|
get depthTest() {
|
|
@@ -28004,8 +28087,7 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
28004
28087
|
}
|
|
28005
28088
|
set depthWrite(value) {
|
|
28006
28089
|
this._depthWrite = Boolean(value);
|
|
28007
|
-
this.
|
|
28008
|
-
this.applyMaterialState(this.stochasticFrame);
|
|
28090
|
+
this.refreshRenderConfiguration();
|
|
28009
28091
|
}
|
|
28010
28092
|
}
|
|
28011
28093
|
function isOpaqueMaterial(material) {
|