@woosh/meep-engine 3.60.5 → 3.60.6
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 +19 -0
- package/package.json +1 -1
- package/src/shade/renderer/Renderer.d.ts +11 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +43 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +26 -43
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +109 -117
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +20 -2
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +66 -2
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
- package/src/shade/renderer/material/GPUMaterialManager.d.ts +4 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +5 -8
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
- package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { DEPTH_STENCIL_FOREGROUND_ONLY } from "../../depth/DEPTH_STENCIL_FOREGROUND_ONLY.js";
|
|
2
|
+
import { chunk_decode_g_buffer_normal } from "../../shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.js";
|
|
3
|
+
import { chunk_encode_g_buffer_normal } from "../../shader/chunk/geometry/normal/chunk_encode_g_buffer_normal.js";
|
|
4
|
+
import { chunk_f16_round_stochastic } from "../../shader/chunk/math/chunk_f16_round_stochastic.js";
|
|
2
5
|
import { chunk_get_std_dev } from "../../shader/chunk/math/statistics/chunk_get_std_dev.js";
|
|
6
|
+
import { chunk_hash_to_float01 } from "../../shader/chunk/random/chunk_hash_to_float01.js";
|
|
7
|
+
import { chunk_pcg3d } from "../../shader/chunk/random/pcg/chunk_pcg3d.js";
|
|
3
8
|
import {
|
|
4
9
|
chunk_texture_sample_catmullrom_hardware_5tap_uv
|
|
5
10
|
} from "../../shader/chunk/texture/sample/chunk_texture_sample_catmullrom_hardware_5tap_uv.js";
|
|
@@ -8,107 +13,174 @@ import { ImageShader } from "../../shader/ImageShader.js";
|
|
|
8
13
|
import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
9
14
|
import { ShaderDescriptor } from "../../shader/ShaderDescriptor.js";
|
|
10
15
|
import { SAMPLER_LINEAR_UPSCALE } from "../../texture/SAMPLER_LINEAR_UPSCALE.js";
|
|
11
|
-
import { f32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
16
|
+
import { f32, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
12
17
|
import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
|
|
13
18
|
import { chunk_taa_velocity_confidence } from "../taa/shader_taa.js";
|
|
14
19
|
import { chunk_taa_get_velocity } from "../velocity/chunk_taa_get_velocity.js";
|
|
15
20
|
|
|
16
21
|
const AO_REPROJECT_SETTINGS_STRUCT = WebGPUStruct.from({
|
|
17
22
|
/**
|
|
18
|
-
* 1 to blend against the history, 0 to take this frame's
|
|
19
|
-
*
|
|
23
|
+
* 1 to blend against the history, 0 to take this frame's alone: the first frame, a reset, or a frame that
|
|
24
|
+
* does not follow the one the history was written on.
|
|
20
25
|
*/
|
|
21
26
|
history_validity: f32,
|
|
27
|
+
/**
|
|
28
|
+
* Seeds the stochastic rounding of the history's store, fresh each frame.
|
|
29
|
+
*/
|
|
30
|
+
frame_index: u32,
|
|
22
31
|
});
|
|
23
32
|
|
|
24
33
|
const resources = new ShaderResourceSetDescriptor();
|
|
25
34
|
|
|
26
|
-
// per-pass,
|
|
35
|
+
// per-pass, 8 bytes, read-only: immediate data rather than a uniform buffer.
|
|
27
36
|
resources.setImmediate("settings", AO_REPROJECT_SETTINGS_STRUCT);
|
|
28
37
|
resources.createGroup()
|
|
29
38
|
.addTexture("tInput")
|
|
30
39
|
.addTexture("tVelocity")
|
|
31
40
|
.addTexture("tOcclusion")
|
|
41
|
+
.addTexture("tNormal", "uint")
|
|
42
|
+
// last frame's G-buffer normal, at the size last frame was drawn at
|
|
43
|
+
.addTexture("tNormalPrevious", "uint")
|
|
32
44
|
.addTexture("tHistory","float")
|
|
33
45
|
.addSampler("sLinear", "filtering", SAMPLER_LINEAR_UPSCALE)
|
|
34
46
|
|
|
35
47
|
const body = CodeChunk.from(
|
|
36
48
|
//language=WGSL
|
|
37
49
|
`
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
struct NeighbourhoodStatistics {
|
|
51
|
+
// visibility's first two moments
|
|
52
|
+
moments: vec2<f32>,
|
|
53
|
+
// the bend's mean, and the mean of its squared length
|
|
54
|
+
bend: vec3<f32>,
|
|
55
|
+
bend_square: f32,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn compute_center_statistics(
|
|
59
|
+
input_center: vec4<f32>,
|
|
40
60
|
input_coord: vec2<i32>,
|
|
41
61
|
input_texture: texture_2d<f32>,
|
|
42
|
-
) ->
|
|
43
|
-
|
|
62
|
+
) -> NeighbourhoodStatistics {
|
|
63
|
+
|
|
44
64
|
const offsets = array< vec2<i32>, 8 >(
|
|
45
65
|
vec2( -1, -1 ),
|
|
46
66
|
vec2( 0, -1 ),
|
|
47
|
-
vec2( 1, -1 ),
|
|
48
|
-
|
|
49
|
-
vec2( -1, 0 ),
|
|
50
|
-
vec2( 1, 0 ),
|
|
51
|
-
|
|
67
|
+
vec2( 1, -1 ),
|
|
68
|
+
|
|
69
|
+
vec2( -1, 0 ),
|
|
70
|
+
vec2( 1, 0 ),
|
|
71
|
+
|
|
52
72
|
vec2( -1, 1 ),
|
|
53
|
-
vec2( 0, 1 ),
|
|
54
|
-
vec2( 1, 1 ),
|
|
73
|
+
vec2( 0, 1 ),
|
|
74
|
+
vec2( 1, 1 ),
|
|
55
75
|
);
|
|
56
|
-
|
|
76
|
+
|
|
57
77
|
const iteratorMax = 8;
|
|
58
78
|
const rcp_divider = 1.f / 9.f;
|
|
59
79
|
|
|
60
80
|
var moments = vec2(input_center.x, input_center.x * input_center.x);
|
|
61
|
-
|
|
81
|
+
|
|
82
|
+
var bend = input_center.yzw;
|
|
83
|
+
var bend_square = dot(input_center.yzw, input_center.yzw);
|
|
84
|
+
|
|
62
85
|
for (var i = 0; i < iteratorMax; i++ ) {
|
|
63
86
|
let new_st = input_coord + offsets[ i ];
|
|
64
87
|
|
|
65
|
-
let texel = textureLoad(input_texture, new_st, 0 )
|
|
66
|
-
|
|
88
|
+
let texel = textureLoad(input_texture, new_st, 0 );
|
|
89
|
+
|
|
67
90
|
moments += vec2(texel.x, texel.x * texel.x);
|
|
91
|
+
bend += texel.yzw;
|
|
92
|
+
bend_square += dot(texel.yzw, texel.yzw);
|
|
68
93
|
}
|
|
69
|
-
|
|
70
|
-
return moments * rcp_divider;
|
|
94
|
+
|
|
95
|
+
return NeighbourhoodStatistics(moments * rcp_divider, bend * rcp_divider, bend_square * rcp_divider);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A face this far from another is another surface: half a right angle, as UE's Lumen takes it
|
|
99
|
+
// (r.Lumen.ScreenProbeGather.Temporal.NormalThreshold). A tessellated curve's faces sit well inside it, a
|
|
100
|
+
// crease's well outside.
|
|
101
|
+
const SAME_SURFACE_COS = 0.70710678;
|
|
102
|
+
|
|
103
|
+
// 1 where the history texel at tap was written on this texel's surface, by its face normal: last frame's
|
|
104
|
+
fn history_texel_on_surface(tap: vec2<i32>, size: vec2<i32>, face_normal: vec3<f32>) -> f32 {
|
|
105
|
+
let history_face_normal = decode_g_buffer_normal(textureLoad(tNormalPrevious, clamp(tap, vec2(0), size - 1), 0).zw);
|
|
106
|
+
|
|
107
|
+
return select(0.0, 1.0, dot(history_face_normal, face_normal) >= SAME_SURFACE_COS);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// The share of the history's bilinear footprint written on this texel's surface, read where the history is, in
|
|
111
|
+
// the previous normal's own grid.
|
|
112
|
+
fn history_surface_agreement(prev_uv: vec2<f32>, face_normal: vec3<f32>) -> f32 {
|
|
113
|
+
let size = vec2<i32>(textureDimensions(tNormalPrevious));
|
|
114
|
+
|
|
115
|
+
let st = prev_uv * vec2<f32>(size) - 0.5;
|
|
116
|
+
let origin = vec2<i32>(floor(st));
|
|
117
|
+
let f = st - floor(st);
|
|
118
|
+
|
|
119
|
+
let weights = vec4(
|
|
120
|
+
(1.0 - f.x) * (1.0 - f.y),
|
|
121
|
+
f.x * (1.0 - f.y),
|
|
122
|
+
(1.0 - f.x) * f.y,
|
|
123
|
+
f.x * f.y,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
let on_surface = vec4(
|
|
127
|
+
history_texel_on_surface(origin, size, face_normal),
|
|
128
|
+
history_texel_on_surface(origin + vec2(1, 0), size, face_normal),
|
|
129
|
+
history_texel_on_surface(origin + vec2(0, 1), size, face_normal),
|
|
130
|
+
history_texel_on_surface(origin + vec2(1, 1), size, face_normal),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return dot(weights, on_surface);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
struct Output {
|
|
137
|
+
// visibility and the bend, accumulated: next frame's history
|
|
138
|
+
@location(0) history: vec4<f32>,
|
|
139
|
+
// the bent normal, octahedral as the G-buffer's normals are
|
|
140
|
+
@location(1) bent_normal: vec2<u32>,
|
|
71
141
|
}
|
|
72
142
|
|
|
73
143
|
@fragment
|
|
74
144
|
fn main(
|
|
75
145
|
@builtin(position) coord: vec4<f32>,
|
|
76
146
|
@location(0) uv: vec2<f32>,
|
|
77
|
-
) ->
|
|
147
|
+
) -> Output {
|
|
78
148
|
let screenST = vec2<i32>( coord.xy );
|
|
79
|
-
|
|
149
|
+
|
|
80
150
|
let resolution = textureDimensions(tInput);
|
|
81
|
-
|
|
151
|
+
|
|
82
152
|
let occlusion_confidence = textureLoad(tOcclusion, screenST, 0).r;
|
|
83
|
-
let
|
|
84
|
-
|
|
153
|
+
let current = textureLoad(tInput, screenST, 0);
|
|
154
|
+
let encoded_normal = textureLoad(tNormal, screenST, 0);
|
|
155
|
+
let shading_normal = decode_g_buffer_normal(encoded_normal.xy);
|
|
156
|
+
|
|
85
157
|
// get velocity
|
|
86
158
|
let velocity = taa_get_velocity( tVelocity, screenST );
|
|
87
|
-
|
|
159
|
+
|
|
88
160
|
let velocity_confidence = taa_velocity_confidence(velocity);
|
|
89
|
-
|
|
90
|
-
|
|
161
|
+
|
|
162
|
+
|
|
91
163
|
// prev frame ST
|
|
92
164
|
let prevFrameScreenST = coord.xy - velocity.xy;
|
|
93
|
-
|
|
165
|
+
|
|
94
166
|
const MIN_VARIANCE = 0.5f; // under motion
|
|
95
167
|
const MAX_VARIANCE = 1.2f; // no motion
|
|
96
168
|
|
|
97
169
|
let variance_scale = mix( MIN_VARIANCE, MAX_VARIANCE, velocity_confidence * velocity_confidence ) ;
|
|
98
|
-
|
|
99
|
-
|
|
170
|
+
|
|
171
|
+
|
|
100
172
|
let history_within_screen = all( prevFrameScreenST >= vec2(0.0) ) && all( prevFrameScreenST < vec2f(resolution )) ;
|
|
101
|
-
|
|
102
|
-
|
|
173
|
+
|
|
174
|
+
|
|
103
175
|
let confidence = velocity_confidence * occlusion_confidence * select(0.0, 1.0,history_within_screen) * settings.history_validity;
|
|
104
176
|
|
|
105
|
-
|
|
106
|
-
var
|
|
107
|
-
|
|
177
|
+
|
|
178
|
+
var resolved: vec4<f32>;
|
|
179
|
+
|
|
108
180
|
if(
|
|
109
181
|
confidence <= 0.001
|
|
110
182
|
) {
|
|
111
|
-
|
|
183
|
+
resolved = current;
|
|
112
184
|
}else{
|
|
113
185
|
|
|
114
186
|
// a uv over this frame's size: the history keeps the size it was written at across a
|
|
@@ -116,51 +188,81 @@ fn main(
|
|
|
116
188
|
let prev_uv = prevFrameScreenST / vec2<f32>(resolution);
|
|
117
189
|
|
|
118
190
|
let history = texture_sample_catmullrom_hardware_5tap_uv(tHistory, sLinear, prev_uv);
|
|
119
|
-
let history_moments = history.rg;
|
|
120
191
|
|
|
121
192
|
// AABB sigma is calibrated against the spatially pre-filtered input.
|
|
122
193
|
// The small spatial denoise upstream is what makes this clamp meaningful:
|
|
123
194
|
// it stabilises the neighbourhood so the AABB is a tight, non-jittery
|
|
124
195
|
// ghosting bound rather than chasing per-frame noise. (Standard
|
|
125
196
|
// pre-blur-then-clamp pattern from SVGF-style and TAA literature.)
|
|
126
|
-
let
|
|
197
|
+
let statistics = compute_center_statistics( current, screenST, tInput);
|
|
127
198
|
|
|
128
|
-
let sigma = get_std_dev(
|
|
199
|
+
let sigma = get_std_dev(statistics.moments.x, statistics.moments.y) * variance_scale;
|
|
129
200
|
|
|
130
201
|
// clamp history mean to neighborhood AABB
|
|
131
|
-
let
|
|
132
|
-
|
|
202
|
+
let clamped_visibility = clamp(history.x, statistics.moments.x - sigma, statistics.moments.x + sigma);
|
|
203
|
+
|
|
204
|
+
// The bend is clamped to a ball about the neighbourhood's mean bend, its radius the same multiple of
|
|
205
|
+
// the bend's deviation as the visibility's box takes of its own. The bend is not a unit vector, so its
|
|
206
|
+
// variance has no cancellation to fight: a neighbourhood of one value has none, and clamps the history
|
|
207
|
+
// onto it.
|
|
208
|
+
let bend_variance = max(statistics.bend_square - dot(statistics.bend, statistics.bend), 0.0);
|
|
209
|
+
let bend_radius = sqrt(bend_variance) * variance_scale;
|
|
133
210
|
|
|
134
|
-
let
|
|
211
|
+
let bend_offset = history.yzw - statistics.bend;
|
|
212
|
+
let bend_offset_length = length(bend_offset);
|
|
135
213
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
214
|
+
// past the radius the offset's length is positive, so the division is safe where it is taken
|
|
215
|
+
let clamped_bend = select(
|
|
216
|
+
history.yzw,
|
|
217
|
+
statistics.bend + bend_offset * (bend_radius / bend_offset_length),
|
|
218
|
+
bend_offset_length > bend_radius
|
|
219
|
+
);
|
|
141
220
|
|
|
142
|
-
// Note: deliberately do NOT gate history_weight on
|
|
221
|
+
// Note: deliberately do NOT gate history_weight on the neighbourhood's variance.
|
|
143
222
|
// Mixing a clamped history mean with a current mean injects cross-term
|
|
144
223
|
// variance (w·(1-w)·(Δmean)²) that compounds through the EMA whenever
|
|
145
224
|
// history hasn't converged. Letting that drive history_confidence down
|
|
146
|
-
// collapses temporal accumulation. The
|
|
147
|
-
// ghosting; rely on
|
|
225
|
+
// collapses temporal accumulation. The clamps already constrain
|
|
226
|
+
// ghosting; rely on them instead and trust history at a fixed rate.
|
|
148
227
|
const HISTORY_WEIGHT_MAX = 0.95;
|
|
149
228
|
|
|
150
229
|
let history_weight = HISTORY_WEIGHT_MAX * occlusion_confidence;
|
|
151
230
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
231
|
+
// A bend means something only against the normal it was measured on. A texel on a crease lands on
|
|
232
|
+
// either side from frame to frame as the projection jitters, so its history holds the other surface's
|
|
233
|
+
// bend, which added to this surface's normal tilts the bent normal towards that surface: a line along
|
|
234
|
+
// every crease. The bend takes the history only for the share written on this texel's own surface.
|
|
235
|
+
// Visibility is not measured against the normal, and keeps all of it.
|
|
236
|
+
let surface_agreement = history_surface_agreement(prev_uv, decode_g_buffer_normal(encoded_normal.zw));
|
|
157
237
|
|
|
158
|
-
|
|
238
|
+
resolved = vec4(
|
|
239
|
+
mix(current.x, clamped_visibility, history_weight),
|
|
240
|
+
mix(current.yzw, clamped_bend, history_weight * surface_agreement),
|
|
241
|
+
);
|
|
159
242
|
|
|
160
243
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
244
|
+
|
|
245
|
+
// Halves round toward zero on the devices measured, so a blend stored as one stalls short of its target by
|
|
246
|
+
// up to a ULP over the history's 5% step. Rounded stochastically, it lands on its target in expectation.
|
|
247
|
+
let hash = pcg3d(vec3<u32>(vec2<u32>(screenST), settings.frame_index));
|
|
248
|
+
let noise = vec4<f32>(
|
|
249
|
+
hash_to_float01(hash.x),
|
|
250
|
+
hash_to_float01(hash.y),
|
|
251
|
+
hash_to_float01(hash.z),
|
|
252
|
+
hash_to_float01(pcg3d(hash).x),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// The bend rides on this frame's own shading normal, so the history adds nothing of the normal map's detail
|
|
256
|
+
// of the texels it was resampled from. An extreme bend can cancel the normal; the normal stands in then.
|
|
257
|
+
let bent = shading_normal + resolved.yzw;
|
|
258
|
+
let bent_length = length(bent);
|
|
259
|
+
|
|
260
|
+
var out: Output;
|
|
261
|
+
|
|
262
|
+
out.history = f16_round_stochastic(resolved, noise);
|
|
263
|
+
out.bent_normal = encode_g_buffer_normal(select(shading_normal, bent / bent_length, bent_length > 1e-4));
|
|
264
|
+
|
|
265
|
+
return out;
|
|
164
266
|
}
|
|
165
267
|
`,
|
|
166
268
|
[
|
|
@@ -168,8 +270,28 @@ fn main(
|
|
|
168
270
|
chunk_taa_velocity_confidence,
|
|
169
271
|
chunk_texture_sample_catmullrom_hardware_5tap_uv,
|
|
170
272
|
chunk_get_std_dev,
|
|
273
|
+
chunk_decode_g_buffer_normal,
|
|
274
|
+
chunk_encode_g_buffer_normal,
|
|
275
|
+
chunk_f16_round_stochastic,
|
|
276
|
+
chunk_pcg3d,
|
|
277
|
+
chunk_hash_to_float01,
|
|
171
278
|
]
|
|
172
279
|
);
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Accumulates GTAO over time: visibility, and the bend that makes the bent normal.
|
|
283
|
+
*
|
|
284
|
+
* The bend takes the history only where the history was written on this texel's surface, by the face normals of
|
|
285
|
+
* `tNormalPrevious` — last frame's G-buffer normal — against this frame's.
|
|
286
|
+
*
|
|
287
|
+
* `tInput`, `tHistory` and the first target are `rgba16float`: visibility in `x`, and in `yzw` the world-space
|
|
288
|
+
* bend — the bent normal minus the shading normal. Each is clamped to this frame's 3x3 neighbourhood before it
|
|
289
|
+
* blends, visibility to a box and the bend to a ball. The second target is the bent normal itself,
|
|
290
|
+
* `normalize(shading normal + bend)`, octahedral in `rg16uint` as the G-buffer normal is, which is what lighting
|
|
291
|
+
* reads.
|
|
292
|
+
*
|
|
293
|
+
* @type {ImageShader}
|
|
294
|
+
*/
|
|
173
295
|
export const ao_reproject_shader = new ImageShader({
|
|
174
296
|
descriptor: ShaderDescriptor.from({
|
|
175
297
|
label: "AO Reproject",
|
|
@@ -177,10 +299,16 @@ export const ao_reproject_shader = new ImageShader({
|
|
|
177
299
|
resources,
|
|
178
300
|
}),
|
|
179
301
|
depthStencil: DEPTH_STENCIL_FOREGROUND_ONLY,
|
|
180
|
-
targets: [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
302
|
+
targets: [
|
|
303
|
+
{
|
|
304
|
+
format: "rgba16float",
|
|
305
|
+
// Background is skipped, so it keeps the clear: not occluded, not bent.
|
|
306
|
+
// This is also next frame's history, whose filtered fetch reaches across silhouettes.
|
|
307
|
+
clearValue: [1, 0, 0, 0],
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
label: "Bent Normals",
|
|
311
|
+
format: "rg16uint",
|
|
312
|
+
},
|
|
313
|
+
]
|
|
314
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment_shader_gtao.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fragment_shader_gtao.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js"],"names":[],"mappings":"AA6WA,+CAyBG;4BAxXyB,6BAA6B"}
|
|
@@ -6,7 +6,6 @@ import { chunk_project_view_position_from_depth } from "../../shader/chunk/camer
|
|
|
6
6
|
import { chunk_get_view_space_depth } from "../../shader/chunk/depth/chunk_get_view_space_depth.js";
|
|
7
7
|
import { chunk_mat4_extract_position } from "../../shader/chunk/geometry/mat4/chunk_mat4_extract_position.js";
|
|
8
8
|
import { chunk_decode_g_buffer_normal } from "../../shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.js";
|
|
9
|
-
import { chunk_encode_g_buffer_normal } from "../../shader/chunk/geometry/normal/chunk_encode_g_buffer_normal.js";
|
|
10
9
|
import { chunk_v3_matrix4_rotate } from "../../shader/chunk/geometry/vec3/chunk_v3_matrix4_rotate.js";
|
|
11
10
|
import { chunk_fast_acos } from "../../shader/chunk/math/fast/chunk_fast_acos.js";
|
|
12
11
|
import { PI_DECLARATION } from "../../shader/chunk/math/PI_DECLARATION.js";
|
|
@@ -95,16 +94,11 @@ const SLICE_COUNT = ${QUALITY_SETTINGS.slices};
|
|
|
95
94
|
|
|
96
95
|
const STEPS = ${QUALITY_SETTINGS.steps};
|
|
97
96
|
|
|
98
|
-
struct Output{
|
|
99
|
-
@location(0) visibility: vec2<f32>, // first 2 moment
|
|
100
|
-
@location(1) bent_normal: vec2<u32>, // octahedral encoding
|
|
101
|
-
}
|
|
102
|
-
|
|
103
97
|
@fragment
|
|
104
98
|
fn main(
|
|
105
99
|
@builtin(position) coord: vec4<f32>,
|
|
106
100
|
@location(0) uv: vec2<f32>,
|
|
107
|
-
) ->
|
|
101
|
+
) -> @location(0) vec4<f32> {
|
|
108
102
|
|
|
109
103
|
// fadeout precompute optimisation
|
|
110
104
|
const falloffRange = ${GTAO_EFFECT_FALLOFF_RANGE} * ${GTAO_EFFECT_RADIUS};
|
|
@@ -160,7 +154,22 @@ fn main(
|
|
|
160
154
|
|
|
161
155
|
// this is the min distance to start sampling from to avoid sampling from the center pixel (no useful data obtained from sampling center pixel)
|
|
162
156
|
let minS = pixelTooCloseThreshold / screenspaceRadius;
|
|
163
|
-
|
|
157
|
+
|
|
158
|
+
// The slices' angle is measured around the view ray, from view-space x made perpendicular to it: at the
|
|
159
|
+
// screen's centre that is the screen's own x, as the paper has it. The ray never runs along x inside a frustum.
|
|
160
|
+
let slice_basis_x = normalize(vec3(1.0, 0.0, 0.0) - view_direction_vs * view_direction_vs.x);
|
|
161
|
+
let slice_basis_y = cross(view_direction_vs, slice_basis_x);
|
|
162
|
+
|
|
163
|
+
// This texel through the projection, reading only the entries a projection fills, as
|
|
164
|
+
// project_view_position_from_depth reads its inverse: x from x and z, y from y and z, w from z. The TAA
|
|
165
|
+
// jitter is in the z column of a perspective, the translation column of an orthographic one.
|
|
166
|
+
let projection = camera.projection_matrix;
|
|
167
|
+
let center_clip_xy = vec2(
|
|
168
|
+
fma(position_vs.x, projection[0][0], fma(position_vs.z, projection[2][0], projection[3][0])),
|
|
169
|
+
fma(position_vs.y, projection[1][1], fma(position_vs.z, projection[2][1], projection[3][1])),
|
|
170
|
+
);
|
|
171
|
+
let center_clip_w = fma(position_vs.z, projection[2][3], projection[3][3]);
|
|
172
|
+
|
|
164
173
|
const INV_SLICE_COUNT = 1.0 / f32(SLICE_COUNT);
|
|
165
174
|
|
|
166
175
|
for (var slice = 0; slice < SLICE_COUNT; slice++) {
|
|
@@ -173,12 +182,24 @@ fn main(
|
|
|
173
182
|
let cosPhi = cos(phi);
|
|
174
183
|
let sinPhi = sin(phi);
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
|
|
185
|
+
// line 8 from the paper, with phi the slice's angle around this texel's own view ray: the integral below
|
|
186
|
+
// is written in that angle. The paper takes phi on screen, which is the same angle only at the screen's
|
|
187
|
+
// centre; off it, slices spread evenly on screen bunch around the view ray, the two of a frame stop
|
|
188
|
+
// cancelling, and an open plane's bent normal leans by degrees towards the screen's edges.
|
|
189
|
+
let sliceViewDir = cosPhi * slice_basis_x + sinPhi * slice_basis_y;
|
|
180
190
|
let sliceWorldDir = v3_matrix4_rotate(sliceViewDir, camera.view_matrix_inverse);
|
|
181
191
|
|
|
192
|
+
// The slice's plane holds the view ray, so on screen it is a line through this texel, along the
|
|
193
|
+
// projection's derivative in the slice's direction: (d(clip.xy) * clip.w - clip.xy * d(clip.w)) / clip.w^2,
|
|
194
|
+
// its scale dropped, into uv and then pixels.
|
|
195
|
+
let slice_clip_xy = vec2(
|
|
196
|
+
fma(sliceViewDir.x, projection[0][0], sliceViewDir.z * projection[2][0]),
|
|
197
|
+
fma(sliceViewDir.y, projection[1][1], sliceViewDir.z * projection[2][1]),
|
|
198
|
+
);
|
|
199
|
+
let slice_clip_w = sliceViewDir.z * projection[2][3];
|
|
200
|
+
let slice_screen = (slice_clip_xy * center_clip_w - center_clip_xy * slice_clip_w) * vec2(0.5, -0.5) * vec2<f32>(ViewportSize);
|
|
201
|
+
let omega = normalize(slice_screen) * screenspaceRadius;
|
|
202
|
+
|
|
182
203
|
// line 9 from the paper
|
|
183
204
|
// NOTE: optimized with FMA, readability suffers
|
|
184
205
|
let orthoWorldDir = fma( vec3( -dot(sliceWorldDir, view_direction_ws) ), view_direction_ws, sliceWorldDir );
|
|
@@ -230,7 +251,7 @@ fn main(
|
|
|
230
251
|
// approx lines 21-22 from the paper, unrolled
|
|
231
252
|
var sampleOffset = s * omega;
|
|
232
253
|
|
|
233
|
-
// omega
|
|
254
|
+
// omega is a unit screen direction times screenspaceRadius, so has length screenspaceRadius
|
|
234
255
|
let sampleOffsetLength = s * screenspaceRadius;
|
|
235
256
|
let mipLevel = gtao_compute_mip_level(sampleOffsetLength);
|
|
236
257
|
|
|
@@ -318,12 +339,10 @@ fn main(
|
|
|
318
339
|
visibility = max(0.03, visibility);
|
|
319
340
|
|
|
320
341
|
bent_normal = normalize(bent_normal);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return out;
|
|
342
|
+
|
|
343
|
+
// The bend, not the bent normal: what the horizons add to the shading normal. Unoccluded it is exactly
|
|
344
|
+
// zero, so the filters downstream can average it without averaging away the normal map's detail.
|
|
345
|
+
return vec4(visibility, bent_normal - view_normal_ws);
|
|
327
346
|
}
|
|
328
347
|
`, [
|
|
329
348
|
chunk_get_view_space_depth,
|
|
@@ -331,7 +350,6 @@ fn main(
|
|
|
331
350
|
PI_HALF_DECLARATION,
|
|
332
351
|
|
|
333
352
|
chunk_decode_g_buffer_normal,
|
|
334
|
-
chunk_encode_g_buffer_normal,
|
|
335
353
|
chunk_spatio_temporal_noise_r2_64,
|
|
336
354
|
|
|
337
355
|
chunk_fast_acos,
|
|
@@ -357,9 +375,10 @@ export const fragment_shader_gtao = new ImageShader({
|
|
|
357
375
|
targets: [
|
|
358
376
|
{
|
|
359
377
|
label: "Occlusion",
|
|
360
|
-
//
|
|
361
|
-
format: "
|
|
362
|
-
|
|
378
|
+
// visibility, and the world-space bend: bent normal minus shading normal
|
|
379
|
+
format: "rgba16float",
|
|
380
|
+
// Background is skipped, so it keeps the clear: not occluded, not bent.
|
|
381
|
+
clearValue: [1, 0, 0, 0],
|
|
363
382
|
blend: BlendState.from({
|
|
364
383
|
color: {
|
|
365
384
|
operation: "add",
|
|
@@ -368,10 +387,5 @@ export const fragment_shader_gtao = new ImageShader({
|
|
|
368
387
|
}
|
|
369
388
|
})
|
|
370
389
|
},
|
|
371
|
-
{
|
|
372
|
-
label: "Bent Normals",
|
|
373
|
-
// Direction is encoded with octahedral
|
|
374
|
-
format: "rg16uint",
|
|
375
|
-
},
|
|
376
390
|
]
|
|
377
391
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment_shader_gtao_with_irradiance.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fragment_shader_gtao_with_irradiance.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js"],"names":[],"mappings":"AA0YA,+DAkCG;4BA5ZyB,6BAA6B"}
|