gaussian-splat-lite 1.1.6 → 1.1.8
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 +26 -1
- package/README.md +11 -3
- package/dist/gaussian-splat-lite.cjs +151 -114
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +79 -61
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +151 -114
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +2346 -2306
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.8] - 2026-09-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed TAA output color conversion and tone mapping on WebGPU and WebGL fallback when required by the renderer settings.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- TAA now reconstructs positions in view space on all backends, reducing small-near-plane numerical error and preserving logarithmic depth precision. History acceptance and accumulation rules are unchanged.
|
|
19
|
+
- TAA selects the nearest sample directly from logarithmic depth, and its node backends match Three.js's minimum near plane when decoding it.
|
|
20
|
+
- Packed per-Splat color and kernel varyings from 24 to 16 bytes across WebGL, WebGPU, and WebGL fallback. RGB and kernel power use half precision; source alpha and squared support radius retain float32 precision. RGB is capped at 65504, and kernel-power rounding can slightly change wide-kernel coverage.
|
|
21
|
+
- Moved fragment support-radius rejection before unpacking kernel data and deferred color assembly until after alpha and coverage rejection on all backends.
|
|
22
|
+
|
|
23
|
+
### Security
|
|
24
|
+
|
|
25
|
+
- Updated the development dependency `fast-uri` to 3.1.7 to resolve high-severity security advisories.
|
|
26
|
+
|
|
27
|
+
## [1.1.7] - 2026-09-27
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Removed the constant directional drift of stochastic noise under TAA by reordering temporal blue-noise offsets across all rendering backends.
|
|
32
|
+
|
|
10
33
|
## [1.1.6] - 2026-09-27
|
|
11
34
|
|
|
12
35
|
### Changed
|
|
@@ -420,7 +443,9 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
420
443
|
|
|
421
444
|
- Initial public release of the Three.js Gaussian Splatting renderer.
|
|
422
445
|
|
|
423
|
-
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.
|
|
446
|
+
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.8...HEAD
|
|
447
|
+
[1.1.8]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.7...v1.1.8
|
|
448
|
+
[1.1.7]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.6...v1.1.7
|
|
424
449
|
[1.1.6]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.5...v1.1.6
|
|
425
450
|
[1.1.5]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.4...v1.1.5
|
|
426
451
|
[1.1.4]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.1.3...v1.1.4
|
package/README.md
CHANGED
|
@@ -8,16 +8,22 @@
|
|
|
8
8
|
|
|
9
9
|
**Three.js Gaussian Splatting · WebGPU · Depth Rendering · Streaming**
|
|
10
10
|
|
|
11
|
+
Gaussian Splatting renderer for **Three.js**, with **WebGPU/WebGL2**, **depth rendering**, and **large-scene streaming**. Load PLY/SPZ/SOG/RAD models, combine them with regular 3D objects, and explore large scenes as detail loads around the camera.
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
11
15
|
**[Try Live Demo](https://gaussian-splat-lite.vercel.app/)**
|
|
12
16
|
|
|
17
|
+
</div>
|
|
18
|
+
|
|
13
19
|
<p align="center">
|
|
14
|
-
<
|
|
20
|
+
<a href="https://gaussian-splat-lite.vercel.app/">
|
|
21
|
+
<img src="./.github/assets/default-demo.gif" alt="Gaussian Splat Lite — default example with a seamless 90-degree camera orbit" width="800">
|
|
22
|
+
</a>
|
|
15
23
|
</p>
|
|
16
24
|
|
|
17
25
|
</div>
|
|
18
26
|
|
|
19
|
-
Gaussian Splatting renderer for **Three.js**, with **WebGPU/WebGL2**, **depth rendering**, and **large-scene streaming**. Load PLY/SPZ/SOG/RAD models, combine them with regular 3D objects, and explore large scenes as detail loads around the camera.
|
|
20
|
-
|
|
21
27
|
## Features
|
|
22
28
|
|
|
23
29
|
| Focus | What you get |
|
|
@@ -184,6 +190,8 @@ See [Contributing](CONTRIBUTING.md#validation) for validation commands. `npm run
|
|
|
184
190
|
|
|
185
191
|
The overall architecture of Gaussian Splat Lite draws on [Spark](https://github.com/sparkjsdev/spark) and [SuperSplat](https://github.com/playcanvas/supersplat).
|
|
186
192
|
|
|
193
|
+
Example model adapted from [shehabmekky](https://superspl.at/scene/c1e6297e) ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)).
|
|
194
|
+
|
|
187
195
|
## License
|
|
188
196
|
|
|
189
197
|
Licensed under [Apache 2.0](LICENSE). See [NOTICE](NOTICE) and [third-party licenses](THIRD_PARTY_LICENSES.md) for attribution.
|
|
@@ -24455,8 +24455,8 @@ class FullScreenQuad {
|
|
|
24455
24455
|
}
|
|
24456
24456
|
}
|
|
24457
24457
|
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}";
|
|
24458
|
-
var splatFragment_default = "precision highp float;\nprecision highp int;\n\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\
|
|
24459
|
-
var splatVertex_default = "precision highp float;\nprecision highp int;\nprecision highp usampler2DArray;\n\n#include <splatDefines>\n\
|
|
24458
|
+
var splatFragment_default = "precision highp float;\nprecision highp int;\n\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\nflat in uvec4 vSplat;\nin vec2 vSplatUv;\nflat in uint vStochasticHash;\n\n#include <logdepthbuf_pars_fragment>\n\nvoid main() {\n float z2 = dot(vSplatUv, vSplatUv);\n if (z2 > uintBitsToFloat(vSplat.w)) {\n discard;\n }\n\n vec2 blueKernelPower = unpackHalf2x16(vSplat.y);\n float kernelPower = blueKernelPower.y;\n float kernelAlpha = exp(-0.5 * z2);\n if (kernelPower != 0.0) {\n kernelAlpha = 1.0 - pow(1.0 - kernelAlpha, kernelPower);\n }\n float alpha = uintBitsToFloat(vSplat.z) * kernelAlpha;\n\n if (alpha < 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 >= alpha) {\n discard;\n }\n\n if (depthOnly) {\n fragColor = vec4(0.0);\n #include <logdepthbuf_fragment>\n return;\n }\n }\n #endif\n\n \n vec4 rgba = vec4(unpackHalf2x16(vSplat.x), blueKernelPower.x, alpha);\n #if !GSL_SORTED_FRAGMENT\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}";
|
|
24459
|
+
var splatVertex_default = "precision highp float;\nprecision highp int;\nprecision highp usampler2DArray;\n\n#include <splatDefines>\n\nflat out uvec4 vSplat;\nout vec2 vSplatUv;\nflat out uint vStochasticHash;\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 minAlpha;\nuniform float blurAmount;\nuniform float preBlurAmount;\nuniform float clipXY;\nuniform float focalAdjustment;\nuniform bool stochastic;\nuniform bool stochasticOrdering;\nuniform vec4 stochasticTemporalSample;\nuniform bool encodeLinear;\nuniform bool depthOnly;\nuniform uint splatCount;\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 index = uint(gl_InstanceID) * uint(SPLATS_PER_INSTANCE) + uint(position.z);\n if (index >= splatCount) return;\n\n uint splatIndex;\n if ((stochastic && !stochasticOrdering) || depthOnly) {\n \n splatIndex = index;\n } else {\n ivec2 orderingCoord = ivec2(int((index >> 2u) & 4095u), int(index >> 14u));\n splatIndex = texelFetch(ordering, orderingCoord, 0)[index & 3u];\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\n \n float maximumSupportRadius = maxStdDev\n + 0.7 * max(kernelShape - 1.0, 0.0);\n float supportRadius = maximumSupportRadius;\n\n scales *= renderToViewScale;\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\n \n if (kernelPower == 0.0) {\n supportRadius = gaussianSupportRadius(alpha, supportRadius);\n } else {\n supportRadius = wideSupportRadius(alpha, kernelPower, supportRadius);\n }\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 if (encodeLinear && !depthOnly) {\n rgba.rgb = srgbToLinear(rgba.rgb);\n }\n \n vec3 rgb = min(rgba.rgb, vec3(65504.0));\n vSplat = uvec4(\n packHalf2x16(rgb.rg),\n packHalf2x16(vec2(rgb.b, kernelPower)),\n floatBitsToUint(alpha),\n floatBitsToUint(supportRadius * supportRadius)\n );\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}";
|
|
24460
24460
|
let shaders = null;
|
|
24461
24461
|
function getShaders() {
|
|
24462
24462
|
if (!shaders) {
|
|
@@ -25613,47 +25613,50 @@ function createSplatFragment({
|
|
|
25613
25613
|
depthOnly,
|
|
25614
25614
|
premultipliedAlpha
|
|
25615
25615
|
}) {
|
|
25616
|
-
const
|
|
25616
|
+
const vSplat = N.varyingProperty("uvec4", "gslSplat");
|
|
25617
25617
|
const vSplatUv = N.varyingProperty("vec2", "gslSplatUv");
|
|
25618
25618
|
const vStochasticHash = N.varyingProperty("uint", "gslStochasticHash");
|
|
25619
|
-
const vSupportRadiusSquared = N.varyingProperty(
|
|
25620
|
-
"float",
|
|
25621
|
-
"gslSupportRadiusSquared"
|
|
25622
|
-
);
|
|
25623
|
-
const vKernelPower = N.varyingProperty("float", "gslKernelPower");
|
|
25624
25619
|
const vViewportOrigin = N.varyingProperty("vec2", "gslViewportOrigin");
|
|
25625
25620
|
const fragmentNode = N.Fn(() => {
|
|
25626
|
-
const rgba = vRgba.toVar();
|
|
25627
25621
|
const z2 = vSplatUv.dot(vSplatUv);
|
|
25628
|
-
z2.greaterThan(
|
|
25622
|
+
z2.greaterThan(N.uintBitsToFloat(vSplat.w)).discard();
|
|
25623
|
+
const blueKernelPower = N.unpackHalf2x16(vSplat.y);
|
|
25624
|
+
const kernelPower = blueKernelPower.y;
|
|
25629
25625
|
const kernelAlpha = z2.mul(-0.5).exp().toVar();
|
|
25630
|
-
N.If(
|
|
25626
|
+
N.If(kernelPower.notEqual(0), () => {
|
|
25631
25627
|
kernelAlpha.assign(
|
|
25632
|
-
N.float(1).sub(N.float(1).sub(kernelAlpha).pow(
|
|
25628
|
+
N.float(1).sub(N.float(1).sub(kernelAlpha).pow(kernelPower))
|
|
25633
25629
|
);
|
|
25634
25630
|
});
|
|
25635
|
-
|
|
25636
|
-
|
|
25631
|
+
const alpha = N.uintBitsToFloat(vSplat.z).mul(kernelAlpha).toVar();
|
|
25632
|
+
alpha.lessThan(minAlpha).discard();
|
|
25633
|
+
if (!sorted) {
|
|
25634
|
+
N.If(stochastic.or(depthOnly), () => {
|
|
25635
|
+
const pixel = N.uvec2(N.screenCoordinate.xy.sub(vViewportOrigin));
|
|
25636
|
+
const offset = N.uvec2(
|
|
25637
|
+
vStochasticHash,
|
|
25638
|
+
vStochasticHash.shiftRight(5)
|
|
25639
|
+
).add(
|
|
25640
|
+
N.select(
|
|
25641
|
+
stochastic.and(depthOnly.not()),
|
|
25642
|
+
N.uvec2(temporalSample.zw),
|
|
25643
|
+
N.uvec2(0)
|
|
25644
|
+
)
|
|
25645
|
+
);
|
|
25646
|
+
const coord = N.ivec2(
|
|
25647
|
+
pixel.x.add(offset.x).bitAnd(31),
|
|
25648
|
+
pixel.y.add(offset.y).bitAnd(31)
|
|
25649
|
+
);
|
|
25650
|
+
const randomValue = N.float(load2D(stochasticNoise, coord).r).add(0.5).div(1024);
|
|
25651
|
+
randomValue.greaterThanEqual(alpha).discard();
|
|
25652
|
+
});
|
|
25653
|
+
}
|
|
25654
|
+
const rgba = N.vec4(
|
|
25655
|
+
N.unpackHalf2x16(vSplat.x),
|
|
25656
|
+
blueKernelPower.x,
|
|
25657
|
+
alpha
|
|
25658
|
+
).toVar();
|
|
25637
25659
|
if (sorted) return rgba;
|
|
25638
|
-
N.If(stochastic.or(depthOnly), () => {
|
|
25639
|
-
const pixel = N.uvec2(N.screenCoordinate.xy.sub(vViewportOrigin));
|
|
25640
|
-
const offset = N.uvec2(
|
|
25641
|
-
vStochasticHash,
|
|
25642
|
-
vStochasticHash.shiftRight(5)
|
|
25643
|
-
).add(
|
|
25644
|
-
N.select(
|
|
25645
|
-
stochastic.and(depthOnly.not()),
|
|
25646
|
-
N.uvec2(temporalSample.zw),
|
|
25647
|
-
N.uvec2(0)
|
|
25648
|
-
)
|
|
25649
|
-
);
|
|
25650
|
-
const coord = N.ivec2(
|
|
25651
|
-
pixel.x.add(offset.x).bitAnd(31),
|
|
25652
|
-
pixel.y.add(offset.y).bitAnd(31)
|
|
25653
|
-
);
|
|
25654
|
-
const randomValue = N.float(load2D(stochasticNoise, coord).r).add(0.5).div(1024);
|
|
25655
|
-
randomValue.greaterThanEqual(rgba.a).discard();
|
|
25656
|
-
});
|
|
25657
25660
|
N.If(stochastic.and(depthOnly.not()), () => {
|
|
25658
25661
|
N.If(premultipliedAlpha.and(stochasticResolve), () => {
|
|
25659
25662
|
rgba.rgb.mulAssign(0.5);
|
|
@@ -25663,15 +25666,22 @@ function createSplatFragment({
|
|
|
25663
25666
|
return rgba;
|
|
25664
25667
|
})();
|
|
25665
25668
|
return {
|
|
25666
|
-
|
|
25669
|
+
vSplat,
|
|
25667
25670
|
vSplatUv,
|
|
25668
25671
|
vStochasticHash,
|
|
25669
|
-
vSupportRadiusSquared,
|
|
25670
|
-
vKernelPower,
|
|
25671
25672
|
vViewportOrigin,
|
|
25672
25673
|
fragmentNode
|
|
25673
25674
|
};
|
|
25674
25675
|
}
|
|
25676
|
+
function packSplatVarying(rgba, supportRadiusSquared, kernelPower) {
|
|
25677
|
+
const rgb = rgba.rgb.min(65504);
|
|
25678
|
+
return N.uvec4(
|
|
25679
|
+
N.packHalf2x16(rgb.xy),
|
|
25680
|
+
N.packHalf2x16(N.vec2(rgb.z, kernelPower)),
|
|
25681
|
+
N.floatBitsToUint(rgba.a),
|
|
25682
|
+
N.floatBitsToUint(supportRadiusSquared)
|
|
25683
|
+
);
|
|
25684
|
+
}
|
|
25675
25685
|
function createSplatNodeMaterial({
|
|
25676
25686
|
uniforms,
|
|
25677
25687
|
orderingNode: providedOrderingNode,
|
|
@@ -25707,15 +25717,7 @@ function createSplatNodeMaterial({
|
|
|
25707
25717
|
"bool"
|
|
25708
25718
|
);
|
|
25709
25719
|
const depthOnly = uniformBinding(uniforms, "depthOnly", "bool");
|
|
25710
|
-
const {
|
|
25711
|
-
vRgba,
|
|
25712
|
-
vSplatUv,
|
|
25713
|
-
vStochasticHash,
|
|
25714
|
-
vSupportRadiusSquared,
|
|
25715
|
-
vKernelPower,
|
|
25716
|
-
vViewportOrigin,
|
|
25717
|
-
fragmentNode
|
|
25718
|
-
} = createSplatFragment({
|
|
25720
|
+
const { vSplat, vSplatUv, vStochasticHash, vViewportOrigin, fragmentNode } = createSplatFragment({
|
|
25719
25721
|
sorted,
|
|
25720
25722
|
minAlpha,
|
|
25721
25723
|
stochastic,
|
|
@@ -25728,24 +25730,22 @@ function createSplatNodeMaterial({
|
|
|
25728
25730
|
function buildVertex(builder) {
|
|
25729
25731
|
const camera = materialCamera(builder);
|
|
25730
25732
|
const clipPosition = N.vec4(0, 0, 2, 1).toVar();
|
|
25731
|
-
|
|
25733
|
+
vSplat.assign(N.uvec4(0));
|
|
25732
25734
|
vSplatUv.assign(N.vec2(0));
|
|
25733
25735
|
vStochasticHash.assign(N.uint(0));
|
|
25734
|
-
vSupportRadiusSquared.assign(0);
|
|
25735
|
-
vKernelPower.assign(0);
|
|
25736
25736
|
const assignVertexData = (data) => {
|
|
25737
25737
|
const rgba = data.rgba.toVar();
|
|
25738
25738
|
N.If(encodeLinear.and(depthOnly.not()), () => {
|
|
25739
25739
|
rgba.rgb.assign(N.sRGBTransferEOTF(rgba.rgb));
|
|
25740
25740
|
});
|
|
25741
25741
|
clipPosition.assign(data.clipPosition);
|
|
25742
|
-
|
|
25742
|
+
vSplat.assign(
|
|
25743
|
+
packSplatVarying(rgba, data.supportRadiusSquared, data.kernelPower)
|
|
25744
|
+
);
|
|
25743
25745
|
vSplatUv.assign(data.splatUv);
|
|
25744
25746
|
N.If(stochastic.or(depthOnly), () => {
|
|
25745
25747
|
vStochasticHash.assign(stochasticHash(data.stochasticSeed));
|
|
25746
25748
|
});
|
|
25747
|
-
vSupportRadiusSquared.assign(data.supportRadiusSquared);
|
|
25748
|
-
vKernelPower.assign(data.kernelPower);
|
|
25749
25749
|
vViewportOrigin.assign(data.viewportOrigin);
|
|
25750
25750
|
};
|
|
25751
25751
|
if (vertexData) {
|
|
@@ -29261,15 +29261,14 @@ function createTAAPresentation(renderer, views) {
|
|
|
29261
29261
|
const depth2 = N.textureLoad(uniforms.depth.value).onObjectUpdate(
|
|
29262
29262
|
() => uniforms.depth.value
|
|
29263
29263
|
);
|
|
29264
|
-
material.fragmentNode = load2D(color2, coord);
|
|
29265
29264
|
material.depthNode = load2D(depth2, coord).r;
|
|
29266
29265
|
material.vertexNode = N.vec4(N.positionGeometry.xy, 0, 1);
|
|
29267
29266
|
const output2 = N.renderOutput(
|
|
29268
|
-
|
|
29267
|
+
load2D(color2, coord),
|
|
29269
29268
|
THREE__namespace.NoToneMapping,
|
|
29270
29269
|
THREE__namespace.NoColorSpace
|
|
29271
29270
|
);
|
|
29272
|
-
material.
|
|
29271
|
+
material.fragmentNode = output2;
|
|
29273
29272
|
configureOutput = (tone, space) => {
|
|
29274
29273
|
if (output2.getToneMapping() !== tone || output2.outputColorSpace !== space) {
|
|
29275
29274
|
output2.setToneMapping(tone);
|
|
@@ -29367,9 +29366,9 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29367
29366
|
() => history[1 - index2].textures[1]
|
|
29368
29367
|
);
|
|
29369
29368
|
const targets = [sourceTarget, ...history];
|
|
29370
|
-
const
|
|
29371
|
-
const
|
|
29372
|
-
const
|
|
29369
|
+
const previousVP = new THREE__namespace.Matrix4();
|
|
29370
|
+
const projectionMatrix = N.uniform(camera.projectionMatrix);
|
|
29371
|
+
const viewToPreviousClip = N.uniform(new THREE__namespace.Matrix4());
|
|
29373
29372
|
const jitter = N.uniform(sample2);
|
|
29374
29373
|
const size = N.uniform(renderSize);
|
|
29375
29374
|
const useHistory = N.uniform(false, "bool");
|
|
@@ -29377,16 +29376,12 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29377
29376
|
const sampleCount = N.property("float", "taaHistorySamples");
|
|
29378
29377
|
const logDepth = N.uniform(new THREE__namespace.Vector2());
|
|
29379
29378
|
const depthProjection = N.uniform(new THREE__namespace.Vector2());
|
|
29379
|
+
const logarithmic = logDepth.x.greaterThan(0);
|
|
29380
29380
|
const reversed = renderer.reversedDepthBuffer;
|
|
29381
29381
|
const zeroToOne = reversed || renderer.coordinateSystem === THREE__namespace.WebGPUCoordinateSystem;
|
|
29382
|
-
const
|
|
29383
|
-
|
|
29384
|
-
|
|
29385
|
-
const viewZ = logDepth.x.negate().mul(encoded.mul(logDepth.y).exp2());
|
|
29386
|
-
value.assign(depthProjection.x.add(depthProjection.y.div(viewZ)));
|
|
29387
|
-
});
|
|
29388
|
-
return value;
|
|
29389
|
-
});
|
|
29382
|
+
const logarithmicViewZ = N.Fn(
|
|
29383
|
+
([encoded]) => logDepth.x.negate().mul(encoded.mul(logDepth.y).exp2())
|
|
29384
|
+
);
|
|
29390
29385
|
const pixel = N.ivec2(N.screenCoordinate.xy);
|
|
29391
29386
|
const material = new NodeMaterial();
|
|
29392
29387
|
material.vertexNode = N.vec4(N.positionGeometry.xy, 0, 1);
|
|
@@ -29400,7 +29395,8 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29400
29395
|
const marked = N.bool(false).toVar();
|
|
29401
29396
|
const low = current.toVar();
|
|
29402
29397
|
const high = current.toVar();
|
|
29403
|
-
const
|
|
29398
|
+
const reverseSelection = reversed ? logarithmic.not() : N.bool(false);
|
|
29399
|
+
const closest = N.select(reverseSelection, 0, 1).toVar();
|
|
29404
29400
|
const closestPixel = pixel.toVar();
|
|
29405
29401
|
for (let y = -1; y <= 1; y++) {
|
|
29406
29402
|
for (let x = -1; x <= 1; x++) {
|
|
@@ -29411,11 +29407,18 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29411
29407
|
const bounded = x !== 0 || y !== 0 ? color2.max(0) : color2;
|
|
29412
29408
|
low.assign(low.min(bounded));
|
|
29413
29409
|
high.assign(high.max(bounded));
|
|
29414
|
-
const z =
|
|
29415
|
-
N.If(
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
|
|
29410
|
+
const z = load2D(depth2, p).r.toVar();
|
|
29411
|
+
N.If(
|
|
29412
|
+
N.select(
|
|
29413
|
+
reverseSelection,
|
|
29414
|
+
z.greaterThan(closest),
|
|
29415
|
+
z.lessThan(closest)
|
|
29416
|
+
),
|
|
29417
|
+
() => {
|
|
29418
|
+
closest.assign(z);
|
|
29419
|
+
closestPixel.assign(p);
|
|
29420
|
+
}
|
|
29421
|
+
);
|
|
29419
29422
|
}
|
|
29420
29423
|
}
|
|
29421
29424
|
N.If(marked, () => {
|
|
@@ -29425,10 +29428,20 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29425
29428
|
N.If(load2D(source, closestPixel).a.greaterThan(1), () => {
|
|
29426
29429
|
ndc.subAssign(jitter.xy);
|
|
29427
29430
|
});
|
|
29428
|
-
const
|
|
29429
|
-
|
|
29430
|
-
);
|
|
29431
|
-
const
|
|
29431
|
+
const p = projectionMatrix;
|
|
29432
|
+
const pz = p.element(2);
|
|
29433
|
+
const pw = p.element(3);
|
|
29434
|
+
const z = zeroToOne ? closest : closest.mul(2).sub(1);
|
|
29435
|
+
const orientation = reversed ? -1 : 1;
|
|
29436
|
+
const viewZ = pw.z.sub(z.mul(pw.w)).mul(orientation).toVar();
|
|
29437
|
+
const viewW = z.mul(pz.w).sub(pz.z).mul(orientation).toVar();
|
|
29438
|
+
N.If(logarithmic, () => {
|
|
29439
|
+
viewZ.assign(logarithmicViewZ(closest));
|
|
29440
|
+
viewW.assign(1);
|
|
29441
|
+
});
|
|
29442
|
+
const clipW = pz.w.mul(viewZ).add(pw.w.mul(viewW));
|
|
29443
|
+
const viewXY = ndc.mul(clipW).sub(pz.xy.mul(viewZ)).sub(pw.xy.mul(viewW)).div(N.vec2(p.element(0).x, p.element(1).y));
|
|
29444
|
+
const projected = viewToPreviousClip.mul(N.vec4(viewXY, viewZ, viewW)).toVar();
|
|
29432
29445
|
N.If(projected.w.greaterThan(0), () => {
|
|
29433
29446
|
const previous = projected.xyz.div(projected.w);
|
|
29434
29447
|
const previousUV = uv2.add(previous.xy.sub(ndc).mul(N.vec2(0.5, -0.5))).toVar();
|
|
@@ -29446,19 +29459,26 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29446
29459
|
const validWeight = N.float(0).toVar();
|
|
29447
29460
|
for (let y = 0; y < 2; y++) {
|
|
29448
29461
|
for (let x = 0; x < 2; x++) {
|
|
29449
|
-
const
|
|
29462
|
+
const p2 = base.add(N.ivec2(x, y));
|
|
29450
29463
|
const weight = (x === 0 ? N.float(1).sub(fraction.x) : fraction.x).mul(y === 0 ? N.float(1).sub(fraction.y) : fraction.y);
|
|
29451
29464
|
N.If(
|
|
29452
|
-
weight.greaterThan(0).and(
|
|
29465
|
+
weight.greaterThan(0).and(p2.x.greaterThanEqual(0)).and(p2.y.greaterThanEqual(0)).and(p2.x.lessThan(N.int(size.x))).and(p2.y.lessThan(N.int(size.y))),
|
|
29453
29466
|
() => {
|
|
29454
|
-
const oldDepth =
|
|
29467
|
+
const oldDepth = load2D(historyDepth, p2).r.toVar();
|
|
29468
|
+
N.If(logarithmic, () => {
|
|
29469
|
+
oldDepth.assign(
|
|
29470
|
+
depthProjection.x.add(
|
|
29471
|
+
depthProjection.y.div(logarithmicViewZ(oldDepth))
|
|
29472
|
+
)
|
|
29473
|
+
);
|
|
29474
|
+
});
|
|
29455
29475
|
const disocclusion = reversed ? oldDepth.sub(expectedDepth) : expectedDepth.sub(oldDepth);
|
|
29456
29476
|
N.If(disocclusion.lessThanEqual(5e-4), () => {
|
|
29457
29477
|
historySum.addAssign(
|
|
29458
|
-
load2D(historyColor,
|
|
29478
|
+
load2D(historyColor, p2).mul(weight)
|
|
29459
29479
|
);
|
|
29460
29480
|
sampleSum.addAssign(
|
|
29461
|
-
load2D(historySamples,
|
|
29481
|
+
load2D(historySamples, p2).r.mul(weight)
|
|
29462
29482
|
);
|
|
29463
29483
|
validWeight.addAssign(weight);
|
|
29464
29484
|
});
|
|
@@ -29532,17 +29552,14 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29532
29552
|
renderer.autoClearStencil
|
|
29533
29553
|
);
|
|
29534
29554
|
renderer.render(scene, camera);
|
|
29535
|
-
|
|
29536
|
-
camera.projectionMatrix,
|
|
29537
|
-
camera.matrixWorldInverse
|
|
29538
|
-
);
|
|
29539
|
-
inverseVP.value.copy(currentVP).invert();
|
|
29555
|
+
viewToPreviousClip.value.multiplyMatrices(previousVP, camera.matrixWorld);
|
|
29540
29556
|
stochasticFrame.value = isStochastic();
|
|
29541
29557
|
useHistory.value = stochasticFrame.value && valid;
|
|
29542
29558
|
const perspective = camera;
|
|
29559
|
+
const near = Math.max(perspective.near, 1e-6);
|
|
29543
29560
|
logDepth.value.set(
|
|
29544
|
-
renderer.logarithmicDepthBuffer && perspective.isPerspectiveCamera ?
|
|
29545
|
-
Math.log2(perspective.far /
|
|
29561
|
+
renderer.logarithmicDepthBuffer && perspective.isPerspectiveCamera ? near : 0,
|
|
29562
|
+
Math.log2(perspective.far / near)
|
|
29546
29563
|
);
|
|
29547
29564
|
const projection = camera.projectionMatrix.elements;
|
|
29548
29565
|
const scale = zeroToOne ? 1 : 0.5;
|
|
@@ -29552,7 +29569,10 @@ function createNodeTAAPipeline(renderer, scene, camera, sample2, renderSize, isS
|
|
|
29552
29569
|
);
|
|
29553
29570
|
renderer.setRenderTarget(history[index2]);
|
|
29554
29571
|
renderer.render(mesh, fullscreenCamera);
|
|
29555
|
-
previousVP.
|
|
29572
|
+
previousVP.multiplyMatrices(
|
|
29573
|
+
camera.projectionMatrix,
|
|
29574
|
+
camera.matrixWorldInverse
|
|
29575
|
+
);
|
|
29556
29576
|
index2 = 1 - index2;
|
|
29557
29577
|
valid = true;
|
|
29558
29578
|
},
|
|
@@ -29583,8 +29603,8 @@ uniform sampler2D historyDepth;
|
|
|
29583
29603
|
uniform sampler2D historySamples;
|
|
29584
29604
|
uniform vec2 renderSize;
|
|
29585
29605
|
uniform vec4 sampleOffset;
|
|
29586
|
-
uniform mat4
|
|
29587
|
-
uniform mat4
|
|
29606
|
+
uniform mat4 projection;
|
|
29607
|
+
uniform mat4 viewToPreviousClip;
|
|
29588
29608
|
uniform bool valid;
|
|
29589
29609
|
uniform bool stochasticFrame;
|
|
29590
29610
|
uniform bool reversed;
|
|
@@ -29593,13 +29613,10 @@ uniform vec2 depthProjection;
|
|
|
29593
29613
|
layout(location = 0) out vec4 fragColor;
|
|
29594
29614
|
layout(location = 1) out float fragSamples;
|
|
29595
29615
|
|
|
29596
|
-
float
|
|
29597
|
-
|
|
29598
|
-
float viewZ = -exp2(encoded * logDepth.y) + 1.0;
|
|
29599
|
-
return depthProjection.x + depthProjection.y / viewZ;
|
|
29616
|
+
float logarithmicViewZ(float encoded) {
|
|
29617
|
+
return -exp2(encoded * logDepth.y) + 1.0;
|
|
29600
29618
|
}
|
|
29601
29619
|
|
|
29602
|
-
float clipZ(float depth) { return reversed ? depth : depth * 2.0 - 1.0; }
|
|
29603
29620
|
ivec2 bounded(ivec2 p) { return clamp(p, ivec2(0), ivec2(renderSize) - 1); }
|
|
29604
29621
|
vec4 colorAt(ivec2 p) {
|
|
29605
29622
|
vec4 color = texelFetch(source, bounded(p), 0);
|
|
@@ -29618,7 +29635,10 @@ void main() {
|
|
|
29618
29635
|
bool marked = false;
|
|
29619
29636
|
vec4 low = current;
|
|
29620
29637
|
vec4 high = current;
|
|
29621
|
-
|
|
29638
|
+
// Log depth increases with distance; compare it before any conversion.
|
|
29639
|
+
bool logarithmic = logDepth.x > 0.0;
|
|
29640
|
+
bool reverseSelection = reversed && !logarithmic;
|
|
29641
|
+
float closest = reverseSelection ? 0.0 : 1.0;
|
|
29622
29642
|
ivec2 closestPixel = pixel;
|
|
29623
29643
|
// Match the node backend's top-left traversal when depths are equal.
|
|
29624
29644
|
for (int y = 1; y >= -1; --y) {
|
|
@@ -29630,8 +29650,8 @@ void main() {
|
|
|
29630
29650
|
if (x != 0 || y != 0) color = max(color, vec4(0.0));
|
|
29631
29651
|
low = min(low, color);
|
|
29632
29652
|
high = max(high, color);
|
|
29633
|
-
float z =
|
|
29634
|
-
if (
|
|
29653
|
+
float z = texelFetch(sourceDepth, p, 0).r;
|
|
29654
|
+
if (reverseSelection ? z > closest : z < closest) {
|
|
29635
29655
|
closest = z;
|
|
29636
29656
|
closestPixel = p;
|
|
29637
29657
|
}
|
|
@@ -29642,8 +29662,21 @@ void main() {
|
|
|
29642
29662
|
vec2 closestUV = (vec2(closestPixel) + 0.5) / renderSize;
|
|
29643
29663
|
vec2 ndc = closestUV * 2.0 - 1.0;
|
|
29644
29664
|
if (texelFetch(source, closestPixel, 0).a > 1.0) ndc -= sampleOffset.xy;
|
|
29645
|
-
|
|
29646
|
-
|
|
29665
|
+
// Reconstruct homogeneous view coordinates without inverse VP cancellation.
|
|
29666
|
+
float z = reversed ? closest : closest * 2.0 - 1.0;
|
|
29667
|
+
// Keep W positive in both depth modes; W = 0 represents a point at infinity.
|
|
29668
|
+
float orientation = reversed ? -1.0 : 1.0;
|
|
29669
|
+
float viewZ = (projection[3][2] - z * projection[3][3]) * orientation;
|
|
29670
|
+
float viewW = (z * projection[2][3] - projection[2][2]) * orientation;
|
|
29671
|
+
if (logarithmic) {
|
|
29672
|
+
// Decode log depth directly to retain its precision.
|
|
29673
|
+
viewZ = logarithmicViewZ(closest);
|
|
29674
|
+
viewW = 1.0;
|
|
29675
|
+
}
|
|
29676
|
+
float clipW = projection[2][3] * viewZ + projection[3][3] * viewW;
|
|
29677
|
+
vec2 viewXY = (ndc * clipW - projection[2].xy * viewZ
|
|
29678
|
+
- projection[3].xy * viewW) / vec2(projection[0][0], projection[1][1]);
|
|
29679
|
+
vec4 projected = viewToPreviousClip * vec4(viewXY, viewZ, viewW);
|
|
29647
29680
|
if (projected.w <= 0.0) return;
|
|
29648
29681
|
vec3 previous = projected.xyz / projected.w;
|
|
29649
29682
|
vec2 previousUV = uv + (previous.xy - ndc) * 0.5;
|
|
@@ -29663,7 +29696,8 @@ void main() {
|
|
|
29663
29696
|
float weight = (x == 0 ? 1.0 - fraction.x : fraction.x)
|
|
29664
29697
|
* (y == 0 ? 1.0 - fraction.y : fraction.y);
|
|
29665
29698
|
if (weight > 0.0 && all(greaterThanEqual(p, ivec2(0))) && all(lessThan(p, ivec2(renderSize)))) {
|
|
29666
|
-
float oldDepth =
|
|
29699
|
+
float oldDepth = texelFetch(historyDepth, p, 0).r;
|
|
29700
|
+
if (logarithmic) oldDepth = depthProjection.x + depthProjection.y / logarithmicViewZ(oldDepth);
|
|
29667
29701
|
float disocclusion = reversed ? oldDepth - expectedDepth : expectedDepth - oldDepth;
|
|
29668
29702
|
if (disocclusion <= 0.0005) {
|
|
29669
29703
|
historySum += texelFetch(history, p, 0) * weight;
|
|
@@ -29710,8 +29744,7 @@ function createWebGLTAAPipeline(renderer, scene, camera, sample2, renderSize, is
|
|
|
29710
29744
|
target.textures.push(samples);
|
|
29711
29745
|
});
|
|
29712
29746
|
const targets = [source, ...history];
|
|
29713
|
-
const
|
|
29714
|
-
const previousVP = { value: new THREE__namespace.Matrix4() };
|
|
29747
|
+
const previousVP = new THREE__namespace.Matrix4();
|
|
29715
29748
|
const reversed = renderer.capabilities.reversedDepthBuffer;
|
|
29716
29749
|
const uniforms = {
|
|
29717
29750
|
source: { value: source.texture },
|
|
@@ -29721,8 +29754,8 @@ function createWebGLTAAPipeline(renderer, scene, camera, sample2, renderSize, is
|
|
|
29721
29754
|
historySamples: { value: history[0].textures[1] },
|
|
29722
29755
|
renderSize: { value: renderSize },
|
|
29723
29756
|
sampleOffset: { value: sample2 },
|
|
29724
|
-
|
|
29725
|
-
|
|
29757
|
+
projection: { value: camera.projectionMatrix },
|
|
29758
|
+
viewToPreviousClip: { value: new THREE__namespace.Matrix4() },
|
|
29726
29759
|
valid: { value: false },
|
|
29727
29760
|
stochasticFrame: { value: false },
|
|
29728
29761
|
reversed: { value: reversed },
|
|
@@ -29776,16 +29809,15 @@ function createWebGLTAAPipeline(renderer, scene, camera, sample2, renderSize, is
|
|
|
29776
29809
|
renderer.autoClearStencil
|
|
29777
29810
|
);
|
|
29778
29811
|
renderer.render(scene, camera);
|
|
29779
|
-
currentVP.multiplyMatrices(
|
|
29780
|
-
camera.projectionMatrix,
|
|
29781
|
-
camera.matrixWorldInverse
|
|
29782
|
-
);
|
|
29783
29812
|
const input = history[1 - index2];
|
|
29784
29813
|
const output2 = history[index2];
|
|
29785
29814
|
uniforms.history.value = input.texture;
|
|
29786
29815
|
uniforms.historyDepth.value = input.depthTexture;
|
|
29787
29816
|
uniforms.historySamples.value = input.textures[1];
|
|
29788
|
-
uniforms.
|
|
29817
|
+
uniforms.viewToPreviousClip.value.multiplyMatrices(
|
|
29818
|
+
previousVP,
|
|
29819
|
+
camera.matrixWorld
|
|
29820
|
+
);
|
|
29789
29821
|
const stochastic = isStochastic();
|
|
29790
29822
|
uniforms.valid.value = stochastic && valid;
|
|
29791
29823
|
uniforms.stochasticFrame.value = stochastic;
|
|
@@ -29802,7 +29834,10 @@ function createWebGLTAAPipeline(renderer, scene, camera, sample2, renderSize, is
|
|
|
29802
29834
|
);
|
|
29803
29835
|
renderer.setRenderTarget(output2);
|
|
29804
29836
|
renderer.render(mesh, fullscreenCamera);
|
|
29805
|
-
previousVP.
|
|
29837
|
+
previousVP.multiplyMatrices(
|
|
29838
|
+
camera.projectionMatrix,
|
|
29839
|
+
camera.matrixWorldInverse
|
|
29840
|
+
);
|
|
29806
29841
|
index2 = 1 - index2;
|
|
29807
29842
|
valid = true;
|
|
29808
29843
|
},
|
|
@@ -29814,7 +29849,8 @@ function createWebGLTAAPipeline(renderer, scene, camera, sample2, renderSize, is
|
|
|
29814
29849
|
}
|
|
29815
29850
|
};
|
|
29816
29851
|
}
|
|
29817
|
-
const
|
|
29852
|
+
const NOISE_PHASES = [0, 4, 1, 6, 2, 7, 3, 5];
|
|
29853
|
+
const SAMPLES = NOISE_PHASES.length;
|
|
29818
29854
|
function halton(index2, base) {
|
|
29819
29855
|
let result = 0;
|
|
29820
29856
|
let fraction = 1;
|
|
@@ -29991,6 +30027,7 @@ class StochasticTAAPass {
|
|
|
29991
30027
|
const xrEnabled = renderer.xr.enabled;
|
|
29992
30028
|
const autoClear = renderer.autoClear;
|
|
29993
30029
|
const index2 = this.frame % SAMPLES;
|
|
30030
|
+
const noisePhase = NOISE_PHASES[index2];
|
|
29994
30031
|
try {
|
|
29995
30032
|
renderer.xr.enabled = false;
|
|
29996
30033
|
for (const view of this.views) {
|
|
@@ -29998,8 +30035,8 @@ class StochasticTAAPass {
|
|
|
29998
30035
|
sample2.set(
|
|
29999
30036
|
2 * (halton(index2 + 1, 2) - 0.5) / size.x,
|
|
30000
30037
|
2 * (halton(index2 + 1, 3) - 0.5) / size.y,
|
|
30001
|
-
|
|
30002
|
-
|
|
30038
|
+
noisePhase * 17 % 32,
|
|
30039
|
+
noisePhase * 29 % 32
|
|
30003
30040
|
);
|
|
30004
30041
|
if (!stochastic) sample2.set(0, 0, 0, 0);
|
|
30005
30042
|
this.splatRenderer[stochasticTemporalSample](sample2);
|