gaussian-splat-lite 1.4.0 → 1.4.1
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 +21 -2
- package/README.md +3 -6
- package/dist/gaussian-splat-lite.cjs +376 -784
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +217 -287
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +378 -786
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +2558 -2850
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/dist/types/addons/NeuralDenoiseNode.d.cts +1 -2
- package/dist/types/addons/NeuralDenoiseNode.d.ts +1 -2
- package/dist/types/addons/NeuralDenoiseXR.d.cts +31 -0
- package/dist/types/addons/NeuralDenoiseXR.d.ts +31 -0
- package/dist/types/addons/neuralDenoiseState.d.cts +22 -0
- package/dist/types/addons/neuralDenoiseState.d.ts +22 -0
- package/dist/types/addons/neuralDenoiseWeights.d.cts +41 -2
- package/dist/types/addons/neuralDenoiseWeights.d.ts +41 -2
- package/dist/types/index.d.cts +1 -2
- package/dist/types/index.d.ts +1 -2
- package/docs/GaussianSplatRenderer.md +2 -2
- package/docs/NeuralDenoiseNode.md +32 -13
- package/docs/StochasticRendering.md +20 -42
- package/package.json +1 -1
- package/dist/types/addons/TAANode.d.cts +0 -49
- package/dist/types/addons/TAANode.d.ts +0 -49
- package/dist/types/addons/TAAPass.d.cts +0 -57
- package/dist/types/addons/TAAPass.d.ts +0 -57
- package/dist/types/addons/taaShared.d.cts +0 -74
- package/dist/types/addons/taaShared.d.ts +0 -74
- package/docs/TAAPass.md +0 -184
|
@@ -4757,37 +4757,16 @@ class WebGLSplatBackend {
|
|
|
4757
4757
|
this.stochasticMaterial.dispose();
|
|
4758
4758
|
}
|
|
4759
4759
|
}
|
|
4760
|
-
function
|
|
4761
|
-
const uniforms = {
|
|
4762
|
-
renderSize: makeUniform(new THREE.Vector2(1, 1)),
|
|
4763
|
-
projection: makeUniform(new THREE.Matrix4()),
|
|
4764
|
-
unjitteredProjection: makeUniform(new THREE.Matrix4()),
|
|
4765
|
-
previousProjection: makeUniform(new THREE.Matrix4()),
|
|
4766
|
-
viewToPreviousClip: makeUniform(new THREE.Matrix4()),
|
|
4767
|
-
previousViewToView: makeUniform(new THREE.Matrix4()),
|
|
4768
|
-
valid: makeUniform(false),
|
|
4769
|
-
reversed: makeUniform(false),
|
|
4770
|
-
logarithmic: makeUniform(false),
|
|
4771
|
-
// Log-depth parameters: x is the current frame, y is the previous frame.
|
|
4772
|
-
logFar: makeUniform(new THREE.Vector2()),
|
|
4773
|
-
logNear: makeUniform(new THREE.Vector2()),
|
|
4774
|
-
// Parameters are copied from the owner before each resolve.
|
|
4775
|
-
depthThreshold: makeUniform(0),
|
|
4776
|
-
edgeDepthDiff: makeUniform(0),
|
|
4777
|
-
maxMotionLength: makeUniform(0),
|
|
4778
|
-
useSubpixelCorrection: makeUniform(false),
|
|
4779
|
-
luminanceCoefficients: makeUniform(new THREE.Vector3())
|
|
4780
|
-
};
|
|
4760
|
+
function createNeuralDenoiseState(targets, margin) {
|
|
4781
4761
|
const baseProjection = new THREE.Matrix4();
|
|
4782
|
-
const previousViewProjection = new THREE.Matrix4();
|
|
4783
|
-
const previousWorld = new THREE.Matrix4();
|
|
4784
|
-
const viewMatrix = new THREE.Matrix4();
|
|
4785
4762
|
return {
|
|
4786
|
-
|
|
4763
|
+
size: new THREE.Vector2(1, 1),
|
|
4764
|
+
projection: new THREE.Matrix4(),
|
|
4765
|
+
valid: false,
|
|
4787
4766
|
historyIndex: 0,
|
|
4788
4767
|
jitterIndex: 0,
|
|
4789
4768
|
reset() {
|
|
4790
|
-
|
|
4769
|
+
this.valid = false;
|
|
4791
4770
|
this.jitterIndex = 0;
|
|
4792
4771
|
},
|
|
4793
4772
|
setSize(width, height) {
|
|
@@ -4797,10 +4776,9 @@ function createTAAState(makeUniform, targets, nodeLogDepth = false, margin = 0)
|
|
|
4797
4776
|
if (source.width === w + margin && source.height === h + margin) return;
|
|
4798
4777
|
source.setSize(w + margin, h + margin);
|
|
4799
4778
|
for (const target of others) target.setSize(w, h);
|
|
4800
|
-
|
|
4779
|
+
this.size.set(w, h);
|
|
4801
4780
|
this.reset();
|
|
4802
4781
|
},
|
|
4803
|
-
// Only the capture target takes stencil; history stores plain depth.
|
|
4804
4782
|
setStencil(stencil) {
|
|
4805
4783
|
const source = targets[0];
|
|
4806
4784
|
const depthTexture = source.depthTexture;
|
|
@@ -4814,50 +4792,18 @@ function createTAAState(makeUniform, targets, nodeLogDepth = false, margin = 0)
|
|
|
4814
4792
|
camera,
|
|
4815
4793
|
baseProjection,
|
|
4816
4794
|
this.jitterIndex,
|
|
4817
|
-
|
|
4818
|
-
|
|
4795
|
+
this.size.x,
|
|
4796
|
+
this.size.y
|
|
4819
4797
|
);
|
|
4820
4798
|
},
|
|
4821
4799
|
endCapture(camera) {
|
|
4822
|
-
|
|
4800
|
+
this.projection.copy(camera.projectionMatrix);
|
|
4823
4801
|
restoreProjection(camera, baseProjection);
|
|
4824
4802
|
},
|
|
4825
|
-
|
|
4826
|
-
const near = Math.max(camera.near, 1e-6);
|
|
4827
|
-
uniforms.logFar.value.x = Math.log2(
|
|
4828
|
-
nodeLogDepth ? camera.far / near : camera.far + 1
|
|
4829
|
-
);
|
|
4830
|
-
uniforms.logNear.value.x = near;
|
|
4831
|
-
uniforms.unjitteredProjection.value.copy(camera.projectionMatrix);
|
|
4832
|
-
uniforms.viewToPreviousClip.value.multiplyMatrices(
|
|
4833
|
-
previousViewProjection,
|
|
4834
|
-
camera.matrixWorld
|
|
4835
|
-
);
|
|
4836
|
-
uniforms.previousViewToView.value.multiplyMatrices(
|
|
4837
|
-
viewMatrix.copy(camera.matrixWorld).invert(),
|
|
4838
|
-
previousWorld
|
|
4839
|
-
);
|
|
4840
|
-
uniforms.depthThreshold.value = parameters2.depthThreshold;
|
|
4841
|
-
uniforms.edgeDepthDiff.value = parameters2.edgeDepthDiff;
|
|
4842
|
-
uniforms.maxMotionLength.value = parameters2.maxMotionLength;
|
|
4843
|
-
uniforms.useSubpixelCorrection.value = parameters2.useSubpixelCorrection;
|
|
4844
|
-
THREE.ColorManagement.getLuminanceCoefficients(
|
|
4845
|
-
uniforms.luminanceCoefficients.value
|
|
4846
|
-
);
|
|
4847
|
-
return uniforms.reversed.value ? THREE.NeverDepth : THREE.AlwaysDepth;
|
|
4848
|
-
},
|
|
4849
|
-
advance(camera) {
|
|
4850
|
-
previousViewProjection.multiplyMatrices(
|
|
4851
|
-
camera.projectionMatrix,
|
|
4852
|
-
viewMatrix
|
|
4853
|
-
);
|
|
4854
|
-
previousWorld.copy(camera.matrixWorld);
|
|
4855
|
-
uniforms.previousProjection.value.copy(uniforms.projection.value);
|
|
4856
|
-
uniforms.logFar.value.y = uniforms.logFar.value.x;
|
|
4857
|
-
uniforms.logNear.value.y = uniforms.logNear.value.x;
|
|
4803
|
+
advance() {
|
|
4858
4804
|
this.historyIndex = 1 - this.historyIndex;
|
|
4859
|
-
|
|
4860
|
-
this.jitterIndex = (this.jitterIndex + 1) %
|
|
4805
|
+
this.valid = true;
|
|
4806
|
+
this.jitterIndex = (this.jitterIndex + 1) % jitterOffsets.length;
|
|
4861
4807
|
}
|
|
4862
4808
|
};
|
|
4863
4809
|
}
|
|
@@ -4872,16 +4818,16 @@ function halton(index2, base) {
|
|
|
4872
4818
|
}
|
|
4873
4819
|
return result;
|
|
4874
4820
|
}
|
|
4875
|
-
const
|
|
4821
|
+
const jitterOffsets = Array.from({ length: 32 }, (_, i) => [
|
|
4876
4822
|
halton(i + 1, 2) - 0.5,
|
|
4877
4823
|
halton(i + 1, 3) - 0.5
|
|
4878
4824
|
]);
|
|
4879
4825
|
const projections = /* @__PURE__ */ new WeakMap();
|
|
4880
|
-
const
|
|
4826
|
+
const getUnjitteredProjection = (camera) => projections.get(camera);
|
|
4881
4827
|
function jitterProjection(camera, base, index2, width, height) {
|
|
4882
4828
|
base.copy(camera.projectionMatrix);
|
|
4883
4829
|
projections.set(camera, base);
|
|
4884
|
-
const [x, y] =
|
|
4830
|
+
const [x, y] = jitterOffsets[index2];
|
|
4885
4831
|
const elements = camera.projectionMatrix.elements;
|
|
4886
4832
|
for (let column = 0; column < 16; column += 4) {
|
|
4887
4833
|
elements[column] -= 2 * x / width * elements[column + 3];
|
|
@@ -5983,7 +5929,7 @@ class ProjectedSplats {
|
|
|
5983
5929
|
this.state.sortRadial.value = radial;
|
|
5984
5930
|
const jitter = this.state.projectionJitter.value;
|
|
5985
5931
|
jitter.set(0, 0);
|
|
5986
|
-
const unjittered =
|
|
5932
|
+
const unjittered = getUnjitteredProjection(camera);
|
|
5987
5933
|
const monoProjection = !multiView && unjittered && getProjectionJitter(camera.projectionMatrix, unjittered, jitter) ? unjittered : null;
|
|
5988
5934
|
geometry.setIndirect(this.indirect);
|
|
5989
5935
|
geometry.setSplatCount(Math.max(1, accumulator.numSplats));
|
|
@@ -6896,676 +6842,6 @@ class GaussianSplatRenderer extends THREE__namespace.Mesh {
|
|
|
6896
6842
|
function isOpaqueMaterial(material) {
|
|
6897
6843
|
return material.transparent === false && material.blending === THREE__namespace.NormalBlending && material.alphaToCoverage === false;
|
|
6898
6844
|
}
|
|
6899
|
-
class StochasticCoverage {
|
|
6900
|
-
constructor() {
|
|
6901
|
-
__publicField(this, "materials", /* @__PURE__ */ new Map());
|
|
6902
|
-
}
|
|
6903
|
-
material(source, renderer) {
|
|
6904
|
-
let entry = this.materials.get(source);
|
|
6905
|
-
if (!entry) {
|
|
6906
|
-
const variant = createStochasticSplatVariant(source, renderer);
|
|
6907
|
-
if (!variant) return source;
|
|
6908
|
-
const dispose = () => {
|
|
6909
|
-
source.removeEventListener("dispose", dispose);
|
|
6910
|
-
variant.dispose();
|
|
6911
|
-
this.materials.delete(source);
|
|
6912
|
-
};
|
|
6913
|
-
entry = { variant, state: new MaterialState(source), dispose };
|
|
6914
|
-
this.materials.set(source, entry);
|
|
6915
|
-
source.addEventListener("dispose", dispose);
|
|
6916
|
-
}
|
|
6917
|
-
entry.state.sync(entry.variant, source);
|
|
6918
|
-
return entry.variant;
|
|
6919
|
-
}
|
|
6920
|
-
render(renderer, scene, camera) {
|
|
6921
|
-
if (!usesNativeWebGPU(renderer) && !renderer.getContext().getExtension("OES_sample_variables"))
|
|
6922
|
-
throw new Error(
|
|
6923
|
-
"Stochastic rendering with MSAA requires OES_sample_variables on WebGL2; use msaa = false on this device"
|
|
6924
|
-
);
|
|
6925
|
-
const nodeRenderer = renderer.isWebGPURenderer === true;
|
|
6926
|
-
const previous = nodeRenderer ? renderer.getRenderObjectFunction() : renderer.renderBufferDirect;
|
|
6927
|
-
const draw = previous ?? renderer.renderObject;
|
|
6928
|
-
const materialIndex = nodeRenderer ? 4 : 3;
|
|
6929
|
-
const hook = (...args) => {
|
|
6930
|
-
args[materialIndex] = this.material(args[materialIndex], renderer);
|
|
6931
|
-
return draw.apply(renderer, args);
|
|
6932
|
-
};
|
|
6933
|
-
if (nodeRenderer) renderer.setRenderObjectFunction(hook);
|
|
6934
|
-
else renderer.renderBufferDirect = hook;
|
|
6935
|
-
try {
|
|
6936
|
-
renderer.render(scene, camera);
|
|
6937
|
-
} finally {
|
|
6938
|
-
if (nodeRenderer) renderer.setRenderObjectFunction(previous);
|
|
6939
|
-
else renderer.renderBufferDirect = previous;
|
|
6940
|
-
}
|
|
6941
|
-
}
|
|
6942
|
-
dispose() {
|
|
6943
|
-
for (const entry of this.materials.values()) entry.dispose();
|
|
6944
|
-
}
|
|
6945
|
-
}
|
|
6946
|
-
const vertexShader$1 = (
|
|
6947
|
-
/* glsl */
|
|
6948
|
-
`
|
|
6949
|
-
out vec2 vUv;
|
|
6950
|
-
void main() {
|
|
6951
|
-
vUv = uv;
|
|
6952
|
-
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
6953
|
-
}
|
|
6954
|
-
`
|
|
6955
|
-
);
|
|
6956
|
-
const fragmentShader = (
|
|
6957
|
-
/* glsl */
|
|
6958
|
-
`
|
|
6959
|
-
precision highp float;
|
|
6960
|
-
precision highp int;
|
|
6961
|
-
uniform sampler2D source;
|
|
6962
|
-
uniform sampler2D sourceDepth;
|
|
6963
|
-
uniform sampler2D history;
|
|
6964
|
-
uniform sampler2D historyDepth;
|
|
6965
|
-
uniform vec2 renderSize;
|
|
6966
|
-
uniform mat4 projection;
|
|
6967
|
-
uniform mat4 unjitteredProjection;
|
|
6968
|
-
uniform mat4 previousProjection;
|
|
6969
|
-
uniform mat4 viewToPreviousClip;
|
|
6970
|
-
uniform mat4 previousViewToView;
|
|
6971
|
-
uniform bool valid;
|
|
6972
|
-
uniform bool reversed;
|
|
6973
|
-
uniform bool logarithmic;
|
|
6974
|
-
uniform vec2 logFar;
|
|
6975
|
-
uniform float depthThreshold;
|
|
6976
|
-
uniform float edgeDepthDiff;
|
|
6977
|
-
uniform float maxMotionLength;
|
|
6978
|
-
uniform bool useSubpixelCorrection;
|
|
6979
|
-
uniform vec3 luminanceCoefficients;
|
|
6980
|
-
in vec2 vUv;
|
|
6981
|
-
out vec4 fragColor;
|
|
6982
|
-
|
|
6983
|
-
ivec2 bounded(ivec2 p) {
|
|
6984
|
-
return clamp(p, ivec2(0), ivec2(renderSize) - 1);
|
|
6985
|
-
}
|
|
6986
|
-
|
|
6987
|
-
// Homogeneous view coordinates retain far-depth precision and avoid a
|
|
6988
|
-
// world-space round trip.
|
|
6989
|
-
vec4 viewPosition(vec2 uv, float depth, mat4 p, float logFarValue) {
|
|
6990
|
-
float z = reversed ? depth : depth * 2.0 - 1.0;
|
|
6991
|
-
float orientation = reversed ? -1.0 : 1.0;
|
|
6992
|
-
float viewZ = (p[3][2] - z * p[3][3]) * orientation;
|
|
6993
|
-
float viewW = (z * p[2][3] - p[2][2]) * orientation;
|
|
6994
|
-
if (logarithmic) {
|
|
6995
|
-
viewZ = 1.0 - exp2(depth * logFarValue);
|
|
6996
|
-
viewW = 1.0;
|
|
6997
|
-
}
|
|
6998
|
-
float clipW = p[2][3] * viewZ + p[3][3] * viewW;
|
|
6999
|
-
vec2 viewXY = ((uv * 2.0 - 1.0) * clipW - p[2].xy * viewZ
|
|
7000
|
-
- p[3].xy * viewW) / vec2(p[0][0], p[1][1]);
|
|
7001
|
-
return vec4(viewXY, viewZ, viewW);
|
|
7002
|
-
}
|
|
7003
|
-
|
|
7004
|
-
float forwardDepth(vec4 positionView) {
|
|
7005
|
-
vec4 clip = projection * positionView;
|
|
7006
|
-
float depth = clip.z / clip.w;
|
|
7007
|
-
return reversed ? 1.0 - depth : depth * 0.5 + 0.5;
|
|
7008
|
-
}
|
|
7009
|
-
|
|
7010
|
-
vec4 clipAABB(vec4 center, vec4 historyColor, vec4 extent) {
|
|
7011
|
-
vec4 delta = historyColor - center;
|
|
7012
|
-
vec3 unit = abs(delta.rgb / (extent.rgb + 1e-7));
|
|
7013
|
-
float maxUnit = max(unit.r, max(unit.g, unit.b));
|
|
7014
|
-
return maxUnit > 1.0 ? center + delta / maxUnit : historyColor;
|
|
7015
|
-
}
|
|
7016
|
-
|
|
7017
|
-
vec4 flickerReduction(vec4 currentColor, vec4 historyColor, float currentWeight) {
|
|
7018
|
-
vec3 compressedCurrent = currentColor.rgb / (max(currentColor.r, max(currentColor.g, currentColor.b)) + 1.0);
|
|
7019
|
-
vec3 compressedHistory = historyColor.rgb / (max(historyColor.r, max(historyColor.g, historyColor.b)) + 1.0);
|
|
7020
|
-
float weightCurrent = currentWeight / (dot(compressedCurrent, luminanceCoefficients) + 1.0);
|
|
7021
|
-
float weightHistory = (1.0 - currentWeight) / (dot(compressedHistory, luminanceCoefficients) + 1.0);
|
|
7022
|
-
return (currentColor * weightCurrent + historyColor * weightHistory)
|
|
7023
|
-
/ max(weightCurrent + weightHistory, 0.00001);
|
|
7024
|
-
}
|
|
7025
|
-
|
|
7026
|
-
void main() {
|
|
7027
|
-
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
7028
|
-
vec4 currentColor = texelFetch(source, pixel, 0);
|
|
7029
|
-
fragColor = currentColor;
|
|
7030
|
-
gl_FragDepth = texelFetch(sourceDepth, pixel, 0).r;
|
|
7031
|
-
if (!valid) return;
|
|
7032
|
-
|
|
7033
|
-
float closestDepth = 2.0;
|
|
7034
|
-
float farthestDepth = -1.0;
|
|
7035
|
-
float closestRawDepth = gl_FragDepth;
|
|
7036
|
-
ivec2 closestPixel = pixel;
|
|
7037
|
-
vec4 moment1 = currentColor;
|
|
7038
|
-
vec4 moment2 = currentColor * currentColor;
|
|
7039
|
-
for (int x = -1; x <= 1; ++x) {
|
|
7040
|
-
for (int y = -1; y <= 1; ++y) {
|
|
7041
|
-
ivec2 p = bounded(pixel + ivec2(x, y));
|
|
7042
|
-
float rawDepth = texelFetch(sourceDepth, p, 0).r;
|
|
7043
|
-
float depth = reversed && !logarithmic ? 1.0 - rawDepth : rawDepth;
|
|
7044
|
-
if (depth < closestDepth) {
|
|
7045
|
-
closestDepth = depth;
|
|
7046
|
-
closestRawDepth = rawDepth;
|
|
7047
|
-
closestPixel = p;
|
|
7048
|
-
}
|
|
7049
|
-
farthestDepth = max(farthestDepth, depth);
|
|
7050
|
-
if (x != 0 || y != 0) {
|
|
7051
|
-
vec4 neighbor = max(texelFetch(source, p, 0), vec4(0.0));
|
|
7052
|
-
moment1 += neighbor;
|
|
7053
|
-
moment2 += neighbor * neighbor;
|
|
7054
|
-
}
|
|
7055
|
-
}
|
|
7056
|
-
}
|
|
7057
|
-
|
|
7058
|
-
vec2 closestUV = (vec2(closestPixel) + 0.5) / renderSize;
|
|
7059
|
-
vec4 positionView = viewPosition(closestUV, closestRawDepth, projection, logFar.x);
|
|
7060
|
-
vec4 previousClip = viewToPreviousClip * positionView;
|
|
7061
|
-
if (previousClip.w <= 0.0) return;
|
|
7062
|
-
vec4 currentClip = unjitteredProjection * positionView;
|
|
7063
|
-
// Exclude projection jitter from camera motion.
|
|
7064
|
-
vec2 offsetUV = (currentClip.xy / currentClip.w - previousClip.xy / previousClip.w) * 0.5;
|
|
7065
|
-
vec2 historyUV = vUv - offsetUV;
|
|
7066
|
-
if (any(lessThan(historyUV, vec2(0.0))) || any(greaterThanEqual(historyUV, vec2(1.0)))) return;
|
|
7067
|
-
|
|
7068
|
-
float oldDepth = texture(historyDepth, historyUV).r;
|
|
7069
|
-
vec4 previousView = viewPosition(historyUV, oldDepth, previousProjection, logFar.y);
|
|
7070
|
-
float previousDepth = forwardDepth(previousViewToView * previousView);
|
|
7071
|
-
if (logarithmic) {
|
|
7072
|
-
// Log depth is monotonic; convert only the extrema for depth thresholds.
|
|
7073
|
-
closestDepth = forwardDepth(positionView);
|
|
7074
|
-
float farthestZ = 1.0 - exp2(farthestDepth * logFar.x);
|
|
7075
|
-
farthestDepth = forwardDepth(vec4(0.0, 0.0, farthestZ, 1.0));
|
|
7076
|
-
}
|
|
7077
|
-
bool isEdge = farthestDepth - closestDepth > edgeDepthDiff;
|
|
7078
|
-
bool isDisocclusion = closestDepth - previousDepth > depthThreshold;
|
|
7079
|
-
if (!isEdge && isDisocclusion) return;
|
|
7080
|
-
|
|
7081
|
-
float motionFactor = clamp(length(offsetUV * renderSize) / maxMotionLength, 0.0, 1.0);
|
|
7082
|
-
float currentWeight = 0.05;
|
|
7083
|
-
if (useSubpixelCorrection) {
|
|
7084
|
-
vec2 phase = fract(offsetUV * renderSize);
|
|
7085
|
-
vec2 weight = max(phase, 1.0 - phase);
|
|
7086
|
-
currentWeight += (1.0 - weight.x * weight.y) / 0.75 * 0.25;
|
|
7087
|
-
}
|
|
7088
|
-
currentWeight = clamp(currentWeight + motionFactor, 0.0, 1.0);
|
|
7089
|
-
float gamma = mix(0.5, 1.0, pow(1.0 - motionFactor, 2.0));
|
|
7090
|
-
vec4 mean = moment1 / 9.0;
|
|
7091
|
-
vec4 extent = sqrt(max(moment2 / 9.0 - mean * mean, vec4(0.0))) * gamma;
|
|
7092
|
-
vec4 historyColor = clipAABB(mean, texture(history, historyUV), extent);
|
|
7093
|
-
fragColor = flickerReduction(currentColor, historyColor, currentWeight);
|
|
7094
|
-
}
|
|
7095
|
-
`
|
|
7096
|
-
);
|
|
7097
|
-
function makeTarget(name) {
|
|
7098
|
-
const target = new THREE.WebGLRenderTarget(1, 1, {
|
|
7099
|
-
type: THREE.HalfFloatType,
|
|
7100
|
-
depthTexture: new THREE.DepthTexture(1, 1, THREE.FloatType)
|
|
7101
|
-
});
|
|
7102
|
-
target.texture.name = name;
|
|
7103
|
-
return target;
|
|
7104
|
-
}
|
|
7105
|
-
class TAAPass extends Pass_js.Pass {
|
|
7106
|
-
/** @param msaa Enable 4× MSAA (default false). Fixed for the pass's lifetime. */
|
|
7107
|
-
constructor(scene, camera, msaa = false) {
|
|
7108
|
-
super();
|
|
7109
|
-
this.scene = scene;
|
|
7110
|
-
this.camera = camera;
|
|
7111
|
-
this.msaa = msaa;
|
|
7112
|
-
this._coverage = msaa ? new StochasticCoverage() : null;
|
|
7113
|
-
this.depthThreshold = 5e-4;
|
|
7114
|
-
this.edgeDepthDiff = 1e-3;
|
|
7115
|
-
this.maxMotionLength = 128;
|
|
7116
|
-
this.useSubpixelCorrection = true;
|
|
7117
|
-
this._source = Object.assign(makeTarget("TAAPass.scene"), {
|
|
7118
|
-
isXRRenderTarget: true,
|
|
7119
|
-
samples: msaa ? 4 : 0
|
|
7120
|
-
});
|
|
7121
|
-
this._history = [
|
|
7122
|
-
makeTarget("TAAPass.history0"),
|
|
7123
|
-
makeTarget("TAAPass.history1")
|
|
7124
|
-
];
|
|
7125
|
-
this._clearColor = new THREE.Color();
|
|
7126
|
-
this._state = createTAAState(
|
|
7127
|
-
(value) => ({ value }),
|
|
7128
|
-
[this._source, ...this._history]
|
|
7129
|
-
);
|
|
7130
|
-
this._uniforms = {
|
|
7131
|
-
source: { value: this._source.texture },
|
|
7132
|
-
sourceDepth: { value: this._source.depthTexture },
|
|
7133
|
-
history: { value: this._history[0].texture },
|
|
7134
|
-
historyDepth: { value: this._history[0].depthTexture },
|
|
7135
|
-
...this._state.uniforms
|
|
7136
|
-
};
|
|
7137
|
-
this._resolveMaterial = new THREE.ShaderMaterial({
|
|
7138
|
-
name: "TAAPass.resolve",
|
|
7139
|
-
uniforms: this._uniforms,
|
|
7140
|
-
vertexShader: vertexShader$1,
|
|
7141
|
-
fragmentShader,
|
|
7142
|
-
glslVersion: THREE.GLSL3,
|
|
7143
|
-
blending: THREE.NoBlending,
|
|
7144
|
-
depthTest: true,
|
|
7145
|
-
depthWrite: true,
|
|
7146
|
-
toneMapped: false
|
|
7147
|
-
});
|
|
7148
|
-
this._copyMaterial = new THREE.ShaderMaterial({
|
|
7149
|
-
name: "TAAPass.copy",
|
|
7150
|
-
uniforms: { source: { value: null }, decode: { value: false } },
|
|
7151
|
-
vertexShader: vertexShader$1,
|
|
7152
|
-
// Inverts OutputPass's transfer, so the two round-trip at any alpha.
|
|
7153
|
-
fragmentShader: (
|
|
7154
|
-
/* glsl */
|
|
7155
|
-
`
|
|
7156
|
-
uniform sampler2D source;
|
|
7157
|
-
uniform bool decode;
|
|
7158
|
-
in vec2 vUv;
|
|
7159
|
-
out vec4 fragColor;
|
|
7160
|
-
void main() {
|
|
7161
|
-
fragColor = texture(source, vUv);
|
|
7162
|
-
if (decode) {
|
|
7163
|
-
vec4 color = vec4(max(fragColor.rgb, 0.0), 1.0);
|
|
7164
|
-
fragColor.rgb = sRGBTransferEOTF(color).rgb;
|
|
7165
|
-
}
|
|
7166
|
-
}
|
|
7167
|
-
`
|
|
7168
|
-
),
|
|
7169
|
-
glslVersion: THREE.GLSL3,
|
|
7170
|
-
blending: THREE.NoBlending,
|
|
7171
|
-
depthTest: false,
|
|
7172
|
-
depthWrite: false,
|
|
7173
|
-
toneMapped: false
|
|
7174
|
-
});
|
|
7175
|
-
this._quad = new Pass_js.FullScreenQuad(this._resolveMaterial);
|
|
7176
|
-
}
|
|
7177
|
-
/** Current scene depth, before temporal accumulation. Owned by this pass. */
|
|
7178
|
-
get depthTexture() {
|
|
7179
|
-
return this._source.depthTexture;
|
|
7180
|
-
}
|
|
7181
|
-
/** Jittered projection used to capture depthTexture. Treat as read-only. */
|
|
7182
|
-
get projectionMatrix() {
|
|
7183
|
-
return this._uniforms.projection.value;
|
|
7184
|
-
}
|
|
7185
|
-
/** Resize all internal targets in physical pixels; a size change clears history. */
|
|
7186
|
-
setSize(width, height) {
|
|
7187
|
-
this._state.setSize(width, height);
|
|
7188
|
-
}
|
|
7189
|
-
/** Discard history after a camera cut, scene replacement, or color-space change. */
|
|
7190
|
-
reset() {
|
|
7191
|
-
this._state.reset();
|
|
7192
|
-
}
|
|
7193
|
-
/** Render the scene and resolve TAA into the composer's working-space buffer. */
|
|
7194
|
-
render(renderer, writeBuffer) {
|
|
7195
|
-
var _a2;
|
|
7196
|
-
const { camera, _state: state, _uniforms: uniforms } = this;
|
|
7197
|
-
this.setSize(writeBuffer.width, writeBuffer.height);
|
|
7198
|
-
const colorSpace = renderer.outputColorSpace;
|
|
7199
|
-
if (this._source.texture.colorSpace !== colorSpace) {
|
|
7200
|
-
this._source.texture.colorSpace = colorSpace;
|
|
7201
|
-
this.reset();
|
|
7202
|
-
}
|
|
7203
|
-
state.setStencil(((_a2 = renderer.getContextAttributes()) == null ? void 0 : _a2.stencil) === true);
|
|
7204
|
-
const previousTarget = renderer.getRenderTarget();
|
|
7205
|
-
const autoClear = renderer.autoClear;
|
|
7206
|
-
const xrEnabled = renderer.xr.enabled;
|
|
7207
|
-
const toneMapping = renderer.toneMapping;
|
|
7208
|
-
try {
|
|
7209
|
-
renderer.xr.enabled = false;
|
|
7210
|
-
renderer.autoClear = false;
|
|
7211
|
-
renderer.toneMapping = THREE.NoToneMapping;
|
|
7212
|
-
const reversedDepth = camera.reversedDepth;
|
|
7213
|
-
state.beginCapture(camera);
|
|
7214
|
-
try {
|
|
7215
|
-
renderer.setRenderTarget(this._source);
|
|
7216
|
-
renderer.setClearColor(
|
|
7217
|
-
renderer.getClearColor(this._clearColor),
|
|
7218
|
-
renderer.getClearAlpha()
|
|
7219
|
-
);
|
|
7220
|
-
renderer.clear();
|
|
7221
|
-
if (this._coverage) this._coverage.render(renderer, this.scene, camera);
|
|
7222
|
-
else renderer.render(this.scene, camera);
|
|
7223
|
-
} finally {
|
|
7224
|
-
state.endCapture(camera);
|
|
7225
|
-
if (camera.reversedDepth !== reversedDepth)
|
|
7226
|
-
camera.updateProjectionMatrix();
|
|
7227
|
-
}
|
|
7228
|
-
const input = this._history[state.historyIndex];
|
|
7229
|
-
const output = this._history[1 - state.historyIndex];
|
|
7230
|
-
if (!uniforms.valid.value) {
|
|
7231
|
-
for (const target of this._history) renderer.initRenderTarget(target);
|
|
7232
|
-
}
|
|
7233
|
-
uniforms.history.value = input.texture;
|
|
7234
|
-
uniforms.historyDepth.value = input.depthTexture;
|
|
7235
|
-
uniforms.reversed.value = renderer.capabilities.reversedDepthBuffer;
|
|
7236
|
-
uniforms.logarithmic.value = renderer.capabilities.logarithmicDepthBuffer && "isPerspectiveCamera" in camera && camera.isPerspectiveCamera;
|
|
7237
|
-
this._resolveMaterial.depthFunc = state.prepareResolve(camera, this);
|
|
7238
|
-
renderer.setRenderTarget(output);
|
|
7239
|
-
this._quad.material = this._resolveMaterial;
|
|
7240
|
-
this._quad.render(renderer);
|
|
7241
|
-
this._copyMaterial.uniforms.source.value = output.texture;
|
|
7242
|
-
this._copyMaterial.uniforms.decode.value = !this.renderToScreen && THREE.ColorManagement.getTransfer(colorSpace) === THREE.SRGBTransfer;
|
|
7243
|
-
renderer.setRenderTarget(this.renderToScreen ? null : writeBuffer);
|
|
7244
|
-
this._quad.material = this._copyMaterial;
|
|
7245
|
-
this._quad.render(renderer);
|
|
7246
|
-
state.advance(camera);
|
|
7247
|
-
} finally {
|
|
7248
|
-
renderer.setRenderTarget(previousTarget);
|
|
7249
|
-
renderer.autoClear = autoClear;
|
|
7250
|
-
renderer.xr.enabled = xrEnabled;
|
|
7251
|
-
renderer.toneMapping = toneMapping;
|
|
7252
|
-
}
|
|
7253
|
-
}
|
|
7254
|
-
dispose() {
|
|
7255
|
-
var _a2;
|
|
7256
|
-
(_a2 = this._coverage) == null ? void 0 : _a2.dispose();
|
|
7257
|
-
this._source.dispose();
|
|
7258
|
-
for (const target of this._history) target.dispose();
|
|
7259
|
-
this._resolveMaterial.dispose();
|
|
7260
|
-
this._copyMaterial.dispose();
|
|
7261
|
-
this._quad.dispose();
|
|
7262
|
-
}
|
|
7263
|
-
}
|
|
7264
|
-
class TAANode extends webgpu.Node {
|
|
7265
|
-
static get type() {
|
|
7266
|
-
return "TAANode";
|
|
7267
|
-
}
|
|
7268
|
-
/** @param msaa Enable 4× MSAA (default false). Fixed for the node's lifetime. */
|
|
7269
|
-
constructor(scene, camera, msaa = false) {
|
|
7270
|
-
super("vec4");
|
|
7271
|
-
this.scene = scene;
|
|
7272
|
-
this.camera = camera;
|
|
7273
|
-
this.msaa = msaa;
|
|
7274
|
-
this.coverage = msaa ? new StochasticCoverage() : null;
|
|
7275
|
-
this.depthThreshold = 5e-4;
|
|
7276
|
-
this.edgeDepthDiff = 1e-3;
|
|
7277
|
-
this.maxMotionLength = 128;
|
|
7278
|
-
this.useSubpixelCorrection = true;
|
|
7279
|
-
this.source = this.makeTarget("TAA.scene");
|
|
7280
|
-
this.source.samples = msaa ? 4 : 0;
|
|
7281
|
-
this.source.storeMultisampledColorBuffer = false;
|
|
7282
|
-
this.history = [
|
|
7283
|
-
this.makeTarget("TAA.history0"),
|
|
7284
|
-
this.makeTarget("TAA.history1")
|
|
7285
|
-
];
|
|
7286
|
-
this.historyColor = N__namespace.texture(this.history[0].texture, N__namespace.screenUV);
|
|
7287
|
-
this.historyDepth = N__namespace.texture(this.history[0].depthTexture, N__namespace.screenUV);
|
|
7288
|
-
this.textureNode = N__namespace.passTexture(this, this.history[0].texture);
|
|
7289
|
-
this.material = new webgpu.NodeMaterial();
|
|
7290
|
-
this.quad = new webgpu.QuadMesh(this.material);
|
|
7291
|
-
this.state = createTAAState(
|
|
7292
|
-
N__namespace.uniform,
|
|
7293
|
-
[this.source, ...this.history],
|
|
7294
|
-
true
|
|
7295
|
-
);
|
|
7296
|
-
this.zeroToOne = N__namespace.uniform(true);
|
|
7297
|
-
this.drawingSize = new THREE.Vector2();
|
|
7298
|
-
this.workingColorSpace = THREE.ColorManagement.workingColorSpace;
|
|
7299
|
-
this.pipelineStates = /* @__PURE__ */ new WeakSet();
|
|
7300
|
-
this.pipelineRendering = false;
|
|
7301
|
-
this.capturePending = true;
|
|
7302
|
-
this.updateBeforeType = webgpu.NodeUpdateType.RENDER;
|
|
7303
|
-
this.material.name = "TAA.resolve";
|
|
7304
|
-
this.material.blending = THREE.NoBlending;
|
|
7305
|
-
this.material.depthTest = true;
|
|
7306
|
-
this.material.depthWrite = true;
|
|
7307
|
-
this.material.toneMapped = false;
|
|
7308
|
-
this.quad.name = "TAA.resolve";
|
|
7309
|
-
}
|
|
7310
|
-
makeTarget(name) {
|
|
7311
|
-
const target = new webgpu.RenderTarget(1, 1, {
|
|
7312
|
-
type: THREE.HalfFloatType,
|
|
7313
|
-
depthTexture: new THREE.DepthTexture(1, 1, THREE.FloatType),
|
|
7314
|
-
samples: 0
|
|
7315
|
-
});
|
|
7316
|
-
target.texture.name = name;
|
|
7317
|
-
return target;
|
|
7318
|
-
}
|
|
7319
|
-
get depthTexture() {
|
|
7320
|
-
return this.source.depthTexture;
|
|
7321
|
-
}
|
|
7322
|
-
get projectionMatrix() {
|
|
7323
|
-
return this.state.uniforms.projection.value;
|
|
7324
|
-
}
|
|
7325
|
-
getTextureNode() {
|
|
7326
|
-
return this.textureNode;
|
|
7327
|
-
}
|
|
7328
|
-
setSize(width, height) {
|
|
7329
|
-
this.state.setSize(width, height);
|
|
7330
|
-
}
|
|
7331
|
-
reset() {
|
|
7332
|
-
this.state.reset();
|
|
7333
|
-
}
|
|
7334
|
-
updateBefore(frame) {
|
|
7335
|
-
if (this.pipelineRendering && !this.capturePending) return;
|
|
7336
|
-
const renderer = frame.renderer;
|
|
7337
|
-
if (this.workingColorSpace !== THREE.ColorManagement.workingColorSpace) {
|
|
7338
|
-
this.workingColorSpace = THREE.ColorManagement.workingColorSpace;
|
|
7339
|
-
this.reset();
|
|
7340
|
-
}
|
|
7341
|
-
const rendererState = webgpu.RendererUtils.saveRendererState(renderer);
|
|
7342
|
-
const xrEnabled = renderer.xr.enabled;
|
|
7343
|
-
const { camera, state } = this;
|
|
7344
|
-
const { uniforms } = state;
|
|
7345
|
-
if (this.pipelineRendering) {
|
|
7346
|
-
this.setSize(this.drawingSize.x, this.drawingSize.y);
|
|
7347
|
-
} else if (rendererState.renderTarget) {
|
|
7348
|
-
this.setSize(
|
|
7349
|
-
rendererState.renderTarget.width,
|
|
7350
|
-
rendererState.renderTarget.height
|
|
7351
|
-
);
|
|
7352
|
-
} else {
|
|
7353
|
-
renderer.getDrawingBufferSize(this.drawingSize);
|
|
7354
|
-
this.setSize(this.drawingSize.x, this.drawingSize.y);
|
|
7355
|
-
}
|
|
7356
|
-
state.setStencil(renderer.stencil);
|
|
7357
|
-
try {
|
|
7358
|
-
renderer.xr.enabled = false;
|
|
7359
|
-
renderer.setMRT(null);
|
|
7360
|
-
renderer.setScissorTest(false);
|
|
7361
|
-
renderer.autoClear = true;
|
|
7362
|
-
const reversedDepth = camera.reversedDepth;
|
|
7363
|
-
const coordinateSystem = camera.coordinateSystem;
|
|
7364
|
-
state.beginCapture(camera);
|
|
7365
|
-
try {
|
|
7366
|
-
renderer.setRenderTarget(this.source);
|
|
7367
|
-
if (this.coverage) this.coverage.render(renderer, this.scene, camera);
|
|
7368
|
-
else renderer.render(this.scene, camera);
|
|
7369
|
-
} finally {
|
|
7370
|
-
state.endCapture(camera);
|
|
7371
|
-
if (camera.reversedDepth !== reversedDepth || camera.coordinateSystem !== coordinateSystem)
|
|
7372
|
-
camera.updateProjectionMatrix();
|
|
7373
|
-
}
|
|
7374
|
-
uniforms.reversed.value = renderer.reversedDepthBuffer;
|
|
7375
|
-
this.zeroToOne.value = renderer.coordinateSystem !== THREE.WebGLCoordinateSystem || renderer.reversedDepthBuffer;
|
|
7376
|
-
uniforms.logarithmic.value = renderer.logarithmicDepthBuffer && "isPerspectiveCamera" in camera && camera.isPerspectiveCamera;
|
|
7377
|
-
this.material.depthFunc = state.prepareResolve(camera, this);
|
|
7378
|
-
if (!uniforms.valid.value)
|
|
7379
|
-
for (const target of this.history) renderer.initRenderTarget(target);
|
|
7380
|
-
const previous = this.history[state.historyIndex];
|
|
7381
|
-
this.historyColor.value = previous.texture;
|
|
7382
|
-
this.historyDepth.value = previous.depthTexture;
|
|
7383
|
-
const output = this.history[1 - state.historyIndex];
|
|
7384
|
-
renderer.setRenderObjectFunction(null);
|
|
7385
|
-
renderer.setRenderTarget(output);
|
|
7386
|
-
this.quad.render(renderer);
|
|
7387
|
-
this.textureNode.value = output.texture;
|
|
7388
|
-
state.advance(camera);
|
|
7389
|
-
this.capturePending = false;
|
|
7390
|
-
} finally {
|
|
7391
|
-
webgpu.RendererUtils.restoreRendererState(renderer, rendererState);
|
|
7392
|
-
renderer.xr.enabled = xrEnabled;
|
|
7393
|
-
}
|
|
7394
|
-
return void 0;
|
|
7395
|
-
}
|
|
7396
|
-
setup(builder) {
|
|
7397
|
-
applyThreeR186Patch(builder.renderer);
|
|
7398
|
-
const pipelineState = builder.context.renderPipelineState;
|
|
7399
|
-
if (pipelineState && !this.pipelineStates.has(pipelineState)) {
|
|
7400
|
-
this.pipelineStates.add(pipelineState);
|
|
7401
|
-
const begin = () => {
|
|
7402
|
-
this.pipelineRendering = true;
|
|
7403
|
-
this.capturePending = true;
|
|
7404
|
-
const renderer = builder.renderer;
|
|
7405
|
-
const target = renderer.getRenderTarget() ?? renderer.getOutputRenderTarget();
|
|
7406
|
-
if (target) this.drawingSize.set(target.width, target.height);
|
|
7407
|
-
else renderer.getDrawingBufferSize(this.drawingSize);
|
|
7408
|
-
};
|
|
7409
|
-
begin();
|
|
7410
|
-
N__namespace.OnBeforeRenderPipeline(begin);
|
|
7411
|
-
N__namespace.OnAfterRenderPipeline(() => {
|
|
7412
|
-
this.pipelineRendering = false;
|
|
7413
|
-
});
|
|
7414
|
-
}
|
|
7415
|
-
const u = this.state.uniforms;
|
|
7416
|
-
const source = N__namespace.texture(this.source.texture, N__namespace.screenUV);
|
|
7417
|
-
const sourceDepth = N__namespace.texture(this.depthTexture, N__namespace.screenUV);
|
|
7418
|
-
const history = this.historyColor;
|
|
7419
|
-
const historyDepth = this.historyDepth;
|
|
7420
|
-
const bounded = (p) => p.clamp(N__namespace.ivec2(0), N__namespace.ivec2(u.renderSize).sub(1));
|
|
7421
|
-
const viewPosition = (uv, depth2, p, far, near) => {
|
|
7422
|
-
const z = N__namespace.select(this.zeroToOne, depth2, depth2.mul(2).sub(1));
|
|
7423
|
-
const orientation = N__namespace.select(u.reversed, -1, 1);
|
|
7424
|
-
const viewZ = p.element(3).z.sub(z.mul(p.element(3).w)).mul(orientation).toVar();
|
|
7425
|
-
const viewW = z.mul(p.element(2).w).sub(p.element(2).z).mul(orientation).toVar();
|
|
7426
|
-
N__namespace.If(u.logarithmic, () => {
|
|
7427
|
-
viewZ.assign(near.mul(depth2.mul(far).exp2()).negate());
|
|
7428
|
-
viewW.assign(1);
|
|
7429
|
-
});
|
|
7430
|
-
const clipW = p.element(2).w.mul(viewZ).add(p.element(3).w.mul(viewW));
|
|
7431
|
-
const xy = uv.mul(2).sub(1).mul(N__namespace.vec2(1, -1)).mul(clipW).sub(p.element(2).xy.mul(viewZ)).sub(p.element(3).xy.mul(viewW)).div(N__namespace.vec2(p.element(0).x, p.element(1).y));
|
|
7432
|
-
return N__namespace.vec4(xy, viewZ, viewW);
|
|
7433
|
-
};
|
|
7434
|
-
const forwardDepth = (position) => {
|
|
7435
|
-
const clip = u.projection.mul(position).toVar();
|
|
7436
|
-
const depth2 = clip.z.div(clip.w);
|
|
7437
|
-
return N__namespace.select(
|
|
7438
|
-
u.reversed,
|
|
7439
|
-
N__namespace.float(1).sub(depth2),
|
|
7440
|
-
N__namespace.select(this.zeroToOne, depth2, depth2.mul(0.5).add(0.5))
|
|
7441
|
-
);
|
|
7442
|
-
};
|
|
7443
|
-
const pixel = N__namespace.ivec2(N__namespace.screenCoordinate.xy);
|
|
7444
|
-
this.material.depthNode = sourceDepth.load(pixel).r;
|
|
7445
|
-
this.material.colorNode = N__namespace.Fn(() => {
|
|
7446
|
-
const currentColor = source.load(pixel).toVar();
|
|
7447
|
-
const result = currentColor.toVar();
|
|
7448
|
-
N__namespace.If(u.valid, () => {
|
|
7449
|
-
const closestDepth = N__namespace.float(2).toVar();
|
|
7450
|
-
const farthestDepth = N__namespace.float(-1).toVar();
|
|
7451
|
-
const closestRawDepth = sourceDepth.load(pixel).r.toVar();
|
|
7452
|
-
const closestPixel = pixel.toVar();
|
|
7453
|
-
const moment1 = currentColor.toVar();
|
|
7454
|
-
const moment2 = currentColor.mul(currentColor).toVar();
|
|
7455
|
-
for (let x = -1; x <= 1; x++) {
|
|
7456
|
-
for (let y = -1; y <= 1; y++) {
|
|
7457
|
-
const p = bounded(pixel.add(N__namespace.ivec2(x, y))).toVar();
|
|
7458
|
-
const rawDepth = sourceDepth.load(p).r.toVar();
|
|
7459
|
-
const depth2 = N__namespace.select(
|
|
7460
|
-
u.reversed.and(u.logarithmic.not()),
|
|
7461
|
-
N__namespace.float(1).sub(rawDepth),
|
|
7462
|
-
rawDepth
|
|
7463
|
-
).toVar();
|
|
7464
|
-
N__namespace.If(depth2.lessThan(closestDepth), () => {
|
|
7465
|
-
closestDepth.assign(depth2);
|
|
7466
|
-
closestRawDepth.assign(rawDepth);
|
|
7467
|
-
closestPixel.assign(p);
|
|
7468
|
-
});
|
|
7469
|
-
farthestDepth.assign(farthestDepth.max(depth2));
|
|
7470
|
-
if (x !== 0 || y !== 0) {
|
|
7471
|
-
const neighbor = source.load(p).max(0).toVar();
|
|
7472
|
-
moment1.addAssign(neighbor);
|
|
7473
|
-
moment2.addAssign(neighbor.mul(neighbor));
|
|
7474
|
-
}
|
|
7475
|
-
}
|
|
7476
|
-
}
|
|
7477
|
-
const closestUV = N__namespace.vec2(closestPixel).add(0.5).div(u.renderSize);
|
|
7478
|
-
const positionView = viewPosition(
|
|
7479
|
-
closestUV,
|
|
7480
|
-
closestRawDepth,
|
|
7481
|
-
u.projection,
|
|
7482
|
-
u.logFar.x,
|
|
7483
|
-
u.logNear.x
|
|
7484
|
-
);
|
|
7485
|
-
const previousClip = u.viewToPreviousClip.mul(positionView).toVar();
|
|
7486
|
-
N__namespace.If(previousClip.w.greaterThan(0), () => {
|
|
7487
|
-
const currentClip = u.unjitteredProjection.mul(positionView).toVar();
|
|
7488
|
-
const offsetUV = currentClip.xy.div(currentClip.w).sub(previousClip.xy.div(previousClip.w)).mul(N__namespace.vec2(0.5, -0.5)).toVar();
|
|
7489
|
-
const historyUV = N__namespace.screenUV.sub(offsetUV).toVar();
|
|
7490
|
-
N__namespace.If(
|
|
7491
|
-
historyUV.greaterThanEqual(0).all().and(historyUV.lessThan(1).all()),
|
|
7492
|
-
() => {
|
|
7493
|
-
const oldDepth = historyDepth.sample(historyUV).r.toVar();
|
|
7494
|
-
const previousView = viewPosition(
|
|
7495
|
-
historyUV,
|
|
7496
|
-
oldDepth,
|
|
7497
|
-
u.previousProjection,
|
|
7498
|
-
u.logFar.y,
|
|
7499
|
-
u.logNear.y
|
|
7500
|
-
);
|
|
7501
|
-
const previousDepth = forwardDepth(
|
|
7502
|
-
u.previousViewToView.mul(previousView)
|
|
7503
|
-
);
|
|
7504
|
-
N__namespace.If(u.logarithmic, () => {
|
|
7505
|
-
closestDepth.assign(forwardDepth(positionView));
|
|
7506
|
-
const farthestZ = u.logNear.x.mul(farthestDepth.mul(u.logFar.x).exp2()).negate();
|
|
7507
|
-
farthestDepth.assign(forwardDepth(N__namespace.vec4(0, 0, farthestZ, 1)));
|
|
7508
|
-
});
|
|
7509
|
-
const isEdge = farthestDepth.sub(closestDepth).greaterThan(u.edgeDepthDiff);
|
|
7510
|
-
const isDisocclusion = closestDepth.sub(previousDepth).greaterThan(u.depthThreshold);
|
|
7511
|
-
N__namespace.If(isEdge.or(isDisocclusion.not()), () => {
|
|
7512
|
-
const motionFactor = offsetUV.mul(u.renderSize).length().div(u.maxMotionLength).clamp(0, 1).toVar();
|
|
7513
|
-
const currentWeight = N__namespace.float(0.05).toVar();
|
|
7514
|
-
N__namespace.If(u.useSubpixelCorrection, () => {
|
|
7515
|
-
const phase = offsetUV.mul(u.renderSize).fract();
|
|
7516
|
-
const weight = phase.max(N__namespace.vec2(1).sub(phase));
|
|
7517
|
-
currentWeight.addAssign(
|
|
7518
|
-
N__namespace.float(1).sub(weight.x.mul(weight.y)).div(0.75).mul(0.25)
|
|
7519
|
-
);
|
|
7520
|
-
});
|
|
7521
|
-
currentWeight.assign(
|
|
7522
|
-
currentWeight.add(motionFactor).clamp(0, 1)
|
|
7523
|
-
);
|
|
7524
|
-
const gamma = N__namespace.mix(
|
|
7525
|
-
0.5,
|
|
7526
|
-
1,
|
|
7527
|
-
N__namespace.float(1).sub(motionFactor).pow(2)
|
|
7528
|
-
);
|
|
7529
|
-
const mean = moment1.div(9).toVar();
|
|
7530
|
-
const extent = moment2.div(9).sub(mean.mul(mean)).max(0).sqrt().mul(gamma);
|
|
7531
|
-
const historyColor = history.sample(historyUV).toVar();
|
|
7532
|
-
const delta = historyColor.sub(mean).toVar();
|
|
7533
|
-
const unit = delta.rgb.div(extent.rgb.add(1e-7)).abs();
|
|
7534
|
-
const maxUnit = unit.x.max(unit.y).max(unit.z);
|
|
7535
|
-
N__namespace.If(maxUnit.greaterThan(1), () => {
|
|
7536
|
-
historyColor.assign(mean.add(delta.div(maxUnit)));
|
|
7537
|
-
});
|
|
7538
|
-
const compressedCurrent = currentColor.rgb.div(
|
|
7539
|
-
currentColor.r.max(currentColor.g).max(currentColor.b).add(1)
|
|
7540
|
-
);
|
|
7541
|
-
const compressedHistory = historyColor.rgb.div(
|
|
7542
|
-
historyColor.r.max(historyColor.g).max(historyColor.b).add(1)
|
|
7543
|
-
);
|
|
7544
|
-
const weightCurrent = currentWeight.div(
|
|
7545
|
-
compressedCurrent.dot(u.luminanceCoefficients).add(1)
|
|
7546
|
-
);
|
|
7547
|
-
const weightHistory = N__namespace.float(1).sub(currentWeight).div(compressedHistory.dot(u.luminanceCoefficients).add(1));
|
|
7548
|
-
result.assign(
|
|
7549
|
-
currentColor.mul(weightCurrent).add(historyColor.mul(weightHistory)).div(weightCurrent.add(weightHistory).max(1e-5))
|
|
7550
|
-
);
|
|
7551
|
-
});
|
|
7552
|
-
}
|
|
7553
|
-
);
|
|
7554
|
-
});
|
|
7555
|
-
});
|
|
7556
|
-
return result;
|
|
7557
|
-
})();
|
|
7558
|
-
return this.textureNode;
|
|
7559
|
-
}
|
|
7560
|
-
dispose() {
|
|
7561
|
-
var _a2;
|
|
7562
|
-
super.dispose();
|
|
7563
|
-
(_a2 = this.coverage) == null ? void 0 : _a2.dispose();
|
|
7564
|
-
this.source.dispose();
|
|
7565
|
-
for (const target of this.history) target.dispose();
|
|
7566
|
-
this.material.dispose();
|
|
7567
|
-
}
|
|
7568
|
-
}
|
|
7569
6845
|
const types = {
|
|
7570
6846
|
f32: "float",
|
|
7571
6847
|
i32: "int",
|
|
@@ -7811,6 +7087,17 @@ fn chroma(c: vec3f) -> vec2f {
|
|
|
7811
7087
|
fn silu(x: vec4f) -> vec4f { return x / (1.0 + exp(-x)); }
|
|
7812
7088
|
fn sigmoid(x: f32) -> f32 { return 1.0 / (1.0 + exp(-x)); }`
|
|
7813
7089
|
);
|
|
7090
|
+
const CATMULL_ROM_WEIGHTS = (
|
|
7091
|
+
/* wgsl */
|
|
7092
|
+
`
|
|
7093
|
+
let center: vec2f = floor(position - 0.5) + 0.5;
|
|
7094
|
+
let t: vec2f = position - center;
|
|
7095
|
+
let w0: vec2f = t * (-0.5 + t * (1.0 - 0.5 * t));
|
|
7096
|
+
let w1: vec2f = 1.0 + t * t * (-2.5 + 1.5 * t);
|
|
7097
|
+
let w2: vec2f = t * (0.5 + t * (2.0 - 1.5 * t));
|
|
7098
|
+
let w3: vec2f = t * t * (-0.5 + 0.5 * t);
|
|
7099
|
+
let w12: vec2f = w1 + w2;`
|
|
7100
|
+
);
|
|
7814
7101
|
const PARAMS = (
|
|
7815
7102
|
/* wgsl */
|
|
7816
7103
|
`
|
|
@@ -7835,6 +7122,7 @@ struct Params {
|
|
|
7835
7122
|
cameraStill: f32, // unchanged view and projection; history stays on the same pixel
|
|
7836
7123
|
offset: vec2f, // where in its target the scene was drawn
|
|
7837
7124
|
colorOffset: vec2f, // zero after encoding, otherwise the scene's offset
|
|
7125
|
+
jitter: vec2f, // where this frame's samples lie, in pixels from pixel centres
|
|
7838
7126
|
};
|
|
7839
7127
|
@group(0) @binding(0) var<uniform> P: Params;
|
|
7840
7128
|
@group(0) @binding(1) var linearSampler: sampler;
|
|
@@ -7904,13 +7192,7 @@ struct Resampled {
|
|
|
7904
7192
|
};
|
|
7905
7193
|
fn catmullRomSpan(image: texture_2d<f32>, uv: vec2f) -> Resampled {
|
|
7906
7194
|
let position: vec2f = uv * P.size;
|
|
7907
|
-
|
|
7908
|
-
let t: vec2f = position - center;
|
|
7909
|
-
let w0: vec2f = t * (-0.5 + t * (1.0 - 0.5 * t));
|
|
7910
|
-
let w1: vec2f = 1.0 + t * t * (-2.5 + 1.5 * t);
|
|
7911
|
-
let w2: vec2f = t * (0.5 + t * (2.0 - 1.5 * t));
|
|
7912
|
-
let w3: vec2f = t * t * (-0.5 + 0.5 * t);
|
|
7913
|
-
let w12: vec2f = w1 + w2;
|
|
7195
|
+
${CATMULL_ROM_WEIGHTS}
|
|
7914
7196
|
let p0: vec2f = (center - 1.0) * P.invSize;
|
|
7915
7197
|
let p12: vec2f = (center + w2 / w12) * P.invSize;
|
|
7916
7198
|
let p3: vec2f = (center + 2.0) * P.invSize;
|
|
@@ -7979,7 +7261,6 @@ fn lanczosKernel(uv: vec2f) -> LanczosKernel {
|
|
|
7979
7261
|
let y: LanczosWeights = lanczosWeights(t.y);
|
|
7980
7262
|
let middle: vec2f = vec2f(x.low.z + x.high.x, y.low.z + y.high.x);
|
|
7981
7263
|
k.centre = (k.base + vec2f(x.high.x, y.high.x) / middle + 0.5) * P.invSize;
|
|
7982
|
-
// All three histories share these 2D tap weights and their normalization.
|
|
7983
7264
|
k.middle = middle.x * middle.y;
|
|
7984
7265
|
k.wx = vec4f(x.low.xy, x.high.yz) * middle.y;
|
|
7985
7266
|
k.wy = vec4f(y.low.xy, y.high.yz) * middle.x;
|
|
@@ -8331,15 +7612,60 @@ ${robust ? ` let under: vec4f = coarse(below, position.xy, 2.0);
|
|
|
8331
7612
|
groups: groups.length
|
|
8332
7613
|
};
|
|
8333
7614
|
}
|
|
8334
|
-
const
|
|
7615
|
+
const MEAN_3X3 = (
|
|
8335
7616
|
/* wgsl */
|
|
8336
7617
|
`
|
|
8337
|
-
// Exact clamped 3x3 mean
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
7618
|
+
// Exact clamped 3x3 mean at a subpixel position, from four bilinear taps.
|
|
7619
|
+
// Each axis has weights (1 - t, 1, 1, t) / 3 over four texels. Pair the
|
|
7620
|
+
// first two and the last two, moving each tap with its pair's weights.
|
|
7621
|
+
fn mean3x3(image: texture_2d<f32>, position: vec2f, offset: vec2f) -> vec4f {
|
|
7622
|
+
let center: vec2f = floor(position - 0.5) + 0.5;
|
|
7623
|
+
let t: vec2f = position - center;
|
|
7624
|
+
let low: vec2f = 2.0 - t;
|
|
7625
|
+
let high: vec2f = 1.0 + t;
|
|
7626
|
+
let size: vec2f = vec2f(textureDimensions(image));
|
|
7627
|
+
let p0: vec2f = (clamp(center - 1.0 + 1.0 / low, vec2f(0.5), P.size - 0.5) + offset) / size;
|
|
7628
|
+
let p1: vec2f = (clamp(center + 1.0 + t / high, vec2f(0.5), P.size - 0.5) + offset) / size;
|
|
7629
|
+
return (textureSampleLevel(image, linearSampler, p0, 0.0) * (low.x * low.y) +
|
|
7630
|
+
textureSampleLevel(image, linearSampler, vec2f(p1.x, p0.y), 0.0) * (high.x * low.y) +
|
|
7631
|
+
textureSampleLevel(image, linearSampler, vec2f(p0.x, p1.y), 0.0) * (low.x * high.y) +
|
|
7632
|
+
textureSampleLevel(image, linearSampler, p1, 0.0) * (high.x * high.y)) / 9.0;
|
|
7633
|
+
}
|
|
7634
|
+
`
|
|
7635
|
+
);
|
|
7636
|
+
const QUALITY_RESAMPLING = (
|
|
7637
|
+
/* wgsl */
|
|
7638
|
+
`
|
|
7639
|
+
// Full Catmull-Rom for the denoised history, retaining all four corner taps.
|
|
7640
|
+
fn catmullRomNine(image: texture_2d<f32>, uv: vec2f) -> vec4f {
|
|
7641
|
+
let position: vec2f = uv * P.size;
|
|
7642
|
+
${CATMULL_ROM_WEIGHTS}
|
|
7643
|
+
let p0: vec2f = (center - 1.0) * P.invSize;
|
|
7644
|
+
let p12: vec2f = (center + w2 / w12) * P.invSize;
|
|
7645
|
+
let p3: vec2f = (center + 2.0) * P.invSize;
|
|
7646
|
+
var result: vec4f = textureSampleLevel(image, linearSampler, p12, 0.0) * (w12.x * w12.y);
|
|
7647
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p0.x, p12.y), 0.0) * (w0.x * w12.y);
|
|
7648
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p3.x, p12.y), 0.0) * (w3.x * w12.y);
|
|
7649
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p12.x, p0.y), 0.0) * (w12.x * w0.y);
|
|
7650
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p12.x, p3.y), 0.0) * (w12.x * w3.y);
|
|
7651
|
+
result += textureSampleLevel(image, linearSampler, p0, 0.0) * (w0.x * w0.y);
|
|
7652
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p3.x, p0.y), 0.0) * (w3.x * w0.y);
|
|
7653
|
+
result += textureSampleLevel(image, linearSampler, vec2f(p0.x, p3.y), 0.0) * (w0.x * w3.y);
|
|
7654
|
+
result += textureSampleLevel(image, linearSampler, p3, 0.0) * (w3.x * w3.y);
|
|
7655
|
+
return result / ((w0.x + w12.x + w3.x) * (w0.y + w12.y + w3.y));
|
|
7656
|
+
}
|
|
7657
|
+
|
|
7658
|
+
// Catmull-Rom of the frame at a position between its pixels, from the five
|
|
7659
|
+
// bilinear taps catmullRomSpan takes, read through the scene's offset.
|
|
7660
|
+
fn frameCatmullRom(position: vec2f) -> vec4f {
|
|
7661
|
+
${CATMULL_ROM_WEIGHTS}
|
|
7662
|
+
let p12: vec2f = center + w2 / w12;
|
|
7663
|
+
var result: vec4f = frameSample(colorTexture, p12) * (w12.x * w12.y);
|
|
7664
|
+
result += frameSample(colorTexture, vec2f(center.x - 1.0, p12.y)) * (w0.x * w12.y);
|
|
7665
|
+
result += frameSample(colorTexture, vec2f(center.x + 2.0, p12.y)) * (w3.x * w12.y);
|
|
7666
|
+
result += frameSample(colorTexture, vec2f(p12.x, center.y - 1.0)) * (w12.x * w0.y);
|
|
7667
|
+
result += frameSample(colorTexture, vec2f(p12.x, center.y + 2.0)) * (w12.x * w3.y);
|
|
7668
|
+
return result / (w12.x * w12.y + (w0.x + w3.x) * w12.y + w12.x * (w0.y + w3.y));
|
|
8343
7669
|
}
|
|
8344
7670
|
`
|
|
8345
7671
|
);
|
|
@@ -8382,7 +7708,7 @@ fn blockFiltered(block: vec2i, color: vec4f) -> vec4f {
|
|
|
8382
7708
|
sum += wx * wy * frameSample(colorTexture, c + vec2f(f32(x), f32(y)) * (11.0 / 6.0));
|
|
8383
7709
|
}
|
|
8384
7710
|
}
|
|
8385
|
-
return mix(color, sum,
|
|
7711
|
+
return mix(color, sum, ${float(weights.compactMix[1])});
|
|
8386
7712
|
}
|
|
8387
7713
|
`
|
|
8388
7714
|
) : (
|
|
@@ -8628,7 +7954,7 @@ ${groups.map(
|
|
|
8628
7954
|
// half-resolution result.
|
|
8629
7955
|
@group(0) @binding(11) var wideTexture: texture_2d<f32>;
|
|
8630
7956
|
@group(0) @binding(12) var depthPrevious: texture_2d<f32>;
|
|
8631
|
-
${weights.compact ?
|
|
7957
|
+
${MEAN_3X3}${weights.compact ? "" : QUALITY_RESAMPLING}${BLOCKS}
|
|
8632
7958
|
// The frame's existing 4x4 mean, used only for rejected surfaces.
|
|
8633
7959
|
@group(0) @binding(13) var rejectedTexture: texture_2d<f32>;
|
|
8634
7960
|
${COARSE}
|
|
@@ -8661,8 +7987,8 @@ struct Output {
|
|
|
8661
7987
|
};
|
|
8662
7988
|
@fragment fn fragment(@builtin(position) position: vec4f) -> Output {
|
|
8663
7989
|
let p: vec2i = vec2i(position.xy);
|
|
8664
|
-
let d: vec4f = ${weights.compact ? "mix(frameLoad(colorTexture, p), compactFiltered(position.xy), 0.5)" : "textureLoad(filteredTexture, p, 0)"};
|
|
8665
7990
|
let color: vec4f = frameLoad(colorTexture, p);
|
|
7991
|
+
${weights.compact ? "" : " let d: vec4f = textureLoad(filteredTexture, p, 0);"}
|
|
8666
7992
|
var sr: vec4f = vec4f(0.0);
|
|
8667
7993
|
var dr: vec4f = vec4f(0.0);
|
|
8668
7994
|
// Second moment of luma, the two history lengths, slow low-pass luma.
|
|
@@ -8676,10 +8002,13 @@ struct Output {
|
|
|
8676
8002
|
var same: f32 = 1.0;
|
|
8677
8003
|
let sparse: f32 = blockSparse(p);
|
|
8678
8004
|
var sparseMotion: f32 = 0.0;
|
|
8005
|
+
${weights.movingMean ? ` // Where the history was, for the accumulated path's mean below.
|
|
8006
|
+
var historyUv: vec2f = position.xy * P.invSize;` : ""}
|
|
8679
8007
|
if (P.historyValid > 0.5) {
|
|
8680
8008
|
let z: f32 = blockDepth(p);
|
|
8681
8009
|
let h: History = reproject(position.xy, z);
|
|
8682
8010
|
valid = h.valid;
|
|
8011
|
+
${weights.movingMean ? " historyUv = h.uv;" : ""}
|
|
8683
8012
|
// The pixel's own motion, or what a turn of the camera sweeps over half
|
|
8684
8013
|
// the view, whichever is more: at the centre of a turn the first is
|
|
8685
8014
|
// small while every surface there shows a new face. An orthographic
|
|
@@ -8707,14 +8036,14 @@ struct Output {
|
|
|
8707
8036
|
a = textureLoad(previousAux, p, 0);
|
|
8708
8037
|
previous.value = textureLoad(previousOutput, p, 0);
|
|
8709
8038
|
} else {
|
|
8710
|
-
${weights.compact ? `
|
|
8711
|
-
|
|
8039
|
+
${weights.compact ? ` // The accumulated history keeps many frames while the camera moves
|
|
8040
|
+
// slowly: read bilinearly, it lost detail with every one of them.
|
|
8041
|
+
sr = max(catmullRom(previousAccumulated, h.uv), vec4f(0.0));
|
|
8042
|
+
dr = max(textureSampleLevel(previousDenoised, linearSampler, h.uv, 0.0), vec4f(0.0));` : ` sr = max(lanczosCross(previousAccumulated, lanczosKernel(h.uv)), vec4f(0.0));
|
|
8043
|
+
// The two short histories take Catmull-Rom: what it blurs over a few
|
|
8044
|
+
// frames is noise more than detail there.
|
|
8045
|
+
dr = max(catmullRomNine(previousDenoised, h.uv), vec4f(0.0));`}
|
|
8712
8046
|
a = previousStatistics(h.uv);
|
|
8713
|
-
// Reuse the sharp display history already read for its clamp span.` : ` let interpolation: LanczosKernel = lanczosKernel(h.uv);
|
|
8714
|
-
sr = max(lanczosCross(previousAccumulated, interpolation), vec4f(0.0));
|
|
8715
|
-
dr = max(lanczosCross(previousDenoised, interpolation), vec4f(0.0));
|
|
8716
|
-
a = previousStatistics(h.uv);
|
|
8717
|
-
previous.value = lanczosCross(previousOutput, interpolation);`}
|
|
8718
8047
|
}
|
|
8719
8048
|
// Where a block has no depth of its own, where to read its history is a
|
|
8720
8049
|
// guess, so while the camera moves it keeps little of the accumulated
|
|
@@ -8725,6 +8054,17 @@ ${weights.compact ? ` sr = max(textureSampleLevel(previousAccumulated, line
|
|
|
8725
8054
|
}
|
|
8726
8055
|
${weights.correction ? "var" : "let"} trust: vec4f = coarse(trustBlocks, position.xy, 4.0) * valid * same;
|
|
8727
8056
|
${weights.correction ? "var" : "let"} strength: f32 = coarse(strengthBlocks, position.xy, 4.0).r;
|
|
8057
|
+
${weights.compact ? ` // A frame drawn with projection jitter shows at pixel p what lies at
|
|
8058
|
+
// p + jitter. Performance reads the frame at p - jitter instead, with one
|
|
8059
|
+
// bilinear tap that also averages up to four samples, and takes its fixed
|
|
8060
|
+
// filter there: the slow luma, the correction and the denoised path see
|
|
8061
|
+
// the frame where it belongs. The filter takes its full share of the 3x3
|
|
8062
|
+
// mean where the view moves or the denoised history is short, and the
|
|
8063
|
+
// smaller one where that history settles, which keeps more detail.
|
|
8064
|
+
let unjittered: vec2f = position.xy - P.jitter;
|
|
8065
|
+
let shifted: vec4f = frameSample(colorTexture, unjittered);
|
|
8066
|
+
let fullShare: f32 = max(speed * speed / (speed * speed + ${float(weights.movingKnee * weights.movingKnee)}), exp(-aux.z * trust.y / ${float(weights.reach)}));
|
|
8067
|
+
let d: vec4f = mix(shifted, mean3x3(colorTexture, unjittered, P.colorOffset), mix(${float(weights.compactMix[0])}, ${float(weights.compactMix[1])}, fullShare));` : ""}
|
|
8728
8068
|
${weights.correction ? ` let corrected: vec4f = correctedPixels(color, d, sr, dr, aux, trust, strength);
|
|
8729
8069
|
trust = vec4f(corrected.xy, trust.z, corrected.z);
|
|
8730
8070
|
strength = corrected.w;` : ""}
|
|
@@ -8752,7 +8092,19 @@ ${weights.correction ? ` let corrected: vec4f = correctedPixels(color, d, sr, d
|
|
|
8752
8092
|
// nothing, which the learned filter leaves standing at any camera speed.
|
|
8753
8093
|
let short: f32 = max(exp(-wh / ${float(weights.reach)}) * max(fast, sparse), 1.0 - same);` : `let short: f32 = max(exp(-wh / ${float(weights.reach)}) * fast, 1.0 - same);`}
|
|
8754
8094
|
let wide: vec4f = ${weights.compact ? "mix(coarse(wideTexture, position.xy, 2.0), coarse(rejectedTexture, position.xy, 4.0), 0.5)" : "coarse(wideTexture, position.xy, 2.0)"};
|
|
8755
|
-
|
|
8095
|
+
${weights.compact ? ` let source: vec4f = shifted;
|
|
8096
|
+
let filtered: vec4f = d;` : ` // A frame drawn with projection jitter shows at pixel p what lies at
|
|
8097
|
+
// p + jitter. Where history is short the denoised path carries the frame,
|
|
8098
|
+
// and moving content would shift back and forth by up to half a pixel: the
|
|
8099
|
+
// path reads the frame and its filtered image at p - jitter instead.
|
|
8100
|
+
var source: vec4f = color;
|
|
8101
|
+
var filtered: vec4f = d;
|
|
8102
|
+
if (P.jitter.x != 0.0 || P.jitter.y != 0.0) {
|
|
8103
|
+
let unjittered: vec2f = position.xy - P.jitter;
|
|
8104
|
+
source = frameCatmullRom(unjittered);
|
|
8105
|
+
filtered = catmullRom(filteredTexture, unjittered * P.invSize);
|
|
8106
|
+
}`}
|
|
8107
|
+
var current: vec4f = mix(mix(source, filtered, strength), wide, short);
|
|
8756
8108
|
// Strengthen only the share whose history belonged to another surface.
|
|
8757
8109
|
// Normal fast motion keeps the learned fallback's detail.
|
|
8758
8110
|
let rejected: vec4f = coarse(rejectedTexture, position.xy, 4.0);
|
|
@@ -8766,7 +8118,24 @@ ${weights.correction ? ` let corrected: vec4f = correctedPixels(color, d, sr, d
|
|
|
8766
8118
|
let error: f32 = sqrt(max(y2 - ys * ys, 0.0) / (ns + 1e-3) + 1e-10);
|
|
8767
8119
|
let ramp: f32 = 1.0 - exp(-ns / ${float(weights.gateFrames)});
|
|
8768
8120
|
let keep: f32 = ramp * (1.0 - clamp(${float(weights.gamma)} * error, 0.0, 1.0)) * trust.w;
|
|
8769
|
-
|
|
8121
|
+
${weights.movingMean ? ` // While the camera moves, the accumulated path holds few samples of the
|
|
8122
|
+
// new view and its grain shows: it is shown from the 3x3 mean of its
|
|
8123
|
+
// history and this frame's filtered sample. What it keeps stays sharp, so
|
|
8124
|
+
// a camera that stops settles as before. This display-only smoothing does
|
|
8125
|
+
// not track moving objects: with a still camera they continue to use the
|
|
8126
|
+
// existing history rejection.
|
|
8127
|
+
var shownAccumulated: vec4f = s;
|
|
8128
|
+
if (P.historyValid > 0.5 && P.cameraStill < 0.5) {
|
|
8129
|
+
let held: vec4f = max(mean3x3(previousAccumulated, historyUv * P.size, vec2f(0.0)), vec4f(0.0));
|
|
8130
|
+
let moving: f32 = speed * speed / (speed * speed + ${float(weights.movingKnee * weights.movingKnee)});
|
|
8131
|
+
let smoothed: vec4f = (held * ws + filtered) / ds;
|
|
8132
|
+
// Only where the two differ by what the mean's noise explains: a larger
|
|
8133
|
+
// difference is detail, and stays.
|
|
8134
|
+
let differ: f32 = luma(abs(smoothed.rgb - s.rgb));
|
|
8135
|
+
let spread: f32 = ${float(weights.movingNoise)} * error * error;
|
|
8136
|
+
shownAccumulated = mix(s, smoothed, ${float(weights.movingMean)} * moving * spread / (spread + differ * differ + 1e-10));
|
|
8137
|
+
}
|
|
8138
|
+
let composite: vec4f = mix(dn, shownAccumulated, keep);` : " let composite: vec4f = mix(dn, s, keep);"}
|
|
8770
8139
|
// Stabilization: a predicted blend with the previous image, which may stay
|
|
8771
8140
|
// as far from this frame's result as half of what it spans around the
|
|
8772
8141
|
// pixel, plus a slack. Taking that span from this frame's result instead
|
|
@@ -9312,7 +8681,7 @@ P.historyValid = ndUniform7.z; P.stabilize = ndUniform7.w;
|
|
|
9312
8681
|
P.halfBias = ndUniform8.x; P.logDepth = ndUniform8.y;
|
|
9313
8682
|
P.logNear = ndUniform8.z; P.logFar = ndUniform8.w;
|
|
9314
8683
|
P.turn = ndUniform9.x; P.cameraStill = ndUniform9.y; P.offset = ndUniform9.zw;
|
|
9315
|
-
P.colorOffset = ndUniform10.xy;
|
|
8684
|
+
P.colorOffset = ndUniform10.xy; P.jitter = ndUniform10.zw;
|
|
9316
8685
|
`
|
|
9317
8686
|
);
|
|
9318
8687
|
class WebGLDenoisePasses extends NeuralDenoisePasses {
|
|
@@ -9390,6 +8759,7 @@ class WebGLDenoisePasses extends NeuralDenoisePasses {
|
|
|
9390
8759
|
}
|
|
9391
8760
|
allocate(views, depth2) {
|
|
9392
8761
|
var _a2, _b;
|
|
8762
|
+
if (depth2) depth2.texture.renderTarget = null;
|
|
9393
8763
|
const target = new this.Target(
|
|
9394
8764
|
((_a2 = views[0]) == null ? void 0 : _a2.width) ?? this.width,
|
|
9395
8765
|
((_b = views[0]) == null ? void 0 : _b.height) ?? this.height,
|
|
@@ -9563,6 +8933,7 @@ const quality = {
|
|
|
9563
8933
|
denoisedFrames: 24,
|
|
9564
8934
|
gateFrames: 8,
|
|
9565
8935
|
motionKnee: 20,
|
|
8936
|
+
movingKnee: 0.5,
|
|
9566
8937
|
stabilizeBlend: 0.9,
|
|
9567
8938
|
stabilizeSlack: 0.04,
|
|
9568
8939
|
evidenceFrames: [3, 6, 12, 24],
|
|
@@ -11602,9 +10973,56 @@ const quality = {
|
|
|
11602
10973
|
}
|
|
11603
10974
|
};
|
|
11604
10975
|
const neuralDenoiseModels = {
|
|
11605
|
-
quality,
|
|
11606
|
-
performance: { ...quality, compact: true }
|
|
10976
|
+
quality: { ...quality, movingMean: 1, movingNoise: 16 },
|
|
10977
|
+
performance: { ...quality, compact: true, compactMix: [0.5, 0.75] }
|
|
11607
10978
|
};
|
|
10979
|
+
class StochasticCoverage {
|
|
10980
|
+
constructor() {
|
|
10981
|
+
__publicField(this, "materials", /* @__PURE__ */ new Map());
|
|
10982
|
+
}
|
|
10983
|
+
material(source, renderer) {
|
|
10984
|
+
let entry = this.materials.get(source);
|
|
10985
|
+
if (!entry) {
|
|
10986
|
+
const variant = createStochasticSplatVariant(source, renderer);
|
|
10987
|
+
if (!variant) return source;
|
|
10988
|
+
const dispose = () => {
|
|
10989
|
+
source.removeEventListener("dispose", dispose);
|
|
10990
|
+
variant.dispose();
|
|
10991
|
+
this.materials.delete(source);
|
|
10992
|
+
};
|
|
10993
|
+
entry = { variant, state: new MaterialState(source), dispose };
|
|
10994
|
+
this.materials.set(source, entry);
|
|
10995
|
+
source.addEventListener("dispose", dispose);
|
|
10996
|
+
}
|
|
10997
|
+
entry.state.sync(entry.variant, source);
|
|
10998
|
+
return entry.variant;
|
|
10999
|
+
}
|
|
11000
|
+
render(renderer, scene, camera) {
|
|
11001
|
+
if (!usesNativeWebGPU(renderer) && !renderer.getContext().getExtension("OES_sample_variables"))
|
|
11002
|
+
throw new Error(
|
|
11003
|
+
"Stochastic rendering with MSAA requires OES_sample_variables on WebGL2; use msaa = false on this device"
|
|
11004
|
+
);
|
|
11005
|
+
const nodeRenderer = renderer.isWebGPURenderer === true;
|
|
11006
|
+
const previous = nodeRenderer ? renderer.getRenderObjectFunction() : renderer.renderBufferDirect;
|
|
11007
|
+
const draw = previous ?? renderer.renderObject;
|
|
11008
|
+
const materialIndex = nodeRenderer ? 4 : 3;
|
|
11009
|
+
const hook = (...args) => {
|
|
11010
|
+
args[materialIndex] = this.material(args[materialIndex], renderer);
|
|
11011
|
+
return draw.apply(renderer, args);
|
|
11012
|
+
};
|
|
11013
|
+
if (nodeRenderer) renderer.setRenderObjectFunction(hook);
|
|
11014
|
+
else renderer.renderBufferDirect = hook;
|
|
11015
|
+
try {
|
|
11016
|
+
renderer.render(scene, camera);
|
|
11017
|
+
} finally {
|
|
11018
|
+
if (nodeRenderer) renderer.setRenderObjectFunction(previous);
|
|
11019
|
+
else renderer.renderBufferDirect = previous;
|
|
11020
|
+
}
|
|
11021
|
+
}
|
|
11022
|
+
dispose() {
|
|
11023
|
+
for (const entry of this.materials.values()) entry.dispose();
|
|
11024
|
+
}
|
|
11025
|
+
}
|
|
11608
11026
|
const MARGIN = 3;
|
|
11609
11027
|
const VIEW_OFFSETS = [
|
|
11610
11028
|
[2, 2],
|
|
@@ -11662,12 +11080,7 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11662
11080
|
});
|
|
11663
11081
|
return color;
|
|
11664
11082
|
})());
|
|
11665
|
-
__publicField(this, "state",
|
|
11666
|
-
(value) => ({ value }),
|
|
11667
|
-
[this.source, ...this.history],
|
|
11668
|
-
true,
|
|
11669
|
-
MARGIN
|
|
11670
|
-
));
|
|
11083
|
+
__publicField(this, "state", createNeuralDenoiseState([this.source, ...this.history], MARGIN));
|
|
11671
11084
|
__publicField(this, "passes", null);
|
|
11672
11085
|
// The scene target's depth has the margin; this one has the image's size.
|
|
11673
11086
|
__publicField(this, "depth", new THREE.DepthTexture(1, 1, THREE.FloatType));
|
|
@@ -11705,7 +11118,7 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11705
11118
|
}
|
|
11706
11119
|
/** The projection `depthTexture` was rendered with. */
|
|
11707
11120
|
get projectionMatrix() {
|
|
11708
|
-
return this.state.
|
|
11121
|
+
return this.state.projection;
|
|
11709
11122
|
}
|
|
11710
11123
|
/** The denoised image, for chaining effects. */
|
|
11711
11124
|
getTextureNode() {
|
|
@@ -11773,7 +11186,7 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11773
11186
|
renderer.toneMapping = THREE.NoToneMapping;
|
|
11774
11187
|
const reversedDepth = camera.reversedDepth;
|
|
11775
11188
|
const coordinateSystem = camera.coordinateSystem;
|
|
11776
|
-
const { x: width, y: height } = state.
|
|
11189
|
+
const { x: width, y: height } = state.size;
|
|
11777
11190
|
const [offsetX, offsetY] = VIEW_OFFSETS[Math.floor(this.frame / 32) % VIEW_OFFSETS.length];
|
|
11778
11191
|
this.frame++;
|
|
11779
11192
|
this.source.viewport.set(offsetX, offsetY, width, height);
|
|
@@ -11787,11 +11200,11 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11787
11200
|
if (camera.reversedDepth !== reversedDepth || camera.coordinateSystem !== coordinateSystem)
|
|
11788
11201
|
camera.updateProjectionMatrix();
|
|
11789
11202
|
}
|
|
11790
|
-
const valid = state.
|
|
11203
|
+
const valid = state.valid;
|
|
11791
11204
|
if (!valid && native)
|
|
11792
11205
|
for (const target of this.history) renderer.initRenderTarget(target);
|
|
11793
11206
|
const u = passes.uniforms;
|
|
11794
|
-
const jittered = state.
|
|
11207
|
+
const jittered = state.projection.elements;
|
|
11795
11208
|
const logarithmic = (nodeRenderer ? renderer.logarithmicDepthBuffer : renderer.capabilities.logarithmicDepthBuffer) && "isPerspectiveCamera" in camera && camera.isPerspectiveCamera;
|
|
11796
11209
|
const near = Math.max(camera.near, 1e-6);
|
|
11797
11210
|
const reversed = nodeRenderer ? renderer.reversedDepthBuffer : renderer.capabilities.reversedDepthBuffer;
|
|
@@ -11833,6 +11246,17 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11833
11246
|
this.source.texture.colorSpace || this.workingColorSpace
|
|
11834
11247
|
) === THREE.LinearTransfer;
|
|
11835
11248
|
u.set(encode ? [0, 0] : [u[62], u[63]], 64);
|
|
11249
|
+
const p = camera.projectionMatrix.elements;
|
|
11250
|
+
let jitterX = 0;
|
|
11251
|
+
let jitterY = 0;
|
|
11252
|
+
if ([8, 9, 12, 13].some((i) => jittered[i] !== p[i])) {
|
|
11253
|
+
const x = (jittered[8] - jittered[12]) / jittered[0];
|
|
11254
|
+
const y = (jittered[9] - jittered[13]) / jittered[5];
|
|
11255
|
+
const w = p[3] * x + p[7] * y - p[11] + p[15];
|
|
11256
|
+
jitterX = (p[0] * x + p[4] * y - p[8] + p[12]) / w * width / 2;
|
|
11257
|
+
jitterY = -((p[1] * x + p[5] * y - p[9] + p[13]) / w) * height / 2;
|
|
11258
|
+
}
|
|
11259
|
+
u.set([jitterX, jitterY], 66);
|
|
11836
11260
|
this.encoded.value = THREE.ColorManagement.getTransfer(this.workingColorSpace) === THREE.LinearTransfer;
|
|
11837
11261
|
const gpu = (texture) => {
|
|
11838
11262
|
const data = backend.get(texture).texture;
|
|
@@ -11865,7 +11289,7 @@ class NeuralDenoiseNode extends webgpu.Node {
|
|
|
11865
11289
|
this.textureNode.value = native ? this.history[1 - previous].texture : passes.result;
|
|
11866
11290
|
this.previousWorld.copy(camera.matrixWorld);
|
|
11867
11291
|
this.previousProjection.copy(camera.projectionMatrix);
|
|
11868
|
-
state.advance(
|
|
11292
|
+
state.advance();
|
|
11869
11293
|
this.capturePending = false;
|
|
11870
11294
|
} finally {
|
|
11871
11295
|
renderer.setRenderTarget(
|
|
@@ -11950,7 +11374,7 @@ void main() {
|
|
|
11950
11374
|
vec4 color = texture2D(image, vUv);
|
|
11951
11375
|
if (encoded) {
|
|
11952
11376
|
// OutputPass applies its transfer directly to premultiplied RGB. Invert
|
|
11953
|
-
// that transfer here
|
|
11377
|
+
// that transfer here to round-trip transparent pixels.
|
|
11954
11378
|
float scale = outputSRGB ? 1.0 : max(color.a, 1e-6);
|
|
11955
11379
|
color.rgb = sRGBTransferEOTF(vec4(max(color.rgb / scale, 0.0), 1.0)).rgb * scale;
|
|
11956
11380
|
}
|
|
@@ -12024,6 +11448,175 @@ void main() {
|
|
|
12024
11448
|
this.quad.dispose();
|
|
12025
11449
|
}
|
|
12026
11450
|
}
|
|
11451
|
+
const EYE_PATTERN_OFFSET = 16;
|
|
11452
|
+
class NeuralDenoiseXR {
|
|
11453
|
+
constructor(renderer, scene, camera, mode = "quality", msaa = false) {
|
|
11454
|
+
__publicField(this, "stabilize", true);
|
|
11455
|
+
__publicField(this, "views", []);
|
|
11456
|
+
__publicField(this, "output", null);
|
|
11457
|
+
assertSupportedRenderer(renderer);
|
|
11458
|
+
this.renderer = renderer;
|
|
11459
|
+
this.scene = scene;
|
|
11460
|
+
this.camera = camera;
|
|
11461
|
+
this.mode = mode;
|
|
11462
|
+
this.msaa = msaa;
|
|
11463
|
+
this.nodeRenderer = isWebGPURenderer(renderer);
|
|
11464
|
+
if (this.nodeRenderer) applyThreeR186Patch(renderer);
|
|
11465
|
+
this.onSessionEnd = () => this.disposeViews();
|
|
11466
|
+
renderer.xr.addEventListener("sessionend", this.onSessionEnd);
|
|
11467
|
+
}
|
|
11468
|
+
createViews(eyes) {
|
|
11469
|
+
this.disposeViews();
|
|
11470
|
+
this.views = eyes.map((eye) => {
|
|
11471
|
+
const camera = new THREE.PerspectiveCamera();
|
|
11472
|
+
const pass = this.nodeRenderer ? new NeuralDenoiseNode(this.scene, camera, this.mode, this.msaa) : new NeuralDenoisePass(this.scene, camera, this.mode, this.msaa);
|
|
11473
|
+
const denoise = this.nodeRenderer ? pass : pass.denoise;
|
|
11474
|
+
denoise.updateBeforeType = webgpu.NodeUpdateType.NONE;
|
|
11475
|
+
return { eye, pass, denoise };
|
|
11476
|
+
});
|
|
11477
|
+
if (this.nodeRenderer) {
|
|
11478
|
+
const material = new webgpu.NodeMaterial();
|
|
11479
|
+
material.name = "NeuralDenoiseXR.output";
|
|
11480
|
+
material.vertexNode = N__namespace.vec4(N__namespace.positionGeometry.xy, 0, 1);
|
|
11481
|
+
const color = N__namespace.Fn((builder) => {
|
|
11482
|
+
const index2 = viewIndex(builder.camera);
|
|
11483
|
+
const color2 = N__namespace.vec4(0).toVar();
|
|
11484
|
+
this.views.forEach((view, i) => {
|
|
11485
|
+
N__namespace.If(index2.equal(i), () => {
|
|
11486
|
+
color2.assign(view.denoise.getTextureNode());
|
|
11487
|
+
});
|
|
11488
|
+
});
|
|
11489
|
+
return color2;
|
|
11490
|
+
})();
|
|
11491
|
+
material.fragmentNode = N__namespace.renderOutput(
|
|
11492
|
+
color,
|
|
11493
|
+
this.renderer.toneMapping,
|
|
11494
|
+
this.renderer.outputColorSpace
|
|
11495
|
+
);
|
|
11496
|
+
material.depthTest = false;
|
|
11497
|
+
material.depthWrite = false;
|
|
11498
|
+
material.blending = THREE.NoBlending;
|
|
11499
|
+
this.output = new webgpu.QuadMesh(material);
|
|
11500
|
+
} else {
|
|
11501
|
+
this.output = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), this.views[0].pass.copy);
|
|
11502
|
+
this.output.onBeforeRender = (renderer, scene, camera) => {
|
|
11503
|
+
const { denoise } = this.views.find((view) => view.eye === camera);
|
|
11504
|
+
const uniforms = this.output.material.uniforms;
|
|
11505
|
+
uniforms.image.value = denoise.textureNode.value;
|
|
11506
|
+
uniforms.encoded.value = denoise.encoded.value;
|
|
11507
|
+
uniforms.outputSRGB.value = THREE.ColorManagement.getTransfer(renderer.outputColorSpace) === THREE.SRGBTransfer;
|
|
11508
|
+
this.output.material.uniformsNeedUpdate = true;
|
|
11509
|
+
};
|
|
11510
|
+
}
|
|
11511
|
+
this.output.frustumCulled = false;
|
|
11512
|
+
this.output.layers.enableAll();
|
|
11513
|
+
}
|
|
11514
|
+
render() {
|
|
11515
|
+
const { renderer } = this;
|
|
11516
|
+
const { xr } = renderer;
|
|
11517
|
+
if (!xr.enabled || !xr.isPresenting) {
|
|
11518
|
+
throw new Error(
|
|
11519
|
+
"NeuralDenoiseXR.render() requires an active WebXR session"
|
|
11520
|
+
);
|
|
11521
|
+
}
|
|
11522
|
+
if (xr.cameraAutoUpdate) {
|
|
11523
|
+
this.camera.updateWorldMatrix(true, false);
|
|
11524
|
+
xr.updateCamera(this.camera);
|
|
11525
|
+
}
|
|
11526
|
+
const eyes = xr.getCamera().cameras;
|
|
11527
|
+
if (eyes.length === 0) return;
|
|
11528
|
+
if (eyes.length !== this.views.length || eyes.some((eye, i) => eye !== this.views[i].eye))
|
|
11529
|
+
this.createViews(eyes);
|
|
11530
|
+
const target = renderer.getRenderTarget();
|
|
11531
|
+
const cubeFace = renderer.getActiveCubeFace();
|
|
11532
|
+
const mipLevel = renderer.getActiveMipmapLevel();
|
|
11533
|
+
const cameraAutoUpdate = xr.cameraAutoUpdate;
|
|
11534
|
+
const autoClear = renderer.autoClear;
|
|
11535
|
+
const toneMapping = renderer.toneMapping;
|
|
11536
|
+
const outputColorSpace = renderer.outputColorSpace;
|
|
11537
|
+
const outputTarget = this.nodeRenderer ? renderer.getOutputRenderTarget() : null;
|
|
11538
|
+
const outputDepth = outputTarget == null ? void 0 : outputTarget.depthBuffer;
|
|
11539
|
+
const samples = /* @__PURE__ */ new Map();
|
|
11540
|
+
this.scene.traverse((object) => {
|
|
11541
|
+
if (object instanceof GaussianSplatRenderer && object.autoAdvanceStochasticSample)
|
|
11542
|
+
samples.set(object, object.stochasticSample + 1 >>> 0);
|
|
11543
|
+
});
|
|
11544
|
+
try {
|
|
11545
|
+
xr.cameraAutoUpdate = false;
|
|
11546
|
+
for (const splat of samples.keys())
|
|
11547
|
+
splat.autoAdvanceStochasticSample = false;
|
|
11548
|
+
for (const [index2, { eye, denoise }] of this.views.entries()) {
|
|
11549
|
+
const { camera } = denoise;
|
|
11550
|
+
for (const [splat, sample] of samples)
|
|
11551
|
+
splat.stochasticSample = sample + index2 * EYE_PATTERN_OFFSET >>> 0;
|
|
11552
|
+
camera.copy(eye, false);
|
|
11553
|
+
camera.matrixAutoUpdate = false;
|
|
11554
|
+
camera.matrixWorldAutoUpdate = false;
|
|
11555
|
+
camera.coordinateSystem = renderer.coordinateSystem;
|
|
11556
|
+
const reversed = this.nodeRenderer ? renderer.reversedDepthBuffer : renderer.capabilities.reversedDepthBuffer;
|
|
11557
|
+
camera._reversedDepth = reversed;
|
|
11558
|
+
if (reversed) {
|
|
11559
|
+
const p = camera.projectionMatrix.elements;
|
|
11560
|
+
const scale = usesNativeWebGPU(renderer) ? 1 : 0.5;
|
|
11561
|
+
for (let i = 0; i < 16; i += 4)
|
|
11562
|
+
p[i + 2] = scale * (p[i + 3] - p[i + 2]);
|
|
11563
|
+
camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert();
|
|
11564
|
+
}
|
|
11565
|
+
denoise.scene = this.scene;
|
|
11566
|
+
denoise.stabilize = this.stabilize;
|
|
11567
|
+
denoise.setSize(eye.viewport.z, eye.viewport.w);
|
|
11568
|
+
renderer.setRenderTarget(denoise.history[0]);
|
|
11569
|
+
denoise.updateBefore({ renderer });
|
|
11570
|
+
}
|
|
11571
|
+
renderer.setRenderTarget(target, cubeFace, mipLevel);
|
|
11572
|
+
if (this.nodeRenderer) {
|
|
11573
|
+
const output = this.output.material.fragmentNode;
|
|
11574
|
+
if (output.getToneMapping() !== toneMapping || output.outputColorSpace !== outputColorSpace) {
|
|
11575
|
+
this.output.material.fragmentNode = N__namespace.renderOutput(
|
|
11576
|
+
output.colorNode,
|
|
11577
|
+
toneMapping,
|
|
11578
|
+
outputColorSpace
|
|
11579
|
+
);
|
|
11580
|
+
this.output.material.needsUpdate = true;
|
|
11581
|
+
}
|
|
11582
|
+
renderer.toneMapping = THREE.NoToneMapping;
|
|
11583
|
+
renderer.outputColorSpace = THREE.ColorManagement.workingColorSpace;
|
|
11584
|
+
if (usesNativeWebGPU(renderer) && (outputTarget == null ? void 0 : outputTarget.useArrayDepthTexture))
|
|
11585
|
+
outputTarget.depthBuffer = true;
|
|
11586
|
+
}
|
|
11587
|
+
renderer.render(this.output, this.camera);
|
|
11588
|
+
} finally {
|
|
11589
|
+
for (const [splat, sample] of samples) {
|
|
11590
|
+
splat.stochasticSample = sample;
|
|
11591
|
+
splat.autoAdvanceStochasticSample = true;
|
|
11592
|
+
}
|
|
11593
|
+
xr.enabled = true;
|
|
11594
|
+
xr.cameraAutoUpdate = cameraAutoUpdate;
|
|
11595
|
+
renderer.autoClear = autoClear;
|
|
11596
|
+
renderer.toneMapping = toneMapping;
|
|
11597
|
+
renderer.outputColorSpace = outputColorSpace;
|
|
11598
|
+
if (outputTarget) outputTarget.depthBuffer = outputDepth;
|
|
11599
|
+
renderer.setRenderTarget(target, cubeFace, mipLevel);
|
|
11600
|
+
}
|
|
11601
|
+
}
|
|
11602
|
+
/** Discards both eyes' history after a camera cut or scene replacement. */
|
|
11603
|
+
reset() {
|
|
11604
|
+
for (const { denoise } of this.views) denoise.reset();
|
|
11605
|
+
}
|
|
11606
|
+
disposeViews() {
|
|
11607
|
+
for (const { pass } of this.views) pass.dispose();
|
|
11608
|
+
this.views = [];
|
|
11609
|
+
if (this.output) {
|
|
11610
|
+
if (this.nodeRenderer) this.output.material.dispose();
|
|
11611
|
+
else this.output.geometry.dispose();
|
|
11612
|
+
this.output = null;
|
|
11613
|
+
}
|
|
11614
|
+
}
|
|
11615
|
+
dispose() {
|
|
11616
|
+
this.renderer.xr.removeEventListener("sessionend", this.onSessionEnd);
|
|
11617
|
+
this.disposeViews();
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
12027
11620
|
class WebGLCapture {
|
|
12028
11621
|
constructor(renderer) {
|
|
12029
11622
|
this.renderer = renderer;
|
|
@@ -17353,6 +16946,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
17353
16946
|
exports.GaussianSplatRenderer = GaussianSplatRenderer;
|
|
17354
16947
|
exports.NeuralDenoiseNode = NeuralDenoiseNode;
|
|
17355
16948
|
exports.NeuralDenoisePass = NeuralDenoisePass;
|
|
16949
|
+
exports.NeuralDenoiseXR = NeuralDenoiseXR;
|
|
17356
16950
|
exports.RadStreamScheduler = RadStreamScheduler;
|
|
17357
16951
|
exports.SogStreamScheduler = SogStreamScheduler;
|
|
17358
16952
|
exports.SplatCapture = SplatCapture;
|
|
@@ -17365,8 +16959,6 @@ exports.SplatFileType = SplatFileType;
|
|
|
17365
16959
|
exports.SplatLoader = SplatLoader;
|
|
17366
16960
|
exports.SplatMesh = SplatMesh;
|
|
17367
16961
|
exports.Splats = Splats;
|
|
17368
|
-
exports.TAANode = TAANode;
|
|
17369
|
-
exports.TAAPass = TAAPass;
|
|
17370
16962
|
exports.defines = defines;
|
|
17371
16963
|
exports.fromHalf = fromHalf;
|
|
17372
16964
|
exports.postDecode = postDecode;
|