@multiplekex/shallot 0.2.3 → 0.2.5

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.
Files changed (42) hide show
  1. package/package.json +1 -1
  2. package/src/extras/arrows/index.ts +3 -3
  3. package/src/extras/caustic.ts +37 -0
  4. package/src/extras/gradient/index.ts +63 -69
  5. package/src/extras/index.ts +3 -0
  6. package/src/extras/lines/index.ts +3 -3
  7. package/src/extras/skylab/index.ts +314 -0
  8. package/src/extras/text/font.ts +69 -14
  9. package/src/extras/text/index.ts +15 -7
  10. package/src/extras/text/sdf.ts +13 -2
  11. package/src/extras/water.ts +64 -0
  12. package/src/standard/defaults.ts +2 -0
  13. package/src/standard/index.ts +2 -0
  14. package/src/standard/raster/index.ts +517 -0
  15. package/src/standard/{render → raytracing}/bvh/blas.ts +3 -3
  16. package/src/standard/{render → raytracing}/bvh/tlas.ts +3 -0
  17. package/src/standard/{render → raytracing}/depth.ts +9 -9
  18. package/src/standard/raytracing/index.ts +380 -0
  19. package/src/standard/{render → raytracing}/instance.ts +3 -0
  20. package/src/standard/raytracing/shaders.ts +815 -0
  21. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  22. package/src/standard/render/camera.ts +88 -80
  23. package/src/standard/render/index.ts +68 -208
  24. package/src/standard/render/indirect.ts +9 -10
  25. package/src/standard/render/mesh/index.ts +35 -166
  26. package/src/standard/render/overlay.ts +4 -4
  27. package/src/standard/render/pass.ts +1 -1
  28. package/src/standard/render/postprocess.ts +75 -50
  29. package/src/standard/render/scene.ts +28 -16
  30. package/src/standard/render/surface/compile.ts +6 -8
  31. package/src/standard/render/surface/noise.ts +15 -2
  32. package/src/standard/render/surface/shaders.ts +257 -0
  33. package/src/standard/render/surface/structs.ts +13 -6
  34. package/src/standard/render/forward/index.ts +0 -259
  35. package/src/standard/render/forward/raster.ts +0 -228
  36. package/src/standard/render/shaders.ts +0 -484
  37. package/src/standard/render/surface/wgsl.ts +0 -573
  38. /package/src/standard/{render → raytracing}/bvh/radix.ts +0 -0
  39. /package/src/standard/{render → raytracing}/bvh/structs.ts +0 -0
  40. /package/src/standard/{render → raytracing}/bvh/traverse.ts +0 -0
  41. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
  42. /package/src/standard/{render → raytracing}/ray.ts +0 -0
