@plasius/gpu-lighting 0.2.5 → 0.2.7

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.
@@ -0,0 +1,61 @@
1
+ @group(0) @binding(0) var<uniform> wavefrontLightingParams: WavefrontLightingParams;
2
+ @group(0) @binding(1) var<storage, read> activeQueue: array<RayRecord>;
3
+ @group(0) @binding(2) var<storage, read> hitBuffer: array<HitRecord>;
4
+ @group(0) @binding(3) var<storage, read> surfaceBuffer: array<SurfaceRecord>;
5
+ @group(0) @binding(4) var<storage, read> materialRefBuffer: array<MaterialReferenceRecord>;
6
+ @group(0) @binding(5) var<storage, read> mediumRefBuffer: array<MediumReferenceRecord>;
7
+ @group(0) @binding(6) var<storage, read_write> accumulationBuffer: array<AccumulationRecord>;
8
+
9
+ fn resolve_emissive_radiance(
10
+ material: MaterialReferenceRecord,
11
+ surface: SurfaceRecord
12
+ ) -> vec3<f32> {
13
+ let uv_weight = saturate_scalar(surface.uv.x + surface.uv.y);
14
+ let emissive_hint = select(0.0, 1.0, (material.flags & 0x1u) != 0u);
15
+ let base = material_base_albedo(material);
16
+ return base * (0.5 + uv_weight * 0.5 + emissive_hint * 2.0);
17
+ }
18
+
19
+ fn accumulate_terminal_sample(
20
+ ray: RayRecord,
21
+ hit: HitRecord,
22
+ surface: SurfaceRecord,
23
+ material: MaterialReferenceRecord,
24
+ accumulation_index: u32
25
+ ) {
26
+ let emissive_radiance = resolve_emissive_radiance(material, surface);
27
+ let terminal_radiance = terminal_radiance_for_hit(hit, ray, emissive_radiance);
28
+ if (luminance(terminal_radiance) <= LIGHTING_EPSILON) {
29
+ return;
30
+ }
31
+
32
+ accumulationBuffer[accumulation_index].sourcePixelId = ray.sourcePixelId;
33
+ accumulationBuffer[accumulation_index].sampleCount =
34
+ accumulationBuffer[accumulation_index].sampleCount + 1u;
35
+ accumulationBuffer[accumulation_index].resetEpoch =
36
+ wavefrontLightingParams.accumulation_reset_epoch;
37
+ accumulationBuffer[accumulation_index].radiance =
38
+ accumulationBuffer[accumulation_index].radiance + terminal_radiance;
39
+ accumulationBuffer[accumulation_index].throughput = ray.throughput;
40
+ }
41
+
42
+ @compute @workgroup_size(64, 1, 1)
43
+ fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
44
+ let index = global_id.x;
45
+ if (index >= wavefrontLightingParams.active_count) {
46
+ return;
47
+ }
48
+
49
+ let ray = activeQueue[index];
50
+ let hit = hitBuffer[index];
51
+ if (!is_terminal_hit_type(hit.hitType)) {
52
+ return;
53
+ }
54
+
55
+ let surface = surfaceBuffer[index];
56
+ let material = materialRefBuffer[surface.materialRefId];
57
+ let _medium = mediumRefBuffer[surface.mediumRefId];
58
+ let accumulation_index = min(ray.sourcePixelId, wavefrontLightingParams.active_count - 1u);
59
+
60
+ accumulate_terminal_sample(ray, hit, surface, material, accumulation_index);
61
+ }
@@ -0,0 +1,237 @@
1
+ const LIGHTING_WAVEFRONT_SCHEMA_VERSION: u32 = 1u;
2
+ const LIGHTING_WAVEFRONT_QUEUE_PAIR_STRATEGY: u32 = 1u;
3
+
4
+ const HIT_TYPE_SURFACE: u32 = 0u;
5
+ const HIT_TYPE_EMISSIVE: u32 = 1u;
6
+ const HIT_TYPE_ENVIRONMENT: u32 = 2u;
7
+ const HIT_TYPE_TRANSPARENT: u32 = 3u;
8
+ const HIT_TYPE_MISS: u32 = 4u;
9
+
10
+ const EVENT_KIND_DIFFUSE: u32 = 0u;
11
+ const EVENT_KIND_REFLECTION: u32 = 1u;
12
+ const EVENT_KIND_REFRACTION: u32 = 2u;
13
+ const EVENT_KIND_TRANSPARENCY: u32 = 3u;
14
+ const EVENT_KIND_TERMINATE: u32 = 4u;
15
+
16
+ const RAY_KIND_PATH: u32 = 0u;
17
+ const RAY_KIND_VISIBILITY_PROBE: u32 = 1u;
18
+ const RAY_KIND_MASK: u32 = 0x3u;
19
+
20
+ const LIGHTING_EPSILON: f32 = 0.0001;
21
+ const LIGHTING_INV_PI: f32 = 0.3183098861837907;
22
+
23
+ struct WavefrontLightingParams {
24
+ active_count: u32,
25
+ next_queue_capacity: u32,
26
+ bounce_index: u32,
27
+ max_depth: u32,
28
+ enable_explicit_light_sampling: u32,
29
+ accumulation_reset_epoch: u32,
30
+ environment_mode: u32,
31
+ environment_intensity: f32,
32
+ sunlit_baseline: f32,
33
+ _padding0: vec3<f32>,
34
+ environment_color: vec4<f32>,
35
+ ambient_color: vec4<f32>,
36
+ environment_miss_radiance: vec4<f32>,
37
+ };
38
+
39
+ struct RayRecord {
40
+ rayId: u32,
41
+ parentRayId: u32,
42
+ sourcePixelId: u32,
43
+ sampleId: u32,
44
+ bounce: u32,
45
+ origin: vec3<f32>,
46
+ _padding0: f32,
47
+ direction: vec3<f32>,
48
+ _padding1: f32,
49
+ throughput: vec3<f32>,
50
+ mediumRefId: u32,
51
+ flags: u32,
52
+ };
53
+
54
+ struct HitRecord {
55
+ rayId: u32,
56
+ sourcePixelId: u32,
57
+ hitType: u32,
58
+ _padding0: u32,
59
+ distance: f32,
60
+ entityId: u32,
61
+ instanceId: u32,
62
+ primitiveId: u32,
63
+ materialId: u32,
64
+ _padding1: vec3<u32>,
65
+ barycentrics: vec3<f32>,
66
+ _padding2: f32,
67
+ uv: vec2<f32>,
68
+ _padding3: vec2<f32>,
69
+ geometricNormal: vec3<f32>,
70
+ _padding4: f32,
71
+ shadingNormal: vec3<f32>,
72
+ frontFace: u32,
73
+ };
74
+
75
+ struct SurfaceRecord {
76
+ rayId: u32,
77
+ entityId: u32,
78
+ materialRefId: u32,
79
+ mediumRefId: u32,
80
+ geometricNormal: vec3<f32>,
81
+ _padding0: f32,
82
+ shadingNormal: vec3<f32>,
83
+ _padding1: f32,
84
+ uv: vec2<f32>,
85
+ _padding2: vec2<f32>,
86
+ tangentFrame0: vec3<f32>,
87
+ _padding3: f32,
88
+ tangentFrame1: vec3<f32>,
89
+ _padding4: f32,
90
+ tangentFrame2: vec3<f32>,
91
+ _padding5: f32,
92
+ };
93
+
94
+ struct MaterialReferenceRecord {
95
+ materialRefId: u32,
96
+ materialId: u32,
97
+ shadingModel: u32,
98
+ textureSetId: u32,
99
+ flags: u32,
100
+ _padding0: vec3<u32>,
101
+ };
102
+
103
+ struct MediumReferenceRecord {
104
+ mediumRefId: u32,
105
+ mediumId: u32,
106
+ phaseModel: u32,
107
+ _padding0: u32,
108
+ absorption: vec3<f32>,
109
+ _padding1: f32,
110
+ scattering: vec3<f32>,
111
+ _padding2: f32,
112
+ };
113
+
114
+ struct AccumulationRecord {
115
+ sourcePixelId: u32,
116
+ sampleCount: u32,
117
+ resetEpoch: u32,
118
+ _padding0: u32,
119
+ radiance: vec3<f32>,
120
+ _padding1: f32,
121
+ throughput: vec3<f32>,
122
+ _padding2: f32,
123
+ };
124
+
125
+ fn luminance(value: vec3<f32>) -> f32 {
126
+ return dot(value, vec3<f32>(0.2126, 0.7152, 0.0722));
127
+ }
128
+
129
+ fn saturate_scalar(value: f32) -> f32 {
130
+ return clamp(value, 0.0, 1.0);
131
+ }
132
+
133
+ fn saturate_vec3(value: vec3<f32>) -> vec3<f32> {
134
+ return max(value, vec3<f32>(0.0));
135
+ }
136
+
137
+ fn safe_normalize(value: vec3<f32>) -> vec3<f32> {
138
+ if (dot(value, value) <= LIGHTING_EPSILON) {
139
+ return vec3<f32>(0.0, 1.0, 0.0);
140
+ }
141
+ return normalize(value);
142
+ }
143
+
144
+ fn faceforward_normal(normal: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
145
+ return select(normal, -normal, dot(normal, direction) > 0.0);
146
+ }
147
+
148
+ fn is_terminal_hit_type(hit_type: u32) -> bool {
149
+ return hit_type == HIT_TYPE_EMISSIVE ||
150
+ hit_type == HIT_TYPE_ENVIRONMENT ||
151
+ hit_type == HIT_TYPE_MISS;
152
+ }
153
+
154
+ fn ray_kind(flags: u32) -> u32 {
155
+ return flags & RAY_KIND_MASK;
156
+ }
157
+
158
+ fn environment_radiance(direction: vec3<f32>) -> vec3<f32> {
159
+ let upward = saturate_scalar(direction.y * 0.5 + 0.5);
160
+ let directional = mix(
161
+ wavefrontLightingParams.environment_color.xyz * 0.45,
162
+ wavefrontLightingParams.environment_color.xyz,
163
+ upward
164
+ );
165
+ let ambient = wavefrontLightingParams.ambient_color.xyz * 0.18;
166
+ let miss = wavefrontLightingParams.environment_miss_radiance.xyz;
167
+ return saturate_vec3(max(directional + ambient, miss));
168
+ }
169
+
170
+ fn terminal_radiance_for_hit(
171
+ hit: HitRecord,
172
+ ray: RayRecord,
173
+ emissive_radiance: vec3<f32>
174
+ ) -> vec3<f32> {
175
+ if (hit.hitType == HIT_TYPE_EMISSIVE) {
176
+ return ray.throughput * saturate_vec3(emissive_radiance);
177
+ }
178
+ if (hit.hitType == HIT_TYPE_ENVIRONMENT) {
179
+ return ray.throughput * environment_radiance(ray.direction);
180
+ }
181
+ if (hit.hitType == HIT_TYPE_MISS) {
182
+ let miss_radiance = max(
183
+ environment_radiance(ray.direction),
184
+ wavefrontLightingParams.environment_miss_radiance.xyz
185
+ );
186
+ return ray.throughput * saturate_vec3(miss_radiance);
187
+ }
188
+ return vec3<f32>(0.0);
189
+ }
190
+
191
+ fn reflect_direction(direction: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
192
+ return safe_normalize(reflect(direction, normal));
193
+ }
194
+
195
+ fn refract_direction(direction: vec3<f32>, normal: vec3<f32>, eta_ratio: f32) -> vec3<f32> {
196
+ let refracted = refract(direction, normal, eta_ratio);
197
+ if (dot(refracted, refracted) <= LIGHTING_EPSILON) {
198
+ return reflect_direction(direction, normal);
199
+ }
200
+ return safe_normalize(refracted);
201
+ }
202
+
203
+ fn choose_continuation_event_kind(hit: HitRecord, material: MaterialReferenceRecord) -> u32 {
204
+ if (hit.hitType == HIT_TYPE_TRANSPARENT || (material.flags & 0x4u) != 0u) {
205
+ return EVENT_KIND_TRANSPARENCY;
206
+ }
207
+ if ((material.flags & 0x8u) != 0u) {
208
+ return EVENT_KIND_REFRACTION;
209
+ }
210
+ if ((material.flags & 0x2u) != 0u) {
211
+ return EVENT_KIND_REFLECTION;
212
+ }
213
+ return EVENT_KIND_DIFFUSE;
214
+ }
215
+
216
+ fn material_base_albedo(material: MaterialReferenceRecord) -> vec3<f32> {
217
+ let tint = vec3<f32>(
218
+ f32((material.materialId & 0xffu)) / 255.0,
219
+ f32((material.textureSetId & 0xffu)) / 255.0,
220
+ f32(((material.materialId ^ material.textureSetId) & 0xffu)) / 255.0
221
+ );
222
+ return max(tint, vec3<f32>(0.12, 0.12, 0.12));
223
+ }
224
+
225
+ fn continuation_attenuation(event_kind: u32, material: MaterialReferenceRecord) -> vec3<f32> {
226
+ let base_albedo = material_base_albedo(material);
227
+ if (event_kind == EVENT_KIND_REFLECTION) {
228
+ return mix(vec3<f32>(0.04), base_albedo, 0.7);
229
+ }
230
+ if (event_kind == EVENT_KIND_REFRACTION) {
231
+ return vec3<f32>(0.92, 0.94, 0.98);
232
+ }
233
+ if (event_kind == EVENT_KIND_TRANSPARENCY) {
234
+ return vec3<f32>(0.75, 0.82, 0.9);
235
+ }
236
+ return base_albedo * LIGHTING_INV_PI;
237
+ }
@@ -0,0 +1,136 @@
1
+ @group(0) @binding(0) var<uniform> wavefrontLightingParams: WavefrontLightingParams;
2
+ @group(0) @binding(1) var<storage, read> activeQueue: array<RayRecord>;
3
+ @group(0) @binding(2) var<storage, read> hitBuffer: array<HitRecord>;
4
+ @group(0) @binding(3) var<storage, read> surfaceBuffer: array<SurfaceRecord>;
5
+ @group(0) @binding(4) var<storage, read> materialRefBuffer: array<MaterialReferenceRecord>;
6
+ @group(0) @binding(5) var<storage, read> mediumRefBuffer: array<MediumReferenceRecord>;
7
+ @group(0) @binding(6) var<storage, read_write> nextQueue: array<RayRecord>;
8
+ @group(0) @binding(7) var<storage, read_write> nextQueueCounter: atomic<u32>;
9
+
10
+ fn queue_continuation_ray(
11
+ ray: RayRecord,
12
+ hit: HitRecord,
13
+ surface: SurfaceRecord,
14
+ direction: vec3<f32>,
15
+ attenuation: vec3<f32>
16
+ ) {
17
+ let slot = atomicAdd(&nextQueueCounter, 1u);
18
+ if (slot >= wavefrontLightingParams.next_queue_capacity) {
19
+ return;
20
+ }
21
+
22
+ let offset_origin =
23
+ ray.origin +
24
+ ray.direction * hit.distance +
25
+ faceforward_normal(surface.geometricNormal, ray.direction) * 0.001;
26
+ nextQueue[slot] = RayRecord(
27
+ ray.rayId ^ ((wavefrontLightingParams.bounce_index + 1u) * 0x9e3779b9u),
28
+ ray.rayId,
29
+ ray.sourcePixelId,
30
+ ray.sampleId,
31
+ wavefrontLightingParams.bounce_index + 1u,
32
+ offset_origin,
33
+ 0.0,
34
+ direction,
35
+ 0.0,
36
+ ray.throughput * attenuation,
37
+ surface.mediumRefId,
38
+ ray.flags
39
+ );
40
+ }
41
+
42
+ fn queue_reflection_continuation(
43
+ ray: RayRecord,
44
+ hit: HitRecord,
45
+ surface: SurfaceRecord,
46
+ material: MaterialReferenceRecord
47
+ ) {
48
+ let facing_normal = faceforward_normal(surface.shadingNormal, ray.direction);
49
+ let direction = reflect_direction(ray.direction, facing_normal);
50
+ let attenuation = continuation_attenuation(EVENT_KIND_REFLECTION, material);
51
+ queue_continuation_ray(ray, hit, surface, direction, attenuation);
52
+ }
53
+
54
+ fn queue_refraction_continuation(
55
+ ray: RayRecord,
56
+ hit: HitRecord,
57
+ surface: SurfaceRecord,
58
+ material: MaterialReferenceRecord
59
+ ) {
60
+ let front_face = hit.frontFace != 0u;
61
+ let facing_normal = faceforward_normal(surface.shadingNormal, ray.direction);
62
+ let eta_ratio = select(1.45, 1.0 / 1.45, front_face);
63
+ let direction = refract_direction(ray.direction, facing_normal, eta_ratio);
64
+ let attenuation = continuation_attenuation(EVENT_KIND_REFRACTION, material);
65
+ queue_continuation_ray(ray, hit, surface, direction, attenuation);
66
+ }
67
+
68
+ fn queue_transparency_continuation(
69
+ ray: RayRecord,
70
+ hit: HitRecord,
71
+ surface: SurfaceRecord,
72
+ material: MaterialReferenceRecord
73
+ ) {
74
+ let attenuation = continuation_attenuation(EVENT_KIND_TRANSPARENCY, material);
75
+ queue_continuation_ray(ray, hit, surface, ray.direction, attenuation);
76
+ }
77
+
78
+ fn queue_diffuse_continuation(
79
+ ray: RayRecord,
80
+ hit: HitRecord,
81
+ surface: SurfaceRecord,
82
+ material: MaterialReferenceRecord
83
+ ) {
84
+ let facing_normal = faceforward_normal(surface.shadingNormal, ray.direction);
85
+ let attenuation = continuation_attenuation(EVENT_KIND_DIFFUSE, material);
86
+ let bias =
87
+ surface.tangentFrame0 * (surface.uv.x - 0.5) +
88
+ surface.tangentFrame1 * (surface.uv.y - 0.5) +
89
+ facing_normal;
90
+ let direction = safe_normalize(bias);
91
+ queue_continuation_ray(ray, hit, surface, direction, attenuation);
92
+ }
93
+
94
+ @compute @workgroup_size(64, 1, 1)
95
+ fn process_job(@builtin(global_invocation_id) global_id: vec3<u32>) {
96
+ let index = global_id.x;
97
+ if (index >= wavefrontLightingParams.active_count) {
98
+ return;
99
+ }
100
+ if (wavefrontLightingParams.bounce_index + 1u >= wavefrontLightingParams.max_depth) {
101
+ return;
102
+ }
103
+
104
+ let ray = activeQueue[index];
105
+ let hit = hitBuffer[index];
106
+ if (is_terminal_hit_type(hit.hitType) || hit.hitType == HIT_TYPE_MISS) {
107
+ return;
108
+ }
109
+
110
+ let surface = surfaceBuffer[index];
111
+ let material = materialRefBuffer[surface.materialRefId];
112
+ let _medium = mediumRefBuffer[surface.mediumRefId];
113
+ let event_kind = choose_continuation_event_kind(hit, material);
114
+ let explicit_light_sampling_enabled = wavefrontLightingParams.enable_explicit_light_sampling != 0u;
115
+
116
+ if (event_kind == EVENT_KIND_REFLECTION) {
117
+ queue_reflection_continuation(ray, hit, surface, material);
118
+ return;
119
+ }
120
+ if (event_kind == EVENT_KIND_REFRACTION) {
121
+ queue_refraction_continuation(ray, hit, surface, material);
122
+ return;
123
+ }
124
+ if (event_kind == EVENT_KIND_TRANSPARENCY) {
125
+ queue_transparency_continuation(ray, hit, surface, material);
126
+ return;
127
+ }
128
+
129
+ if (explicit_light_sampling_enabled) {
130
+ // Explicit light sampling remains optional for correctness in this first slice.
131
+ queue_diffuse_continuation(ray, hit, surface, material);
132
+ return;
133
+ }
134
+
135
+ queue_diffuse_continuation(ray, hit, surface, material);
136
+ }