@plasius/gpu-lighting 0.1.16 → 0.1.18
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 +43 -0
- package/README.md +54 -0
- package/dist/index.cjs +105 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -1
- package/dist/techniques/techniques/hybrid/direct-lighting.job.wgsl +69 -2
- package/dist/techniques/techniques/hybrid/final-gather.job.wgsl +62 -2
- package/dist/techniques/techniques/hybrid/prelude.wgsl +140 -0
- package/dist/techniques/techniques/hybrid/radiance-cache.job.wgsl +57 -2
- package/dist/techniques/techniques/hybrid/reflection-resolve.job.wgsl +235 -2
- package/dist/techniques/techniques/hybrid/screen-trace.job.wgsl +219 -2
- package/dist/techniques/techniques/pathtracer/accumulate.job.wgsl +64 -2
- package/dist/techniques/techniques/pathtracer/denoise.job.wgsl +125 -2
- package/dist/techniques/techniques/pathtracer/pathtrace.job.wgsl +429 -2
- package/dist/techniques/techniques/pathtracer/prelude.wgsl +260 -0
- package/package.json +1 -1
- package/src/index.js +114 -0
- package/src/techniques/hybrid/direct-lighting.job.wgsl +69 -2
- package/src/techniques/hybrid/final-gather.job.wgsl +62 -2
- package/src/techniques/hybrid/prelude.wgsl +140 -0
- package/src/techniques/hybrid/radiance-cache.job.wgsl +57 -2
- package/src/techniques/hybrid/reflection-resolve.job.wgsl +235 -2
- package/src/techniques/hybrid/screen-trace.job.wgsl +219 -2
- package/src/techniques/pathtracer/accumulate.job.wgsl +64 -2
- package/src/techniques/pathtracer/denoise.job.wgsl +125 -2
- package/src/techniques/pathtracer/pathtrace.job.wgsl +429 -2
- package/src/techniques/pathtracer/prelude.wgsl +260 -0
|
@@ -1,3 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
@group(0) @binding(0) var<uniform> hybridFrameParams: HybridFrameParams;
|
|
2
|
+
@group(0) @binding(1) var<storage, read> hybridReflectionSurfaces: array<HybridReflectionSurface>;
|
|
3
|
+
@group(0) @binding(2) var<storage, read> hybridDirectLightingInput: array<HybridLightingPixel>;
|
|
4
|
+
@group(0) @binding(3) var<storage, read> hybridRadianceCacheHistory: array<HybridRadianceCacheEntry>;
|
|
5
|
+
@group(0) @binding(4) var<storage, read_write> hybridRadianceCacheOutput: array<HybridRadianceCacheEntry>;
|
|
6
|
+
|
|
7
|
+
fn radiance_cache_index(pixel: vec2<u32>) -> u32 {
|
|
8
|
+
return pixel.y * max(hybridFrameParams.image_width, 1u) + pixel.x;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@compute @workgroup_size(8, 8, 1)
|
|
12
|
+
fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
13
|
+
if (
|
|
14
|
+
global_id.x >= hybridFrameParams.image_width ||
|
|
15
|
+
global_id.y >= hybridFrameParams.image_height
|
|
16
|
+
) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let index = radiance_cache_index(global_id.xy);
|
|
21
|
+
let surface = hybridReflectionSurfaces[index];
|
|
22
|
+
let direct = hybridDirectLightingInput[index];
|
|
23
|
+
let previous = hybridRadianceCacheHistory[index];
|
|
24
|
+
let normal = hybrid_safe_normalize(surface.normal_roughness.xyz);
|
|
25
|
+
let roughness = clamp(surface.normal_roughness.w + hybridFrameParams.roughness_bias, 0.02, 1.0);
|
|
26
|
+
let occlusion = hybrid_saturate(surface.emission_occlusion.w);
|
|
27
|
+
let sky_probe = hybrid_environment(normal, hybridFrameParams.sky_intensity, hybridFrameParams.sky_mode);
|
|
28
|
+
let direct_irradiance = direct.radiance_confidence.xyz * (0.28 + (1.0 - roughness) * 0.12);
|
|
29
|
+
let cache_fill = sky_probe * (0.08 + occlusion * 0.12);
|
|
30
|
+
let current_irradiance = direct_irradiance + cache_fill;
|
|
31
|
+
let bent_normal = hybrid_safe_normalize(
|
|
32
|
+
normal * (0.75 + occlusion * 0.25) +
|
|
33
|
+
previous.bent_normal_depth.xyz * previous.irradiance_validity.w * 0.2
|
|
34
|
+
);
|
|
35
|
+
let history_weight = select(
|
|
36
|
+
0.0,
|
|
37
|
+
encode_history_weight(hybridFrameParams.history_weight) * previous.irradiance_validity.w,
|
|
38
|
+
hybridFrameParams.reflection_reset == 0u && previous.irradiance_validity.w > 0.0
|
|
39
|
+
);
|
|
40
|
+
let resolved_irradiance =
|
|
41
|
+
previous.irradiance_validity.xyz * history_weight +
|
|
42
|
+
current_irradiance * (1.0 - history_weight);
|
|
43
|
+
let validity = clamp(
|
|
44
|
+
hybrid_luminance(resolved_irradiance) * 0.05 +
|
|
45
|
+
occlusion * 0.35 +
|
|
46
|
+
(1.0 - roughness) * 0.15,
|
|
47
|
+
0.0,
|
|
48
|
+
1.0
|
|
49
|
+
);
|
|
50
|
+
let probe_depth =
|
|
51
|
+
previous.bent_normal_depth.w * history_weight +
|
|
52
|
+
length(surface.position.xyz) * (1.0 - history_weight);
|
|
53
|
+
|
|
54
|
+
hybridRadianceCacheOutput[index] = HybridRadianceCacheEntry(
|
|
55
|
+
vec4<f32>(resolved_irradiance, validity),
|
|
56
|
+
vec4<f32>(bent_normal, probe_depth)
|
|
57
|
+
);
|
|
3
58
|
}
|
|
@@ -1,3 +1,236 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
@group(0) @binding(0) var<uniform> hybridFrameParams: HybridFrameParams;
|
|
2
|
+
@group(0) @binding(1) var<uniform> hybridReflectionCamera: HybridReflectionCamera;
|
|
3
|
+
@group(0) @binding(2) var<storage, read> hybridReflectionSurfaces: array<HybridReflectionSurface>;
|
|
4
|
+
@group(0) @binding(3) var<storage, read> hybridReflectionHistory: array<HybridReflectionPixel>;
|
|
5
|
+
@group(0) @binding(4) var<storage, read> hybridReflectionScene: HybridReflectionSceneMetadata;
|
|
6
|
+
@group(0) @binding(5) var<uniform> hybridGroundPlane: HybridGroundPlane;
|
|
7
|
+
@group(0) @binding(6) var<storage, read> hybridReflectionMaterials: array<HybridReflectionMaterial>;
|
|
8
|
+
@group(0) @binding(7) var<storage, read> hybridReflectionSpheres: array<HybridReflectionSphere>;
|
|
9
|
+
@group(0) @binding(8) var<storage, read_write> hybridReflectionOutput: array<HybridReflectionPixel>;
|
|
10
|
+
|
|
11
|
+
fn reflection_index(pixel: vec2<u32>) -> u32 {
|
|
12
|
+
return pixel.y * max(hybridFrameParams.image_width, 1u) + pixel.x;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn unpack_reflection_material(index: u32) -> HybridReflectionMaterial {
|
|
16
|
+
if (hybridReflectionScene.material_count == 0u) {
|
|
17
|
+
return HybridReflectionMaterial(
|
|
18
|
+
vec4<f32>(0.7, 0.72, 0.76, 0.4),
|
|
19
|
+
vec4<f32>(0.0, 0.0, 0.0, 0.0)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let safe_index = min(index, hybridReflectionScene.material_count - 1u);
|
|
24
|
+
return hybridReflectionMaterials[safe_index];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fn miss_reflection() -> HybridReflectionTrace {
|
|
28
|
+
return HybridReflectionTrace(
|
|
29
|
+
0u,
|
|
30
|
+
hybridReflectionScene.max_trace_distance,
|
|
31
|
+
vec3<f32>(0.0),
|
|
32
|
+
0u,
|
|
33
|
+
vec3<f32>(0.0, 1.0, 0.0),
|
|
34
|
+
0u
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn set_best_reflection(best: ptr<function, HybridReflectionTrace>, candidate: HybridReflectionTrace) {
|
|
39
|
+
if (candidate.hit_mask == 1u && candidate.distance < (*best).distance) {
|
|
40
|
+
*best = candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn trace_reflection_ground(
|
|
45
|
+
origin: vec3<f32>,
|
|
46
|
+
direction: vec3<f32>,
|
|
47
|
+
best: ptr<function, HybridReflectionTrace>
|
|
48
|
+
) {
|
|
49
|
+
if (hybridGroundPlane.enabled == 0u) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let plane_normal = hybrid_safe_normalize(hybridGroundPlane.normal);
|
|
54
|
+
let denominator = dot(plane_normal, direction);
|
|
55
|
+
if (abs(denominator) <= 0.0005) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let distance = -(dot(plane_normal, origin) + hybridGroundPlane.height) / denominator;
|
|
60
|
+
if (distance <= 0.0005 || distance >= (*best).distance || distance >= hybridReflectionScene.max_trace_distance) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
set_best_reflection(
|
|
65
|
+
best,
|
|
66
|
+
HybridReflectionTrace(
|
|
67
|
+
1u,
|
|
68
|
+
distance,
|
|
69
|
+
origin + direction * distance,
|
|
70
|
+
hybridGroundPlane.material_index,
|
|
71
|
+
plane_normal,
|
|
72
|
+
0u
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn trace_reflection_spheres(
|
|
78
|
+
origin: vec3<f32>,
|
|
79
|
+
direction: vec3<f32>,
|
|
80
|
+
best: ptr<function, HybridReflectionTrace>
|
|
81
|
+
) {
|
|
82
|
+
var index = 0u;
|
|
83
|
+
loop {
|
|
84
|
+
if (index >= hybridReflectionScene.sphere_count) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let sphere = hybridReflectionSpheres[index];
|
|
89
|
+
let offset = origin - sphere.center_radius.xyz;
|
|
90
|
+
let a = dot(direction, direction);
|
|
91
|
+
let half_b = dot(offset, direction);
|
|
92
|
+
let c = dot(offset, offset) - sphere.center_radius.w * sphere.center_radius.w;
|
|
93
|
+
let discriminant = half_b * half_b - a * c;
|
|
94
|
+
if (discriminant >= 0.0) {
|
|
95
|
+
let root = sqrt(discriminant);
|
|
96
|
+
var distance = (-half_b - root) / max(a, 0.0005);
|
|
97
|
+
if (distance <= 0.0005) {
|
|
98
|
+
distance = (-half_b + root) / max(a, 0.0005);
|
|
99
|
+
}
|
|
100
|
+
if (distance > 0.0005 && distance < (*best).distance && distance < hybridReflectionScene.max_trace_distance) {
|
|
101
|
+
let position = origin + direction * distance;
|
|
102
|
+
let normal = hybrid_safe_normalize(position - sphere.center_radius.xyz);
|
|
103
|
+
set_best_reflection(
|
|
104
|
+
best,
|
|
105
|
+
HybridReflectionTrace(
|
|
106
|
+
1u,
|
|
107
|
+
distance,
|
|
108
|
+
position,
|
|
109
|
+
sphere.material_index,
|
|
110
|
+
normal,
|
|
111
|
+
0u
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
index = index + 1u;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn trace_reflection_scene(origin: vec3<f32>, direction: vec3<f32>) -> HybridReflectionTrace {
|
|
122
|
+
var best = miss_reflection();
|
|
123
|
+
trace_reflection_ground(origin, direction, &best);
|
|
124
|
+
trace_reflection_spheres(origin, direction, &best);
|
|
125
|
+
return best;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn evaluate_hit_lighting(trace: HybridReflectionTrace, view_direction: vec3<f32>) -> vec3<f32> {
|
|
129
|
+
let material = unpack_reflection_material(trace.material_index);
|
|
130
|
+
let surface_normal = hybrid_safe_normalize(trace.normal);
|
|
131
|
+
let light_direction = hybrid_safe_normalize(vec3<f32>(0.31, 0.92, 0.22));
|
|
132
|
+
let ndotl = hybrid_saturate(dot(surface_normal, light_direction));
|
|
133
|
+
let halfway = hybrid_safe_normalize(light_direction + view_direction);
|
|
134
|
+
let roughness = clamp(material.albedo_roughness.w, 0.02, 1.0);
|
|
135
|
+
let metalness = hybrid_saturate(material.emission_metalness.w);
|
|
136
|
+
let albedo = material.albedo_roughness.xyz;
|
|
137
|
+
let f0 = vec3<f32>(0.04) * (1.0 - metalness) + albedo * metalness;
|
|
138
|
+
let fresnel = hybrid_fresnel_schlick(
|
|
139
|
+
hybrid_saturate(dot(surface_normal, view_direction)),
|
|
140
|
+
f0
|
|
141
|
+
);
|
|
142
|
+
let diffuse = albedo * ndotl * 0.3183098861837907 * (1.0 - metalness);
|
|
143
|
+
let specular_power = 12.0 + (1.0 - roughness) * 96.0;
|
|
144
|
+
let specular = fresnel * pow(hybrid_saturate(dot(surface_normal, halfway)), specular_power) * ndotl;
|
|
145
|
+
let direct_light = vec3<f32>(5.4, 5.0, 4.6) * ndotl;
|
|
146
|
+
let environment = hybrid_environment(surface_normal, hybridFrameParams.sky_intensity, hybridFrameParams.sky_mode);
|
|
147
|
+
return material.emission_metalness.xyz + (diffuse + specular) * direct_light + environment * 0.08;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@compute @workgroup_size(8, 8, 1)
|
|
151
|
+
fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
152
|
+
if (
|
|
153
|
+
global_id.x >= hybridFrameParams.image_width ||
|
|
154
|
+
global_id.y >= hybridFrameParams.image_height
|
|
155
|
+
) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let index = reflection_index(global_id.xy);
|
|
160
|
+
let surface = hybridReflectionSurfaces[index];
|
|
161
|
+
let previous = hybridReflectionHistory[index];
|
|
162
|
+
let position = surface.position.xyz;
|
|
163
|
+
let normal = hybrid_safe_normalize(surface.normal_roughness.xyz);
|
|
164
|
+
let roughness = clamp(surface.normal_roughness.w + hybridFrameParams.roughness_bias, 0.02, 1.0);
|
|
165
|
+
let albedo = surface.albedo_metalness.xyz;
|
|
166
|
+
let metalness = hybrid_saturate(surface.albedo_metalness.w);
|
|
167
|
+
let occlusion = hybrid_saturate(surface.emission_occlusion.w);
|
|
168
|
+
let view_direction = hybrid_safe_normalize(hybridReflectionCamera.position - position);
|
|
169
|
+
let f0 = vec3<f32>(0.04) * (1.0 - metalness) + albedo * metalness;
|
|
170
|
+
let fresnel = hybrid_fresnel_schlick(hybrid_saturate(dot(normal, view_direction)), f0);
|
|
171
|
+
let grazing_boost = pow(1.0 - hybrid_saturate(dot(normal, view_direction)), 5.0);
|
|
172
|
+
let reflection_budget = clamp(
|
|
173
|
+
(metalness * 0.85 + max(fresnel.x, max(fresnel.y, fresnel.z))) *
|
|
174
|
+
(1.0 - roughness * 0.65) +
|
|
175
|
+
grazing_boost * 0.2,
|
|
176
|
+
0.0,
|
|
177
|
+
1.0
|
|
178
|
+
);
|
|
179
|
+
let trace_needed = reflection_budget > 0.02;
|
|
180
|
+
var random_state = hybrid_hash_u32(
|
|
181
|
+
hybridFrameParams.frame_index * 747796405u +
|
|
182
|
+
index * 2891336453u +
|
|
183
|
+
0x27d4eb2du
|
|
184
|
+
);
|
|
185
|
+
let reflection_direction = hybrid_safe_normalize(
|
|
186
|
+
reflect(-view_direction, normal) +
|
|
187
|
+
hybrid_sample_unit_sphere(&random_state) * roughness * roughness * 0.35
|
|
188
|
+
);
|
|
189
|
+
var trace = miss_reflection();
|
|
190
|
+
if (trace_needed) {
|
|
191
|
+
trace = trace_reflection_scene(
|
|
192
|
+
position + normal * max(hybridFrameParams.thickness, 0.0005),
|
|
193
|
+
reflection_direction
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
let hit_radiance = select(
|
|
197
|
+
hybrid_environment(reflection_direction, hybridFrameParams.sky_intensity, hybridFrameParams.sky_mode),
|
|
198
|
+
evaluate_hit_lighting(trace, -reflection_direction),
|
|
199
|
+
trace.hit_mask == 1u
|
|
200
|
+
);
|
|
201
|
+
let roughness_mix = roughness * roughness;
|
|
202
|
+
let distant_fade = exp(
|
|
203
|
+
-trace.distance /
|
|
204
|
+
max(hybridFrameParams.max_reflection_distance * 0.65, 0.0005)
|
|
205
|
+
);
|
|
206
|
+
let reflection_color =
|
|
207
|
+
hit_radiance * reflection_budget * distant_fade * occlusion;
|
|
208
|
+
let sky_lobe = hybrid_environment(normal, hybridFrameParams.sky_intensity, hybridFrameParams.sky_mode);
|
|
209
|
+
let shaped_reflection =
|
|
210
|
+
reflection_color * (1.0 - roughness_mix * 0.35) +
|
|
211
|
+
sky_lobe * roughness_mix * 0.18 * reflection_budget;
|
|
212
|
+
let history_weight = select(
|
|
213
|
+
0.0,
|
|
214
|
+
encode_history_weight(hybridFrameParams.history_weight),
|
|
215
|
+
hybridFrameParams.reflection_reset == 0u && previous.reflection_confidence.w > 0.0
|
|
216
|
+
);
|
|
217
|
+
let resolved =
|
|
218
|
+
previous.reflection_confidence.xyz * history_weight +
|
|
219
|
+
shaped_reflection * (1.0 - history_weight);
|
|
220
|
+
let confidence = clamp(
|
|
221
|
+
reflection_budget * (0.45 + distant_fade * 0.55) * (1.0 - roughness * 0.35),
|
|
222
|
+
0.0,
|
|
223
|
+
1.0
|
|
224
|
+
);
|
|
225
|
+
let resolved_normal = select(normal, hybrid_safe_normalize(trace.normal), trace.hit_mask == 1u);
|
|
226
|
+
let resolved_distance = select(
|
|
227
|
+
hybridFrameParams.max_reflection_distance,
|
|
228
|
+
trace.distance,
|
|
229
|
+
trace.hit_mask == 1u
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
hybridReflectionOutput[index] = HybridReflectionPixel(
|
|
233
|
+
vec4<f32>(resolved * hybridFrameParams.exposure, confidence),
|
|
234
|
+
vec4<f32>(resolved_normal, resolved_distance)
|
|
235
|
+
);
|
|
3
236
|
}
|
|
@@ -1,3 +1,220 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
@group(0) @binding(0) var<uniform> hybridFrameParams: HybridFrameParams;
|
|
2
|
+
@group(0) @binding(1) var<uniform> hybridReflectionCamera: HybridReflectionCamera;
|
|
3
|
+
@group(0) @binding(2) var<storage, read> hybridReflectionSurfaces: array<HybridReflectionSurface>;
|
|
4
|
+
@group(0) @binding(3) var<storage, read> hybridScreenTraceHistory: array<HybridScreenTracePixel>;
|
|
5
|
+
@group(0) @binding(4) var<storage, read> hybridReflectionScene: HybridReflectionSceneMetadata;
|
|
6
|
+
@group(0) @binding(5) var<uniform> hybridGroundPlane: HybridGroundPlane;
|
|
7
|
+
@group(0) @binding(6) var<storage, read> hybridReflectionMaterials: array<HybridReflectionMaterial>;
|
|
8
|
+
@group(0) @binding(7) var<storage, read> hybridReflectionSpheres: array<HybridReflectionSphere>;
|
|
9
|
+
@group(0) @binding(8) var<storage, read_write> hybridScreenTraceOutput: array<HybridScreenTracePixel>;
|
|
10
|
+
|
|
11
|
+
fn screen_trace_index(pixel: vec2<u32>) -> u32 {
|
|
12
|
+
return pixel.y * max(hybridFrameParams.image_width, 1u) + pixel.x;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn unpack_reflection_material(index: u32) -> HybridReflectionMaterial {
|
|
16
|
+
if (hybridReflectionScene.material_count == 0u) {
|
|
17
|
+
return HybridReflectionMaterial(
|
|
18
|
+
vec4<f32>(0.7, 0.72, 0.76, 0.4),
|
|
19
|
+
vec4<f32>(0.0, 0.0, 0.0, 0.0)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let safe_index = min(index, hybridReflectionScene.material_count - 1u);
|
|
24
|
+
return hybridReflectionMaterials[safe_index];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fn miss_trace() -> HybridReflectionTrace {
|
|
28
|
+
return HybridReflectionTrace(
|
|
29
|
+
0u,
|
|
30
|
+
hybridReflectionScene.max_trace_distance,
|
|
31
|
+
vec3<f32>(0.0),
|
|
32
|
+
0u,
|
|
33
|
+
vec3<f32>(0.0, 1.0, 0.0),
|
|
34
|
+
0u
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn set_best_trace(best: ptr<function, HybridReflectionTrace>, candidate: HybridReflectionTrace) {
|
|
39
|
+
if (candidate.hit_mask == 1u && candidate.distance < (*best).distance) {
|
|
40
|
+
*best = candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn trace_ground(
|
|
45
|
+
origin: vec3<f32>,
|
|
46
|
+
direction: vec3<f32>,
|
|
47
|
+
best: ptr<function, HybridReflectionTrace>
|
|
48
|
+
) {
|
|
49
|
+
if (hybridGroundPlane.enabled == 0u) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let plane_normal = hybrid_safe_normalize(hybridGroundPlane.normal);
|
|
54
|
+
let denominator = dot(plane_normal, direction);
|
|
55
|
+
if (abs(denominator) <= 0.0005) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let distance = -(dot(plane_normal, origin) + hybridGroundPlane.height) / denominator;
|
|
60
|
+
if (distance <= 0.0005 || distance >= (*best).distance || distance >= hybridReflectionScene.max_trace_distance) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
set_best_trace(
|
|
65
|
+
best,
|
|
66
|
+
HybridReflectionTrace(
|
|
67
|
+
1u,
|
|
68
|
+
distance,
|
|
69
|
+
origin + direction * distance,
|
|
70
|
+
hybridGroundPlane.material_index,
|
|
71
|
+
plane_normal,
|
|
72
|
+
0u
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn trace_spheres(
|
|
78
|
+
origin: vec3<f32>,
|
|
79
|
+
direction: vec3<f32>,
|
|
80
|
+
best: ptr<function, HybridReflectionTrace>
|
|
81
|
+
) {
|
|
82
|
+
var index = 0u;
|
|
83
|
+
loop {
|
|
84
|
+
if (index >= hybridReflectionScene.sphere_count) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let sphere = hybridReflectionSpheres[index];
|
|
89
|
+
let offset = origin - sphere.center_radius.xyz;
|
|
90
|
+
let a = dot(direction, direction);
|
|
91
|
+
let half_b = dot(offset, direction);
|
|
92
|
+
let c = dot(offset, offset) - sphere.center_radius.w * sphere.center_radius.w;
|
|
93
|
+
let discriminant = half_b * half_b - a * c;
|
|
94
|
+
if (discriminant >= 0.0) {
|
|
95
|
+
let root = sqrt(discriminant);
|
|
96
|
+
var distance = (-half_b - root) / max(a, 0.0005);
|
|
97
|
+
if (distance <= 0.0005) {
|
|
98
|
+
distance = (-half_b + root) / max(a, 0.0005);
|
|
99
|
+
}
|
|
100
|
+
if (distance > 0.0005 && distance < (*best).distance && distance < hybridReflectionScene.max_trace_distance) {
|
|
101
|
+
let position = origin + direction * distance;
|
|
102
|
+
let normal = hybrid_safe_normalize(position - sphere.center_radius.xyz);
|
|
103
|
+
set_best_trace(
|
|
104
|
+
best,
|
|
105
|
+
HybridReflectionTrace(
|
|
106
|
+
1u,
|
|
107
|
+
distance,
|
|
108
|
+
position,
|
|
109
|
+
sphere.material_index,
|
|
110
|
+
normal,
|
|
111
|
+
0u
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
index = index + 1u;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn trace_screen_scene(origin: vec3<f32>, direction: vec3<f32>) -> HybridReflectionTrace {
|
|
122
|
+
var best = miss_trace();
|
|
123
|
+
trace_ground(origin, direction, &best);
|
|
124
|
+
trace_spheres(origin, direction, &best);
|
|
125
|
+
return best;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn evaluate_hit_radiance(trace: HybridReflectionTrace, reflection_direction: vec3<f32>) -> vec3<f32> {
|
|
129
|
+
let material = unpack_reflection_material(trace.material_index);
|
|
130
|
+
let normal = hybrid_safe_normalize(trace.normal);
|
|
131
|
+
let view_direction = -reflection_direction;
|
|
132
|
+
let light_direction = hybrid_safe_normalize(vec3<f32>(0.31, 0.92, 0.22));
|
|
133
|
+
let ndotl = hybrid_saturate(dot(normal, light_direction));
|
|
134
|
+
let halfway = hybrid_safe_normalize(light_direction + view_direction);
|
|
135
|
+
let roughness = clamp(material.albedo_roughness.w, 0.02, 1.0);
|
|
136
|
+
let metalness = hybrid_saturate(material.emission_metalness.w);
|
|
137
|
+
let albedo = material.albedo_roughness.xyz;
|
|
138
|
+
let fresnel = hybrid_fresnel_schlick(
|
|
139
|
+
hybrid_saturate(dot(normal, view_direction)),
|
|
140
|
+
hybrid_surface_f0(albedo, metalness)
|
|
141
|
+
);
|
|
142
|
+
let diffuse = albedo * ndotl * 0.3183098861837907 * (1.0 - metalness);
|
|
143
|
+
let specular = fresnel * pow(hybrid_saturate(dot(normal, halfway)), 12.0 + (1.0 - roughness) * 84.0) * ndotl;
|
|
144
|
+
let sky_fill = hybrid_environment(normal, hybridFrameParams.sky_intensity, hybridFrameParams.sky_mode);
|
|
145
|
+
return material.emission_metalness.xyz + (diffuse + specular) * vec3<f32>(4.8, 4.5, 4.1) + sky_fill * 0.1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@compute @workgroup_size(8, 8, 1)
|
|
149
|
+
fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
150
|
+
if (
|
|
151
|
+
global_id.x >= hybridFrameParams.image_width ||
|
|
152
|
+
global_id.y >= hybridFrameParams.image_height
|
|
153
|
+
) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let index = screen_trace_index(global_id.xy);
|
|
158
|
+
let surface = hybridReflectionSurfaces[index];
|
|
159
|
+
let previous = hybridScreenTraceHistory[index];
|
|
160
|
+
let position = surface.position.xyz;
|
|
161
|
+
let normal = hybrid_safe_normalize(surface.normal_roughness.xyz);
|
|
162
|
+
let roughness = clamp(surface.normal_roughness.w + hybridFrameParams.roughness_bias, 0.02, 1.0);
|
|
163
|
+
let albedo = surface.albedo_metalness.xyz;
|
|
164
|
+
let metalness = hybrid_saturate(surface.albedo_metalness.w);
|
|
165
|
+
let occlusion = hybrid_saturate(surface.emission_occlusion.w);
|
|
166
|
+
let view_direction = hybrid_safe_normalize(hybridReflectionCamera.position - position);
|
|
167
|
+
var random_state = hybrid_hash_u32(
|
|
168
|
+
hybridFrameParams.frame_index * 1664525u +
|
|
169
|
+
index * 1013904223u +
|
|
170
|
+
0x68bc21ebu
|
|
171
|
+
);
|
|
172
|
+
let reflected_direction = hybrid_safe_normalize(
|
|
173
|
+
reflect(-view_direction, normal) +
|
|
174
|
+
hybrid_sample_unit_sphere(&random_state) * roughness * roughness * 0.25
|
|
175
|
+
);
|
|
176
|
+
let trace = trace_screen_scene(
|
|
177
|
+
position + normal * max(hybridFrameParams.thickness, 0.0005),
|
|
178
|
+
reflected_direction
|
|
179
|
+
);
|
|
180
|
+
let sky_fallback = hybrid_environment(
|
|
181
|
+
reflected_direction,
|
|
182
|
+
hybridFrameParams.sky_intensity,
|
|
183
|
+
hybridFrameParams.sky_mode
|
|
184
|
+
);
|
|
185
|
+
let hit_radiance = select(
|
|
186
|
+
sky_fallback,
|
|
187
|
+
evaluate_hit_radiance(trace, reflected_direction),
|
|
188
|
+
trace.hit_mask == 1u
|
|
189
|
+
);
|
|
190
|
+
let reflection_budget = clamp(
|
|
191
|
+
max(hybrid_surface_f0(albedo, metalness).x, max(albedo.y * metalness, albedo.z * metalness)) +
|
|
192
|
+
(1.0 - roughness) * 0.45,
|
|
193
|
+
0.0,
|
|
194
|
+
1.0
|
|
195
|
+
);
|
|
196
|
+
let trace_confidence = clamp(
|
|
197
|
+
reflection_budget * occlusion * select(0.35, 0.9, trace.hit_mask == 1u),
|
|
198
|
+
0.0,
|
|
199
|
+
1.0
|
|
200
|
+
);
|
|
201
|
+
let history_weight = select(
|
|
202
|
+
0.0,
|
|
203
|
+
encode_history_weight(hybridFrameParams.history_weight),
|
|
204
|
+
hybridFrameParams.reflection_reset == 0u && previous.radiance_confidence.w > 0.0
|
|
205
|
+
);
|
|
206
|
+
let resolved_radiance =
|
|
207
|
+
previous.radiance_confidence.xyz * history_weight +
|
|
208
|
+
hit_radiance * trace_confidence * (1.0 - history_weight);
|
|
209
|
+
let resolved_normal = select(normal, hybrid_safe_normalize(trace.normal), trace.hit_mask == 1u);
|
|
210
|
+
let resolved_distance = select(
|
|
211
|
+
hybridFrameParams.max_reflection_distance,
|
|
212
|
+
trace.distance,
|
|
213
|
+
trace.hit_mask == 1u
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
hybridScreenTraceOutput[index] = HybridScreenTracePixel(
|
|
217
|
+
vec4<f32>(resolved_radiance * hybridFrameParams.exposure, trace_confidence),
|
|
218
|
+
vec4<f32>(resolved_normal, resolved_distance)
|
|
219
|
+
);
|
|
3
220
|
}
|
|
@@ -1,3 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
@group(0) @binding(0) var<uniform> pathTracerParams: PathTracerParams;
|
|
2
|
+
@group(0) @binding(1) var<storage, read> pathSampleBuffer: array<PathSamplePixel>;
|
|
3
|
+
@group(0) @binding(2) var<storage, read_write> pathAccumulationBuffer: array<PathAccumulationPixel>;
|
|
4
|
+
|
|
5
|
+
fn accumulation_index(pixel: vec2<u32>) -> u32 {
|
|
6
|
+
return pixel.y * max(pathTracerParams.image_width, 1u) + pixel.x;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
fn reset_accumulation(sample: PathSamplePixel) -> PathAccumulationPixel {
|
|
10
|
+
let sample_luminance = luminance(sample.radiance_opacity.xyz);
|
|
11
|
+
return PathAccumulationPixel(
|
|
12
|
+
vec4<f32>(sample.radiance_opacity.xyz, max(sample.albedo_sample_count.w, 1.0)),
|
|
13
|
+
vec4<f32>(sample_luminance, 0.0, 0.0, f32(pathTracerParams.frame_index))
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@compute @workgroup_size(8, 8, 1)
|
|
18
|
+
fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
19
|
+
if (
|
|
20
|
+
global_id.x >= pathTracerParams.image_width ||
|
|
21
|
+
global_id.y >= pathTracerParams.image_height
|
|
22
|
+
) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let index = accumulation_index(global_id.xy);
|
|
27
|
+
let sample = pathSampleBuffer[index];
|
|
28
|
+
let history = pathAccumulationBuffer[index];
|
|
29
|
+
let sample_luminance = luminance(sample.radiance_opacity.xyz);
|
|
30
|
+
let current_samples = max(sample.albedo_sample_count.w, 1.0);
|
|
31
|
+
let history_samples = max(history.integrated_radiance.w, 0.0);
|
|
32
|
+
let should_reset =
|
|
33
|
+
pathTracerParams.accumulation_reset != 0u ||
|
|
34
|
+
history_samples <= 0.0 ||
|
|
35
|
+
history.moments.w <= 0.0;
|
|
36
|
+
|
|
37
|
+
if (should_reset) {
|
|
38
|
+
pathAccumulationBuffer[index] = reset_accumulation(sample);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let combined_samples = min(history_samples + current_samples, 4096.0);
|
|
43
|
+
let progressive_weight = current_samples / max(combined_samples, 1.0);
|
|
44
|
+
let minimum_refresh = 1.0 - clamp(pathTracerParams.history_blend, 0.0, 0.98);
|
|
45
|
+
let blend = clamp(max(progressive_weight, minimum_refresh), 0.02, 1.0);
|
|
46
|
+
let integrated =
|
|
47
|
+
history.integrated_radiance.xyz * (1.0 - blend) +
|
|
48
|
+
sample.radiance_opacity.xyz * blend;
|
|
49
|
+
let mean_luminance =
|
|
50
|
+
history.moments.x * (1.0 - blend) +
|
|
51
|
+
sample_luminance * blend;
|
|
52
|
+
let variance_sample = sample_luminance - mean_luminance;
|
|
53
|
+
let variance =
|
|
54
|
+
max(0.0, history.moments.y * (1.0 - blend) + variance_sample * variance_sample * blend);
|
|
55
|
+
|
|
56
|
+
pathAccumulationBuffer[index] = PathAccumulationPixel(
|
|
57
|
+
vec4<f32>(integrated, combined_samples),
|
|
58
|
+
vec4<f32>(
|
|
59
|
+
mean_luminance,
|
|
60
|
+
variance,
|
|
61
|
+
1.0 - blend,
|
|
62
|
+
f32(pathTracerParams.frame_index)
|
|
63
|
+
)
|
|
64
|
+
);
|
|
3
65
|
}
|