@@ -1,228 +0,0 @@
1
- import type { SurfaceData } from "../surface";
2
- import { SCENE_STRUCT_WGSL, WGSL_STRUCTS } from "../surface/structs";
3
- import { compileVertexBody, WGSL_LIGHTING_CALC, SKY_WGSL, SKY_DIR_WGSL } from "../surface/wgsl";
4
-
5
- function compileSurfaceVariant(id: number, data: SurfaceData): string {
6
- const vertexBody = compileVertexBody(data.vertex);
7
- const fragmentBody = data.fragment ?? "";
8
- const lit = data.lit !== false;
9
-
10
- return `
11
- fn userVertexTransform_${id}(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
12
- ${vertexBody}
13
- }
14
-
15
- fn userFragment_${id}(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
16
- ${fragmentBody}
17
- }
18
-
19
- fn applyLighting_${id}(surface: SurfaceData) -> vec3<f32> {
20
- ${
21
- lit
22
- ? `${WGSL_LIGHTING_CALC}
23
- return surface.baseColor * lighting + surface.emission;`
24
- : "return surface.baseColor;"
25
- }
26
- }
27
- `;
28
- }
29
-
30
- function compileDispatchFunctions(surfaceCount: number): string {
31
- const vertexCases = Array.from(
32
- { length: surfaceCount },
33
- (_, i) => ` case ${i}u: { return userVertexTransform_${i}(worldPos, normal, eid); }`
34
- ).join("\n");
35
-
36
- const fragmentCases = Array.from(
37
- { length: surfaceCount },
38
- (_, i) => ` case ${i}u: { userFragment_${i}(surface, position); }`
39
- ).join("\n");
40
-
41
- const lightingCases = Array.from(
42
- { length: surfaceCount },
43
- (_, i) => ` case ${i}u: { return applyLighting_${i}(surface); }`
44
- ).join("\n");
45
-
46
- return `
47
- fn dispatchVertexTransform(surfaceId: u32, worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
48
- switch surfaceId {
49
- ${vertexCases}
50
- default: { return userVertexTransform_0(worldPos, normal, eid); }
51
- }
52
- }
53
-
54
- fn dispatchFragment(surfaceId: u32, surface: ptr<function, SurfaceData>, position: vec4<f32>) {
55
- switch surfaceId {
56
- ${fragmentCases}
57
- default: { userFragment_0(surface, position); }
58
- }
59
- }
60
-
61
- fn dispatchLighting(surfaceId: u32, surface: SurfaceData) -> vec3<f32> {
62
- switch surfaceId {
63
- ${lightingCases}
64
- default: { return applyLighting_0(surface); }
65
- }
66
- }
67
- `;
68
- }
69
-
70
- export function compileRasterShader(surfaces: SurfaceData[]): string {
71
- const surfaceVariants = surfaces.map((s, i) => compileSurfaceVariant(i, s)).join("\n");
72
- const dispatchFunctions = compileDispatchFunctions(surfaces.length);
73
-
74
- return /* wgsl */ `
75
- ${WGSL_STRUCTS}
76
-
77
- @group(0) @binding(8) var<storage, read> surfaceIds: array<u32>;
78
-
79
- ${surfaceVariants}
80
- ${dispatchFunctions}
81
-
82
- @vertex
83
- fn vs(input: VertexInput) -> VertexOutput {
84
- let eid = entityIds[input.instance];
85
- let world = matrices[eid];
86
- let scaledPos = input.position * sizes[eid].xyz;
87
- let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
88
- let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
89
- let surfaceId = surfaceIds[eid] & 0xFFu;
90
- let finalWorldPos = dispatchVertexTransform(surfaceId, baseWorldPos, worldNormal, eid);
91
- _ = shapes[eid];
92
-
93
- var output: VertexOutput;
94
- output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
95
- output.color = colors[eid];
96
- output.worldNormal = worldNormal;
97
- output.entityId = eid;
98
- output.worldPos = finalWorldPos;
99
- return output;
100
- }
101
-
102
- @fragment
103
- fn fs(input: VertexOutput) -> FragmentOutput {
104
- let eid = input.entityId;
105
- let pbrData = pbr[eid];
106
- let emissionData = emission[eid];
107
- let surfaceId = surfaceIds[eid] & 0xFFu;
108
-
109
- var surface: SurfaceData;
110
- surface.baseColor = input.color.rgb;
111
- surface.roughness = pbrData.x;
112
- surface.metallic = pbrData.y;
113
- surface.emission = emissionData.rgb * emissionData.a;
114
- surface.normal = normalize(input.worldNormal);
115
- surface.worldPos = input.worldPos;
116
-
117
- dispatchFragment(surfaceId, &surface, input.position);
118
-
119
- let litColor = dispatchLighting(surfaceId, surface);
120
-
121
- var output: FragmentOutput;
122
- output.color = vec4<f32>(litColor, input.color.a);
123
- output.entityId = input.entityId;
124
- return output;
125
- }
126
- `;
127
- }
128
-
129
- export async function createRasterPipeline(
130
- device: GPUDevice,
131
- surfaces: SurfaceData[],
132
- colorFormat: GPUTextureFormat
133
- ): Promise<GPURenderPipeline> {
134
- const code = compileRasterShader(surfaces);
135
- const module = device.createShaderModule({ code });
136
-
137
- return device.createRenderPipelineAsync({
138
- layout: "auto",
139
- vertex: {
140
- module,
141
- entryPoint: "vs",
142
- buffers: [
143
- {
144
- arrayStride: 24,
145
- attributes: [
146
- { shaderLocation: 0, offset: 0, format: "float32x3" },
147
- { shaderLocation: 1, offset: 12, format: "float32x3" },
148
- ],
149
- },
150
- ],
151
- },
152
- fragment: {
153
- module,
154
- entryPoint: "fs",
155
- targets: [{ format: colorFormat }, { format: "r32uint" }],
156
- },
157
- depthStencil: {
158
- format: "depth24plus",
159
- depthWriteEnabled: true,
160
- depthCompare: "less",
161
- },
162
- primitive: {
163
- topology: "triangle-list",
164
- cullMode: "back",
165
- },
166
- });
167
- }
168
-
169
- export function compileSkyShader(): string {
170
- return /* wgsl */ `
171
- ${SCENE_STRUCT_WGSL}
172
-
173
- @group(0) @binding(0) var<uniform> scene: Scene;
174
-
175
- ${SKY_DIR_WGSL}
176
- ${SKY_WGSL}
177
-
178
- struct VertexOutput {
179
- @builtin(position) position: vec4<f32>,
180
- @location(0) uv: vec2<f32>,
181
- }
182
-
183
- @vertex
184
- fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
185
- var positions = array<vec2<f32>, 3>(
186
- vec2(-1.0, -1.0),
187
- vec2(3.0, -1.0),
188
- vec2(-1.0, 3.0)
189
- );
190
- var output: VertexOutput;
191
- output.position = vec4(positions[vertexIndex], 0.0, 1.0);
192
- output.uv = (positions[vertexIndex] + 1.0) * 0.5;
193
- output.uv.y = 1.0 - output.uv.y;
194
- return output;
195
- }
196
-
197
- @fragment
198
- fn fs(input: VertexOutput) -> @location(0) vec4<f32> {
199
- let dir = computeSkyDir(input.uv.x, input.uv.y);
200
- let color = sampleSky(dir);
201
- return vec4(color, 1.0);
202
- }
203
- `;
204
- }
205
-
206
- export async function createSkyPipeline(
207
- device: GPUDevice,
208
- colorFormat: GPUTextureFormat
209
- ): Promise<GPURenderPipeline> {
210
- const code = compileSkyShader();
211
- const module = device.createShaderModule({ code });
212
-
213
- return device.createRenderPipelineAsync({
214
- layout: "auto",
215
- vertex: { module, entryPoint: "vs" },
216
- fragment: {
217
- module,
218
- entryPoint: "fs",
219
- targets: [{ format: colorFormat }],
220
- },
221
- depthStencil: {
222
- format: "depth24plus",
223
- depthWriteEnabled: false,
224
- depthCompare: "always",
225
- },
226
- primitive: { topology: "triangle-list" },
227
- });
228
- }
@@ -1,484 +0,0 @@
1
- import type { SurfaceData } from "./surface";
2
- import {
3
- compileVertexBody,
4
- compileApplyLighting,
5
- reflectionWgsl,
6
- SHADOW_WGSL,
7
- SPECULAR_WGSL,
8
- REFRACTION_WGSL,
9
- SKY_DIR_WGSL,
10
- SKY_WGSL,
11
- HAZE_WGSL,
12
- } from "./surface/wgsl";
13
- import {
14
- BVH_STRUCTS,
15
- TLAS_BLAS_STRUCTS,
16
- TLAS_BLAS_BINDINGS,
17
- TLAS_BLAS_TRAVERSAL,
18
- TLAS_BLAS_ANY_HIT,
19
- } from "./bvh/traverse";
20
- import { RAY_STRUCT_WGSL, HIT_RESULT_STRUCT_WGSL } from "./bvh/structs";
21
- import { SURFACE_DATA_STRUCT_WGSL, SCENE_STRUCT_WGSL, DATA_STRUCT_WGSL } from "./surface/structs";
22
-
23
- export { SCENE_STRUCT_WGSL };
24
-
25
- const EPSILON = 1e-7;
26
-
27
- export const RT_STRUCTS = /* wgsl */ `
28
- ${RAY_STRUCT_WGSL}
29
- ${HIT_RESULT_STRUCT_WGSL}
30
- ${SURFACE_DATA_STRUCT_WGSL}
31
- ${SCENE_STRUCT_WGSL}
32
- ${DATA_STRUCT_WGSL}
33
- `;
34
-
35
- export const RT_BINDINGS = /* wgsl */ `
36
- @group(0) @binding(0) var<uniform> scene: Scene;
37
- @group(0) @binding(1) var<storage, read> data: array<Data>;
38
- @group(0) @binding(2) var output_scene: texture_storage_2d<rgba8unorm, write>;
39
- @group(0) @binding(3) var output_depth: texture_storage_2d<r32float, write>;
40
- @group(0) @binding(4) var output_entityId: texture_storage_2d<r32uint, write>;
41
- `;
42
-
43
- export const RT_UTILS = /* wgsl */ `
44
- fn getData(eid: u32) -> Data {
45
- return data[eid];
46
- }
47
-
48
- fn getSurfaceType(eid: u32) -> u32 {
49
- return data[eid].flags & 0xFFu;
50
- }
51
-
52
- fn getVolume(eid: u32) -> u32 {
53
- return (data[eid].flags >> 8u) & 0xFu;
54
- }
55
-
56
- fn getShapeId(eid: u32) -> u32 {
57
- return data[eid].flags >> 16u;
58
- }
59
-
60
- fn getInstanceCount() -> u32 {
61
- return scene.instanceCount;
62
- }
63
- `;
64
-
65
- export const RT_RAY_GEN = /* wgsl */ `
66
- ${SKY_DIR_WGSL}
67
-
68
- struct PrimaryRay {
69
- origin: vec3<f32>,
70
- direction: vec3<f32>,
71
- skyDir: vec3<f32>,
72
- }
73
-
74
- fn generateRay(screenX: f32, screenY: f32) -> PrimaryRay {
75
- var result: PrimaryRay;
76
- result.skyDir = computeSkyDir(screenX, screenY);
77
-
78
- let width = scene.viewport.x;
79
- let height = scene.viewport.y;
80
- let ndcX = screenX * 2.0 - 1.0;
81
- let ndcY = 1.0 - screenY * 2.0;
82
- let aspect = width / height;
83
-
84
- let cameraWorld = scene.cameraWorld;
85
- let camPosX = cameraWorld[3][0];
86
- let camPosY = cameraWorld[3][1];
87
- let camPosZ = cameraWorld[3][2];
88
-
89
- if (scene.cameraMode > 0.5) {
90
- let r00 = cameraWorld[0][0]; let r10 = cameraWorld[0][1]; let r20 = cameraWorld[0][2];
91
- let r01 = cameraWorld[1][0]; let r11 = cameraWorld[1][1]; let r21 = cameraWorld[1][2];
92
- let r02 = cameraWorld[2][0]; let r12 = cameraWorld[2][1]; let r22 = cameraWorld[2][2];
93
-
94
- let halfHeight = scene.cameraSize;
95
- let halfWidth = halfHeight * aspect;
96
- let offsetX = ndcX * halfWidth;
97
- let offsetY = ndcY * halfHeight;
98
- let fwdX = -r02; let fwdY = -r12; let fwdZ = -r22;
99
-
100
- result.origin = vec3(
101
- camPosX + r00 * offsetX + r01 * offsetY + fwdX * scene.near,
102
- camPosY + r10 * offsetX + r11 * offsetY + fwdY * scene.near,
103
- camPosZ + r20 * offsetX + r21 * offsetY + fwdZ * scene.near
104
- );
105
- result.direction = vec3(fwdX, fwdY, fwdZ);
106
- } else {
107
- let dir = result.skyDir;
108
- result.origin = vec3(camPosX + dir.x * scene.near, camPosY + dir.y * scene.near, camPosZ + dir.z * scene.near);
109
- result.direction = dir;
110
- }
111
-
112
- return result;
113
- }
114
- `;
115
-
116
- export const RT_INTERSECTION = /* wgsl */ `
117
- const EPSILON: f32 = ${EPSILON};
118
- `;
119
-
120
- export function compileRTSurface(data: SurfaceData): string {
121
- return compileUberShader([data]);
122
- }
123
-
124
- function compileSurfaceVariant(id: number, data: SurfaceData): string {
125
- const vertexBody = compileVertexBody(data.vertex);
126
- const fragmentBody = data.fragment ?? "";
127
- const lightingCode = compileApplyLighting(data.lit, true, true);
128
-
129
- return `
130
- fn userVertexTransform_${id}(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
131
- ${vertexBody}
132
- }
133
-
134
- fn userFragment_${id}(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
135
- ${fragmentBody}
136
- }
137
-
138
- fn applyLighting_${id}(surface: SurfaceData, rayDir: vec3<f32>) -> vec3<f32> {
139
- ${lightingCode}
140
- }
141
- `;
142
- }
143
-
144
- function compileDispatchFunctions(surfaceCount: number): string {
145
- const vertexCases = Array.from(
146
- { length: surfaceCount },
147
- (_, i) => ` case ${i}u: { return userVertexTransform_${i}(worldPos, normal, eid); }`
148
- ).join("\n");
149
-
150
- const fragmentCases = Array.from(
151
- { length: surfaceCount },
152
- (_, i) => ` case ${i}u: { userFragment_${i}(surface, position); }`
153
- ).join("\n");
154
-
155
- const lightingCases = Array.from(
156
- { length: surfaceCount },
157
- (_, i) => ` case ${i}u: { return applyLighting_${i}(surface, rayDir); }`
158
- ).join("\n");
159
-
160
- return `
161
- fn dispatchVertexTransform(surfaceId: u32, worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
162
- switch surfaceId {
163
- ${vertexCases}
164
- default: { return userVertexTransform_0(worldPos, normal, eid); }
165
- }
166
- }
167
-
168
- fn dispatchFragment(surfaceId: u32, surface: ptr<function, SurfaceData>, position: vec4<f32>) {
169
- switch surfaceId {
170
- ${fragmentCases}
171
- default: { userFragment_0(surface, position); }
172
- }
173
- }
174
-
175
- fn dispatchLighting(surfaceId: u32, surface: SurfaceData, rayDir: vec3<f32>) -> vec3<f32> {
176
- switch surfaceId {
177
- ${lightingCases}
178
- default: { return applyLighting_0(surface, rayDir); }
179
- }
180
- }
181
- `;
182
- }
183
-
184
- export function compileUberShader(
185
- surfaces: SurfaceData[],
186
- _shadows: boolean = true,
187
- _reflections: boolean = true,
188
- _refractions: boolean = true
189
- ): string {
190
- const surfaceVariants = surfaces.map((s, i) => compileSurfaceVariant(i, s)).join("\n");
191
- const dispatchFunctions = compileDispatchFunctions(surfaces.length);
192
-
193
- return /* wgsl */ `
194
- ${RT_STRUCTS}
195
- ${BVH_STRUCTS}
196
- ${TLAS_BLAS_STRUCTS}
197
- ${RT_BINDINGS}
198
- ${RT_UTILS}
199
- ${TLAS_BLAS_BINDINGS}
200
- ${RT_RAY_GEN}
201
- ${RT_INTERSECTION}
202
- ${TLAS_BLAS_TRAVERSAL}
203
- ${TLAS_BLAS_ANY_HIT}
204
- ${SHADOW_WGSL}
205
- ${SPECULAR_WGSL}
206
- ${REFRACTION_WGSL}
207
- ${SKY_WGSL}
208
- ${HAZE_WGSL}
209
- ${reflectionWgsl(true)}
210
-
211
- ${surfaceVariants}
212
- ${dispatchFunctions}
213
-
214
- const MAX_TRANSPARENT_DEPTH: u32 = 4u;
215
- const TRANSPARENCY_EPSILON: f32 = 0.001;
216
- const MAX_REFRACTION_DEPTH: u32 = 2u;
217
- const REFRACTION_EPSILON: f32 = 0.001;
218
-
219
- ${compileRefractionTracer(true)}
220
-
221
- @compute @workgroup_size(8, 8)
222
- fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
223
- let width = u32(scene.viewport.x);
224
- let height = u32(scene.viewport.y);
225
-
226
- if (gid.x >= width || gid.y >= height) {
227
- return;
228
- }
229
-
230
- let screenX = (f32(gid.x) + 0.5) / f32(width);
231
- let screenY = (f32(gid.y) + 0.5) / f32(height);
232
-
233
- let primary = generateRay(screenX, screenY);
234
- var ray: Ray;
235
- ray.origin = primary.origin;
236
- ray.direction = primary.direction;
237
-
238
- var finalColor = vec3(0.0);
239
- var remainingOpacity = 1.0;
240
- var depth = 1e30;
241
- var entityId = 0u;
242
- var firstHit = true;
243
-
244
- for (var i = 0u; i < MAX_TRANSPARENT_DEPTH; i++) {
245
- let hit = trace(ray);
246
-
247
- if (!hit.hit) {
248
- finalColor += sampleSky(primary.skyDir) * remainingOpacity;
249
- break;
250
- }
251
-
252
- let eid = hit.entityId;
253
- let d = getData(eid);
254
- let opacity = d.baseColor.a;
255
- let surfaceId = getSurfaceType(eid);
256
-
257
- if (firstHit) {
258
- depth = hit.t;
259
- entityId = eid;
260
- firstHit = false;
261
- }
262
-
263
- let finalWorldPos = dispatchVertexTransform(surfaceId, hit.worldPos, hit.normal, eid);
264
-
265
- var surface: SurfaceData;
266
- surface.baseColor = d.baseColor.rgb;
267
- surface.roughness = d.pbr.x;
268
- surface.metallic = d.pbr.y;
269
- surface.opacity = opacity;
270
- surface.emission = d.emission.rgb * d.emission.a;
271
- surface.normal = hit.normal;
272
- surface.worldPos = finalWorldPos;
273
-
274
- dispatchFragment(surfaceId, &surface, vec4(f32(gid.x), f32(gid.y), hit.t, 1.0));
275
-
276
- ${compileRefractionBranch()}
277
- let litColor = dispatchLighting(surfaceId, surface, primary.direction);
278
- let hazeColor = applyHaze(litColor, hit.t);
279
-
280
- let contribution = opacity * remainingOpacity;
281
- finalColor += hazeColor * contribution;
282
-
283
- if (opacity >= 1.0) {
284
- break;
285
- }
286
-
287
- remainingOpacity *= (1.0 - opacity);
288
- if (remainingOpacity < 0.02) {
289
- break;
290
- }
291
-
292
- ray.origin = hit.worldPos + ray.direction * TRANSPARENCY_EPSILON;
293
- }
294
-
295
- textureStore(output_scene, vec2<i32>(gid.xy), vec4(finalColor, 1.0));
296
- textureStore(output_depth, vec2<i32>(gid.xy), vec4(depth, 0.0, 0.0, 0.0));
297
- textureStore(output_entityId, vec2<i32>(gid.xy), vec4(entityId, 0u, 0u, 0u));
298
- }
299
- `;
300
- }
301
-
302
- function compileRefractionTracer(_shadows: boolean = true): string {
303
- const shadowCheck = `var hitShadow = 1.0;
304
- if (hitNdotL > 0.0 && scene.shadowSamples > 0u) {
305
- var shadowRay: Ray;
306
- shadowRay.origin = refHit.worldPos + refHit.normal * REFRACTION_EPSILON;
307
- shadowRay.direction = hitL;
308
- if (traceAnyHit(shadowRay, 1000.0)) { hitShadow = 0.0; }
309
- }`;
310
-
311
- return /* wgsl */ `
312
- const VOLUME_SOLID: u32 = 0u;
313
- const VOLUME_HALF_SPACE: u32 = 1u;
314
-
315
- fn traceRefraction(
316
- startPos: vec3<f32>,
317
- startNormal: vec3<f32>,
318
- rayDir: vec3<f32>,
319
- ior: f32,
320
- mediumColor: vec3<f32>,
321
- volumeType: u32,
322
- maxBounces: u32
323
- ) -> vec3<f32> {
324
- var currentPos = startPos;
325
- var currentNormal = startNormal;
326
- var currentDir = rayDir;
327
- var currentIOR = ior;
328
- var currentMediumColor = mediumColor;
329
- var totalDistance = 0.0;
330
- var inMedium = true;
331
- let isHalfSpace = volumeType == VOLUME_HALF_SPACE;
332
-
333
- for (var bounce = 0u; bounce < maxBounces; bounce++) {
334
- let entering = dot(currentDir, currentNormal) < 0.0;
335
- let n = select(-currentNormal, currentNormal, entering);
336
- let n1 = select(currentIOR, 1.0, entering);
337
- let n2 = select(1.0, currentIOR, entering);
338
- let eta = n1 / n2;
339
-
340
- let refractResult = refractRay(currentDir, n, eta);
341
- let isTIR = refractResult.w > 0.5;
342
-
343
- var refractRay_: Ray;
344
- refractRay_.direction = refractResult.xyz;
345
- refractRay_.origin = select(
346
- currentPos - n * REFRACTION_EPSILON,
347
- currentPos + n * REFRACTION_EPSILON,
348
- isTIR
349
- );
350
-
351
- let refHit = trace(refractRay_);
352
- if (!refHit.hit) {
353
- if (isHalfSpace && inMedium) {
354
- let depthFade = exp(-totalDistance * 0.1);
355
- return currentMediumColor * depthFade * scene.ambientColor.rgb * scene.ambientColor.a;
356
- }
357
- var skyColor = sampleSky(refractRay_.direction);
358
- if (inMedium && totalDistance > 0.0) {
359
- let absorption = exp(-totalDistance * 0.5);
360
- skyColor = mix(skyColor * currentMediumColor, skyColor, absorption);
361
- }
362
- return skyColor;
363
- }
364
-
365
- if (inMedium) {
366
- totalDistance += refHit.t;
367
- }
368
-
369
- let refEid = refHit.entityId;
370
- let refracted = getData(refEid);
371
- let refOpacity = refracted.baseColor.a;
372
- let refIOR = refracted.pbr.z;
373
-
374
- if (refIOR <= 1.0) {
375
- let hitBaseColor = refracted.baseColor.rgb;
376
- let hitMetallic = refracted.pbr.y;
377
- let hitEmission = refracted.emission.rgb * refracted.emission.a;
378
-
379
- let hitL = -scene.sunDirection.xyz;
380
- let hitNdotL = max(dot(refHit.normal, hitL), 0.0);
381
- ${shadowCheck}
382
-
383
- let hitAmbient = scene.ambientColor.rgb * scene.ambientColor.a;
384
- let hitSun = scene.sunColor.rgb * hitNdotL * hitShadow;
385
- var hitDiffuse = hitBaseColor * (hitAmbient + hitSun) * (1.0 - hitMetallic);
386
-
387
- if (totalDistance > 0.0) {
388
- let absorption = exp(-totalDistance * 0.5);
389
- hitDiffuse = mix(hitDiffuse * currentMediumColor, hitDiffuse, absorption);
390
- }
391
-
392
- var surfaceColor = applyHaze(hitDiffuse + hitEmission, refHit.t);
393
-
394
- if (refOpacity >= 1.0) {
395
- return surfaceColor;
396
- }
397
-
398
- var behindRay: Ray;
399
- behindRay.origin = refHit.worldPos + refractRay_.direction * REFRACTION_EPSILON;
400
- behindRay.direction = refractRay_.direction;
401
- let behindHit = trace(behindRay);
402
- var behindColor = sampleSky(refractRay_.direction);
403
- if (behindHit.hit) {
404
- let behind = getData(behindHit.entityId);
405
- let behindBaseColor = behind.baseColor.rgb;
406
- let behindMetallic = behind.pbr.y;
407
- let behindEmission = behind.emission.rgb * behind.emission.a;
408
- let behindNdotL = max(dot(behindHit.normal, hitL), 0.0);
409
- let behindAmbient = scene.ambientColor.rgb * scene.ambientColor.a;
410
- let behindSun = scene.sunColor.rgb * behindNdotL;
411
- behindColor = applyHaze(behindBaseColor * (behindAmbient + behindSun) * (1.0 - behindMetallic) + behindEmission, behindHit.t);
412
- }
413
-
414
- return surfaceColor * refOpacity + behindColor * (1.0 - refOpacity);
415
- }
416
-
417
- if (!entering) {
418
- inMedium = false;
419
- } else {
420
- inMedium = true;
421
- currentMediumColor = refracted.baseColor.rgb;
422
- totalDistance = 0.0;
423
- }
424
-
425
- currentPos = refHit.worldPos;
426
- currentNormal = refHit.normal;
427
- currentDir = refractRay_.direction;
428
- currentIOR = refIOR;
429
- }
430
-
431
- if (isHalfSpace && inMedium) {
432
- let depthFade = exp(-totalDistance * 0.1);
433
- return currentMediumColor * depthFade * scene.ambientColor.rgb * scene.ambientColor.a;
434
- }
435
- var skyColor = sampleSky(currentDir);
436
- if (inMedium && totalDistance > 0.0) {
437
- let absorption = exp(-totalDistance * 0.5);
438
- skyColor = mix(skyColor * currentMediumColor, skyColor, absorption);
439
- }
440
- return skyColor;
441
- }
442
- `;
443
- }
444
-
445
- function compileRefractionBranch(): string {
446
- return /* wgsl */ `
447
- let ior = d.pbr.z;
448
- if (ior > 1.0 && scene.refractionDepth > 0u) {
449
- let entering = dot(ray.direction, surface.normal) < 0.0;
450
- let n = select(-surface.normal, surface.normal, entering);
451
- let n1 = select(ior, 1.0, entering);
452
- let n2 = select(1.0, ior, entering);
453
- let cosI = abs(dot(ray.direction, n));
454
- let fresnel = fresnelSchlickIOR(cosI, n1, n2);
455
-
456
- let volumeType = getVolume(eid);
457
- let refractedColor = traceRefraction(
458
- surface.worldPos, surface.normal, ray.direction, ior,
459
- surface.baseColor, volumeType, scene.refractionDepth
460
- );
461
-
462
- let contribution = opacity * remainingOpacity;
463
- let refractContrib = (1.0 - fresnel) * contribution;
464
- finalColor += applyHaze(refractedColor, hit.t) * refractContrib;
465
-
466
- let litColor = dispatchLighting(surfaceId, surface, primary.direction);
467
- finalColor += applyHaze(litColor, hit.t) * fresnel * contribution;
468
-
469
- if (opacity >= 1.0) {
470
- break;
471
- }
472
- remainingOpacity *= (1.0 - opacity);
473
- if (remainingOpacity < 0.02) {
474
- break;
475
- }
476
- ray.origin = hit.worldPos + ray.direction * TRANSPARENCY_EPSILON;
477
- continue;
478
- }
479
- `;
480
- }
481
-
482
- export function compileForwardShader(surface: SurfaceData): string {
483
- return compileUberShader([surface]);
484
- }