@tinypoly/gpu-renderer 0.0.0-stage → 0.0.0
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/ARCHITECTURE.md +334 -0
- package/LICENSE +21 -0
- package/README.md +258 -2
- package/dist/accumulator.d.ts +27 -0
- package/dist/accumulator.d.ts.map +1 -0
- package/dist/accumulator.js +57 -0
- package/dist/accumulator.js.map +1 -0
- package/dist/bvh.d.ts +26 -0
- package/dist/bvh.d.ts.map +1 -0
- package/dist/bvh.js +207 -0
- package/dist/bvh.js.map +1 -0
- package/dist/camera.d.ts +29 -0
- package/dist/camera.d.ts.map +1 -0
- package/dist/camera.js +48 -0
- package/dist/camera.js.map +1 -0
- package/dist/canvas.d.ts +9 -0
- package/dist/canvas.d.ts.map +1 -0
- package/dist/canvas.js +11 -0
- package/dist/canvas.js.map +1 -0
- package/dist/denoise.d.ts +69 -0
- package/dist/denoise.d.ts.map +1 -0
- package/dist/denoise.js +455 -0
- package/dist/denoise.js.map +1 -0
- package/dist/device.d.ts +37 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +52 -0
- package/dist/device.js.map +1 -0
- package/dist/dispatchScheduler.d.ts +43 -0
- package/dist/dispatchScheduler.d.ts.map +1 -0
- package/dist/dispatchScheduler.js +49 -0
- package/dist/dispatchScheduler.js.map +1 -0
- package/dist/environment.d.ts +39 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +168 -0
- package/dist/environment.js.map +1 -0
- package/dist/frameLayout.d.ts +4 -0
- package/dist/frameLayout.d.ts.map +1 -0
- package/dist/frameLayout.js +58 -0
- package/dist/frameLayout.js.map +1 -0
- package/dist/geometry.d.ts +75 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +671 -0
- package/dist/geometry.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/lights.d.ts +64 -0
- package/dist/lights.d.ts.map +1 -0
- package/dist/lights.js +264 -0
- package/dist/lights.js.map +1 -0
- package/dist/materialLayout.d.ts +143 -0
- package/dist/materialLayout.d.ts.map +1 -0
- package/dist/materialLayout.js +297 -0
- package/dist/materialLayout.js.map +1 -0
- package/dist/math.d.ts +10 -0
- package/dist/math.d.ts.map +1 -0
- package/dist/math.js +40 -0
- package/dist/math.js.map +1 -0
- package/dist/pathTracePass.d.ts +27 -0
- package/dist/pathTracePass.d.ts.map +1 -0
- package/dist/pathTracePass.js +83 -0
- package/dist/pathTracePass.js.map +1 -0
- package/dist/planarCapture.d.ts +38 -0
- package/dist/planarCapture.d.ts.map +1 -0
- package/dist/planarCapture.js +163 -0
- package/dist/planarCapture.js.map +1 -0
- package/dist/presentPass.d.ts +31 -0
- package/dist/presentPass.d.ts.map +1 -0
- package/dist/presentPass.js +78 -0
- package/dist/presentPass.js.map +1 -0
- package/dist/renderer.d.ts +196 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +839 -0
- package/dist/renderer.js.map +1 -0
- package/dist/sceneAssets.d.ts +24 -0
- package/dist/sceneAssets.d.ts.map +1 -0
- package/dist/sceneAssets.js +77 -0
- package/dist/sceneAssets.js.map +1 -0
- package/dist/sceneUpload.d.ts +61 -0
- package/dist/sceneUpload.d.ts.map +1 -0
- package/dist/sceneUpload.js +98 -0
- package/dist/sceneUpload.js.map +1 -0
- package/dist/shaderModule.d.ts +7 -0
- package/dist/shaderModule.d.ts.map +1 -0
- package/dist/shaderModule.js +19 -0
- package/dist/shaderModule.js.map +1 -0
- package/dist/shaderProgram.d.ts +124 -0
- package/dist/shaderProgram.d.ts.map +1 -0
- package/dist/shaderProgram.js +1030 -0
- package/dist/shaderProgram.js.map +1 -0
- package/dist/structLayout.d.ts +28 -0
- package/dist/structLayout.d.ts.map +1 -0
- package/dist/structLayout.js +58 -0
- package/dist/structLayout.js.map +1 -0
- package/dist/texturePack.d.ts +54 -0
- package/dist/texturePack.d.ts.map +1 -0
- package/dist/texturePack.js +86 -0
- package/dist/texturePack.js.map +1 -0
- package/dist/upstream.d.ts +7 -0
- package/dist/upstream.d.ts.map +1 -0
- package/dist/upstream.js +11 -0
- package/dist/upstream.js.map +1 -0
- package/dist/volumetric.d.ts +10 -0
- package/dist/volumetric.d.ts.map +1 -0
- package/dist/volumetric.js +34 -0
- package/dist/volumetric.js.map +1 -0
- package/dist/wgsl/bsdf.wgsl.d.ts +13 -0
- package/dist/wgsl/bsdf.wgsl.d.ts.map +1 -0
- package/dist/wgsl/bsdf.wgsl.js +619 -0
- package/dist/wgsl/bsdf.wgsl.js.map +1 -0
- package/dist/wgsl/bvh.wgsl.d.ts +3 -0
- package/dist/wgsl/bvh.wgsl.d.ts.map +1 -0
- package/dist/wgsl/bvh.wgsl.js +218 -0
- package/dist/wgsl/bvh.wgsl.js.map +1 -0
- package/dist/wgsl/common.wgsl.d.ts +3 -0
- package/dist/wgsl/common.wgsl.d.ts.map +1 -0
- package/dist/wgsl/common.wgsl.js +193 -0
- package/dist/wgsl/common.wgsl.js.map +1 -0
- package/dist/wgsl/environment.wgsl.d.ts +10 -0
- package/dist/wgsl/environment.wgsl.d.ts.map +1 -0
- package/dist/wgsl/environment.wgsl.js +145 -0
- package/dist/wgsl/environment.wgsl.js.map +1 -0
- package/dist/wgsl/lights.wgsl.d.ts +10 -0
- package/dist/wgsl/lights.wgsl.d.ts.map +1 -0
- package/dist/wgsl/lights.wgsl.js +434 -0
- package/dist/wgsl/lights.wgsl.js.map +1 -0
- package/dist/wgsl/pathTrace.wgsl.d.ts +14 -0
- package/dist/wgsl/pathTrace.wgsl.d.ts.map +1 -0
- package/dist/wgsl/pathTrace.wgsl.js +1020 -0
- package/dist/wgsl/pathTrace.wgsl.js.map +1 -0
- package/dist/wgsl/present.wgsl.d.ts +7 -0
- package/dist/wgsl/present.wgsl.d.ts.map +1 -0
- package/dist/wgsl/present.wgsl.js +154 -0
- package/dist/wgsl/present.wgsl.js.map +1 -0
- package/dist/wgsl/random.wgsl.d.ts +9 -0
- package/dist/wgsl/random.wgsl.d.ts.map +1 -0
- package/dist/wgsl/random.wgsl.js +91 -0
- package/dist/wgsl/random.wgsl.js.map +1 -0
- package/dist/wgsl/sampling.wgsl.d.ts +3 -0
- package/dist/wgsl/sampling.wgsl.d.ts.map +1 -0
- package/dist/wgsl/sampling.wgsl.js +31 -0
- package/dist/wgsl/sampling.wgsl.js.map +1 -0
- package/dist/wgsl/shaderSupport.wgsl.d.ts +6 -0
- package/dist/wgsl/shaderSupport.wgsl.d.ts.map +1 -0
- package/dist/wgsl/shaderSupport.wgsl.js +120 -0
- package/dist/wgsl/shaderSupport.wgsl.js.map +1 -0
- package/dist/wgsl/surface.wgsl.d.ts +10 -0
- package/dist/wgsl/surface.wgsl.d.ts.map +1 -0
- package/dist/wgsl/surface.wgsl.js +794 -0
- package/dist/wgsl/surface.wgsl.js.map +1 -0
- package/dist/wgsl/texture.wgsl.d.ts +11 -0
- package/dist/wgsl/texture.wgsl.d.ts.map +1 -0
- package/dist/wgsl/texture.wgsl.js +132 -0
- package/dist/wgsl/texture.wgsl.js.map +1 -0
- package/package.json +76 -3
- package/src/accumulator.ts +64 -0
- package/src/bvh.ts +272 -0
- package/src/camera.ts +77 -0
- package/src/canvas.ts +13 -0
- package/src/denoise.ts +514 -0
- package/src/device.ts +81 -0
- package/src/dispatchScheduler.ts +71 -0
- package/src/environment.ts +233 -0
- package/src/frameLayout.ts +59 -0
- package/src/geometry.ts +899 -0
- package/src/index.ts +61 -0
- package/src/lights.ts +365 -0
- package/src/materialLayout.ts +408 -0
- package/src/math.ts +48 -0
- package/src/pathTracePass.ts +97 -0
- package/src/planarCapture.ts +225 -0
- package/src/presentPass.ts +99 -0
- package/src/renderer.ts +1117 -0
- package/src/sceneAssets.ts +118 -0
- package/src/sceneUpload.ts +185 -0
- package/src/shaderModule.ts +19 -0
- package/src/shaderProgram.ts +1370 -0
- package/src/structLayout.ts +93 -0
- package/src/texturePack.ts +152 -0
- package/src/upstream.ts +56 -0
- package/src/volumetric.ts +42 -0
- package/src/wgsl/bsdf.wgsl.ts +618 -0
- package/src/wgsl/bvh.wgsl.ts +217 -0
- package/src/wgsl/common.wgsl.ts +193 -0
- package/src/wgsl/environment.wgsl.ts +144 -0
- package/src/wgsl/lights.wgsl.ts +433 -0
- package/src/wgsl/pathTrace.wgsl.ts +1019 -0
- package/src/wgsl/present.wgsl.ts +153 -0
- package/src/wgsl/random.wgsl.ts +90 -0
- package/src/wgsl/sampling.wgsl.ts +30 -0
- package/src/wgsl/shaderSupport.wgsl.ts +119 -0
- package/src/wgsl/surface.wgsl.ts +793 -0
- package/src/wgsl/texture.wgsl.ts +131 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The presentation pass: divides the accumulation buffer by its sample count and applies exposure, tone mapping,
|
|
3
|
+
* vignette and grain. The operators are ports of `color.ts` in the CPU renderer, which are in turn Three's ACES, AgX
|
|
4
|
+
* and Khronos neutral curves, so the two renderers agree on the display transform even when they disagree on the light.
|
|
5
|
+
*/
|
|
6
|
+
export const PRESENT_WGSL = /* wgsl */ `
|
|
7
|
+
struct Present {
|
|
8
|
+
resolution: vec2<u32>,
|
|
9
|
+
exposure: f32,
|
|
10
|
+
toneMapping: u32,
|
|
11
|
+
vignette: f32,
|
|
12
|
+
grain: f32,
|
|
13
|
+
frameIndex: u32,
|
|
14
|
+
// A transparent background: the canvas gets the coverage as alpha, premultiplied as it is configured.
|
|
15
|
+
transparent: u32,
|
|
16
|
+
// Whether the colour comes from the denoiser's output rather than straight from the accumulation.
|
|
17
|
+
denoised: u32,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
@group(0) @binding(0) var<uniform> present: Present;
|
|
21
|
+
@group(0) @binding(1) var<storage, read> accumulation: array<vec4<f32>>;
|
|
22
|
+
@group(0) @binding(2) var<storage, read> denoised: array<vec4<f32>>;
|
|
23
|
+
|
|
24
|
+
@vertex
|
|
25
|
+
fn vertexMain(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> {
|
|
26
|
+
// One oversized triangle covers the viewport with no vertex buffer.
|
|
27
|
+
let x = f32(index / 2u) * 4.0 - 1.0;
|
|
28
|
+
let y = f32(index & 1u) * 4.0 - 1.0;
|
|
29
|
+
|
|
30
|
+
return vec4<f32>(x, y, 0.0, 1.0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn toneMapAces(color: vec3<f32>) -> vec3<f32> {
|
|
34
|
+
let toAces = mat3x3<f32>(
|
|
35
|
+
vec3<f32>(0.59719, 0.076, 0.0284),
|
|
36
|
+
vec3<f32>(0.35458, 0.90834, 0.13383),
|
|
37
|
+
vec3<f32>(0.04823, 0.01566, 0.83777));
|
|
38
|
+
let fromAces = mat3x3<f32>(
|
|
39
|
+
vec3<f32>(1.60475, -0.10208, -0.00327),
|
|
40
|
+
vec3<f32>(-0.53108, 1.10813, -0.07276),
|
|
41
|
+
vec3<f32>(-0.07367, -0.00605, 1.07602));
|
|
42
|
+
let v = toAces * (color / 0.6);
|
|
43
|
+
|
|
44
|
+
return fromAces * ((v * (v + 0.0245786) - 0.000090537) / (v * (0.983729 * v + 0.432951) + 0.238081));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn toneMapAgx(color: vec3<f32>) -> vec3<f32> {
|
|
48
|
+
let toAgx = mat3x3<f32>(
|
|
49
|
+
vec3<f32>(0.6274, 0.0691, 0.0164),
|
|
50
|
+
vec3<f32>(0.3293, 0.9195, 0.088),
|
|
51
|
+
vec3<f32>(0.0433, 0.0113, 0.8956));
|
|
52
|
+
let inset = mat3x3<f32>(
|
|
53
|
+
vec3<f32>(0.856627153315983, 0.137318972929847, 0.11189821299995),
|
|
54
|
+
vec3<f32>(0.0951212405381588, 0.761241990602591, 0.0767994186031903),
|
|
55
|
+
vec3<f32>(0.0482516061458583, 0.101439036467562, 0.811302368396859));
|
|
56
|
+
let outset = mat3x3<f32>(
|
|
57
|
+
vec3<f32>(1.1271005818144368, -0.1413297634984383, -0.14132976349843826),
|
|
58
|
+
vec3<f32>(-0.11060664309660323, 1.157823702216272, -0.11060664309660294),
|
|
59
|
+
vec3<f32>(-0.016493938717834573, -0.016493938717834257, 1.2519364065950405));
|
|
60
|
+
let fromAgx = mat3x3<f32>(
|
|
61
|
+
vec3<f32>(1.6605, -0.1246, -0.0182),
|
|
62
|
+
vec3<f32>(-0.5876, 1.1329, -0.1006),
|
|
63
|
+
vec3<f32>(-0.0728, -0.0083, 1.1187));
|
|
64
|
+
let logged = inset * (toAgx * color);
|
|
65
|
+
let x = clamp((log2(max(logged, vec3<f32>(1e-10))) + 12.47393) / 16.499999, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
66
|
+
let curved = 15.5 * pow(x, vec3<f32>(6.0)) - 40.14 * pow(x, vec3<f32>(5.0)) + 31.96 * pow(x, vec3<f32>(4.0))
|
|
67
|
+
- 6.868 * pow(x, vec3<f32>(3.0)) + 0.4298 * x * x + 0.1191 * x - 0.00232;
|
|
68
|
+
|
|
69
|
+
return fromAgx * pow(max(outset * curved, vec3<f32>(0.0)), vec3<f32>(2.2));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn toneMapNeutral(color: vec3<f32>) -> vec3<f32> {
|
|
73
|
+
let low = min(color.r, min(color.g, color.b));
|
|
74
|
+
let offset = select(0.04, low - 6.25 * low * low, low < 0.08);
|
|
75
|
+
let shifted = color - offset;
|
|
76
|
+
let peak = max(shifted.r, max(shifted.g, shifted.b));
|
|
77
|
+
|
|
78
|
+
if (peak < 0.76) {
|
|
79
|
+
return shifted;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let newPeak = 1.0 - 0.24 * 0.24 / (peak + 0.24 - 0.76);
|
|
83
|
+
let g = 1.0 - 1.0 / (0.15 * (peak - newPeak) + 1.0);
|
|
84
|
+
|
|
85
|
+
return shifted * (newPeak / peak) * (1.0 - g) + newPeak * g;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn toneMap(color: vec3<f32>) -> vec3<f32> {
|
|
89
|
+
let exposed = max(color * present.exposure, vec3<f32>(0.0));
|
|
90
|
+
var mapped = exposed;
|
|
91
|
+
|
|
92
|
+
if (present.toneMapping == 0u) {
|
|
93
|
+
mapped = toneMapAces(exposed);
|
|
94
|
+
} else if (present.toneMapping == 1u) {
|
|
95
|
+
mapped = toneMapAgx(exposed);
|
|
96
|
+
} else if (present.toneMapping == 2u) {
|
|
97
|
+
mapped = toneMapNeutral(exposed);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return clamp(mapped, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn linearToSrgb(color: vec3<f32>) -> vec3<f32> {
|
|
104
|
+
let low = color * 12.92;
|
|
105
|
+
let high = 1.055 * pow(max(color, vec3<f32>(0.0)), vec3<f32>(1.0 / 2.4)) - 0.055;
|
|
106
|
+
|
|
107
|
+
return select(high, low, color <= vec3<f32>(0.0031308));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn hash(value: u32) -> f32 {
|
|
111
|
+
var state = value * 747796405u + 2891336453u;
|
|
112
|
+
state = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
|
113
|
+
|
|
114
|
+
return f32((state >> 22u) ^ state) * 2.3283064365386963e-10;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@fragment
|
|
118
|
+
fn fragmentMain(@builtin(position) position: vec4<f32>) -> @location(0) vec4<f32> {
|
|
119
|
+
let pixel = vec2<u32>(position.xy);
|
|
120
|
+
let index = pixel.y * present.resolution.x + pixel.x;
|
|
121
|
+
let stored = accumulation[index];
|
|
122
|
+
|
|
123
|
+
// Nothing has been traced into this pixel yet: it stays see-through, so whatever is under the canvas shows.
|
|
124
|
+
if (stored.w <= 0.0) {
|
|
125
|
+
return vec4<f32>(0.0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
var color = select(stored.rgb / stored.w, vec3<f32>(0.0), stored.w <= 0.0);
|
|
129
|
+
|
|
130
|
+
if (present.denoised != 0u) {
|
|
131
|
+
color = denoised[index].rgb;
|
|
132
|
+
}
|
|
133
|
+
var alpha = 1.0;
|
|
134
|
+
|
|
135
|
+
if (present.transparent != 0u) {
|
|
136
|
+
let coverage = accumulation[present.resolution.x * present.resolution.y + index].x;
|
|
137
|
+
alpha = select(clamp(coverage / stored.w, 0.0, 1.0), 0.0, stored.w <= 0.0);
|
|
138
|
+
// The radiance is premultiplied by the coverage; the tone curve applies to the colour itself.
|
|
139
|
+
color = select(color / alpha, vec3<f32>(0.0), alpha <= 0.0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let centred = position.xy / vec2<f32>(present.resolution) - 0.5;
|
|
143
|
+
let falloff = 1.0 - present.vignette * dot(centred, centred) * 2.0;
|
|
144
|
+
var output = linearToSrgb(toneMap(color * max(falloff, 0.0)));
|
|
145
|
+
|
|
146
|
+
if (present.grain > 0.0) {
|
|
147
|
+
let noise = hash(index * 9781u + present.frameIndex * 6151u) - 0.5;
|
|
148
|
+
output = clamp(output + noise * present.grain * 0.1, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return vec4<f32>(output * alpha, alpha);
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sample generator: a Sobol sequence with hash based Owen scrambling (Burley, "Practical Hash-based Owen
|
|
3
|
+
* Scrambling", 2020). Every pixel draws its own scrambled sequence, decorrelated from its neighbours, and inside a pixel
|
|
4
|
+
* the samples are stratified in every pair of dimensions, so the estimate converges faster than with independent
|
|
5
|
+
* random numbers. Only the sample index, the pixel and a dimension counter feed the generator, so a pixel draws the same
|
|
6
|
+
* numbers whichever dispatch happens to cover it and the image does not depend on the tiling.
|
|
7
|
+
*/
|
|
8
|
+
export const RANDOM_WGSL = /* wgsl */ `
|
|
9
|
+
fn pcgHash(value: u32) -> u32 {
|
|
10
|
+
let state = value * 747796405u + 2891336453u;
|
|
11
|
+
let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
|
12
|
+
|
|
13
|
+
return (word >> 22u) ^ word;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
struct PathSampler {
|
|
17
|
+
seed: u32,
|
|
18
|
+
index: u32,
|
|
19
|
+
dimension: u32,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
fn makeSampler(pixel: vec2<u32>, index: u32) -> PathSampler {
|
|
23
|
+
var rng: PathSampler;
|
|
24
|
+
rng.seed = pcgHash(pixel.x * 1973u + pixel.y * 9277u + 1u);
|
|
25
|
+
rng.index = index;
|
|
26
|
+
rng.dimension = 0u;
|
|
27
|
+
|
|
28
|
+
return rng;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The Laine-Karras hash, which permutes the high bits in a way that nests: what Owen scrambling asks for. Every
|
|
33
|
+
* multiplier has to be even, or a bit stops depending on itself and the map is no longer a permutation.
|
|
34
|
+
*/
|
|
35
|
+
fn laineKarras(value: u32, seed: u32) -> u32 {
|
|
36
|
+
var x = value + seed;
|
|
37
|
+
x = x ^ (x * 0x6c50b47cu);
|
|
38
|
+
x = x ^ (x * 0xb82f1e52u);
|
|
39
|
+
x = x ^ (x * 0xc7afe784u);
|
|
40
|
+
x = x ^ (x * 0x8d22f6e6u);
|
|
41
|
+
|
|
42
|
+
return x;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn nestedScramble(value: u32, seed: u32) -> u32 {
|
|
46
|
+
return reverseBits(laineKarras(reverseBits(value), seed));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The first two Sobol dimensions, as fixed point fractions. */
|
|
50
|
+
fn sobol2(index: u32) -> vec2<u32> {
|
|
51
|
+
var second = 0u;
|
|
52
|
+
var direction = 0x80000000u;
|
|
53
|
+
var bits = index;
|
|
54
|
+
|
|
55
|
+
loop {
|
|
56
|
+
if (bits == 0u) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if ((bits & 1u) != 0u) {
|
|
61
|
+
second = second ^ direction;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
bits = bits >> 1u;
|
|
65
|
+
direction = direction ^ (direction >> 1u);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return vec2<u32>(reverseBits(index), second);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn toUnit(value: u32) -> f32 {
|
|
72
|
+
return min(f32(value) * 2.3283064365386963e-10, 0.99999994);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The next pair of dimensions, in [0, 1). The sample index is shuffled per pair as well, which pads the two dimensional
|
|
77
|
+
* sequence out to as many dimensions as a path needs without correlating them.
|
|
78
|
+
*/
|
|
79
|
+
fn sample2D(rng: ptr<function, PathSampler>) -> vec2<f32> {
|
|
80
|
+
let pair = (*rng).seed + (*rng).dimension * 2654435761u;
|
|
81
|
+
(*rng).dimension = (*rng).dimension + 1u;
|
|
82
|
+
|
|
83
|
+
let shuffled = nestedScramble((*rng).index, pcgHash(pair));
|
|
84
|
+
let point = sobol2(shuffled);
|
|
85
|
+
|
|
86
|
+
return vec2<f32>(
|
|
87
|
+
toUnit(nestedScramble(point.x, pcgHash(pair + 0x9e3779b9u))),
|
|
88
|
+
toUnit(nestedScramble(point.y, pcgHash(pair + 0x3c6ef372u))));
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The geometric primitives the lobes and the light sampling are built from. */
|
|
2
|
+
export const SAMPLING_WGSL = /* wgsl */ `
|
|
3
|
+
/** Branchless orthonormal basis around a unit normal (Duff and others, 2017). */
|
|
4
|
+
fn orthonormalBasis(n: vec3<f32>) -> mat3x3<f32> {
|
|
5
|
+
let s = select(-1.0, 1.0, n.z >= 0.0);
|
|
6
|
+
let a = -1.0 / (s + n.z);
|
|
7
|
+
let b = n.x * n.y * a;
|
|
8
|
+
|
|
9
|
+
return mat3x3<f32>(
|
|
10
|
+
vec3<f32>(1.0 + s * n.x * n.x * a, s * b, -s * n.x),
|
|
11
|
+
vec3<f32>(b, s + n.y * n.y * a, -n.y),
|
|
12
|
+
n);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Cosine weighted direction in the hemisphere around the normal; the weight cancels the Lambert cosine. */
|
|
16
|
+
fn cosineDirection(normal: vec3<f32>, u: vec2<f32>) -> vec3<f32> {
|
|
17
|
+
let radius = sqrt(u.x);
|
|
18
|
+
let angle = 2.0 * PI * u.y;
|
|
19
|
+
let local = vec3<f32>(radius * cos(angle), radius * sin(angle), sqrt(max(0.0, 1.0 - u.x)));
|
|
20
|
+
|
|
21
|
+
return normalize(orthonormalBasis(normal) * local);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The unit normal of the triangle's plane; which side it faces does not matter, every emitter is two sided. */
|
|
25
|
+
fn triangleNormal(triangle: u32) -> vec3<f32> {
|
|
26
|
+
let a = triangleVertex(triangle, 0u);
|
|
27
|
+
|
|
28
|
+
return normalize(cross(triangleVertex(triangle, 1u) - a, triangleVertex(triangle, 2u) - a));
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a translated shader reads from the kernel: its inputs at a hit, its texture calls and its varyings. The
|
|
3
|
+
* programs themselves are generated per scene by shaderProgram.ts and come right after this.
|
|
4
|
+
*/
|
|
5
|
+
export const SHADER_SUPPORT_WGSL = /* wgsl */ `
|
|
6
|
+
/** Everything a fragment shader can ask about the point it runs at. */
|
|
7
|
+
struct ShaderInput {
|
|
8
|
+
// Where the material's uniform values start in the texture buffer.
|
|
9
|
+
data: u32,
|
|
10
|
+
material: u32,
|
|
11
|
+
triangle: u32,
|
|
12
|
+
frontFacing: bool,
|
|
13
|
+
bary: vec2<f32>,
|
|
14
|
+
// The barycentrics the pixel to the right and the pixel above would have found on the same plane.
|
|
15
|
+
baryDx: vec2<f32>,
|
|
16
|
+
baryDy: vec2<f32>,
|
|
17
|
+
// GL window coordinates: x right, y up from the bottom, z the depth, w the reciprocal of clip w.
|
|
18
|
+
fragCoord: vec4<f32>,
|
|
19
|
+
pointCoord: vec2<f32>,
|
|
20
|
+
// A sky's view direction.
|
|
21
|
+
direction: vec3<f32>,
|
|
22
|
+
// A built-in material's state before lighting, for its hooks, in Three's view space.
|
|
23
|
+
diffuseColor: vec4<f32>,
|
|
24
|
+
normal: vec3<f32>,
|
|
25
|
+
roughness: f32,
|
|
26
|
+
metalness: f32,
|
|
27
|
+
emissive: vec3<f32>,
|
|
28
|
+
viewPosition: vec3<f32>,
|
|
29
|
+
geometryNormal: vec3<f32>,
|
|
30
|
+
uv: vec2<f32>,
|
|
31
|
+
// What gl_FragColor holds when a hook after <opaque_fragment> starts.
|
|
32
|
+
fragColor: vec4<f32>,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
struct ShaderOutput {
|
|
36
|
+
color: vec4<f32>,
|
|
37
|
+
discarded: bool,
|
|
38
|
+
diffuseColor: vec4<f32>,
|
|
39
|
+
normal: vec3<f32>,
|
|
40
|
+
roughness: f32,
|
|
41
|
+
metalness: f32,
|
|
42
|
+
emissive: vec3<f32>,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** The output of a shader that changed nothing, which is what a hook that does not touch a value leaves. */
|
|
46
|
+
fn noShaderOutput(input: ShaderInput) -> ShaderOutput {
|
|
47
|
+
var output: ShaderOutput;
|
|
48
|
+
output.color = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
49
|
+
output.discarded = false;
|
|
50
|
+
output.diffuseColor = input.diffuseColor;
|
|
51
|
+
output.normal = input.normal;
|
|
52
|
+
output.roughness = input.roughness;
|
|
53
|
+
output.metalness = input.metalness;
|
|
54
|
+
output.emissive = input.emissive;
|
|
55
|
+
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** texture2D: an unbound sampler reads black, as WebGL's does. */
|
|
60
|
+
fn shaderSample2D(index: u32, uv: vec2<f32>) -> vec4<f32> {
|
|
61
|
+
if (index == NO_TEXTURE) {
|
|
62
|
+
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return sampleTextureRaw(index, uv);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** textureCube: the snapshot hands a cube map over unwrapped, mirrored the way Three samples a cube texture. */
|
|
69
|
+
fn shaderSampleCube(index: u32, direction: vec3<f32>) -> vec4<f32> {
|
|
70
|
+
if (index == NO_TEXTURE) {
|
|
71
|
+
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return sampleEquirect(index, direction);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn shaderTexelFetch(index: u32, texel: vec2<i32>) -> vec4<f32> {
|
|
78
|
+
if (index == NO_TEXTURE) {
|
|
79
|
+
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let info = textureInfo(index);
|
|
83
|
+
let size = vec2<i32>(info.size);
|
|
84
|
+
let at = clamp(texel, vec2<i32>(0), max(size - vec2<i32>(1), vec2<i32>(0)));
|
|
85
|
+
// texelFetch reads rows as GL stores them; a texture flipped on upload is flipped back here.
|
|
86
|
+
let row = select(at.y, size.y - 1 - at.y, (info.flags & 8u) != 0u);
|
|
87
|
+
|
|
88
|
+
return readTexel(info, at.x, row);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fn shaderTextureSize(index: u32) -> vec2<i32> {
|
|
92
|
+
if (index == NO_TEXTURE) {
|
|
93
|
+
return vec2<i32>(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return vec2<i32>(textureInfo(index).size);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Where a triangle's varyings start (x) and how many floats one corner has (y). */
|
|
100
|
+
fn shaderVaryingAddress(material: u32, triangle: u32) -> vec2<u32> {
|
|
101
|
+
let stride = materials[material].varyingStride;
|
|
102
|
+
let local = triangle - materials[material].firstTriangle;
|
|
103
|
+
|
|
104
|
+
return vec2<u32>(frame.varyingOffset + materials[material].varyingBase + local * 3u * stride, stride);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** One component of a varying at a point: interpolated, or the last vertex's for a flat one, as GL takes it. */
|
|
108
|
+
fn shaderVarying(at: vec2<u32>, component: u32, bary: vec2<f32>, flat: bool) -> f32 {
|
|
109
|
+
let v0 = attributes[at.x + component];
|
|
110
|
+
let v1 = attributes[at.x + at.y + component];
|
|
111
|
+
let v2 = attributes[at.x + 2u * at.y + component];
|
|
112
|
+
|
|
113
|
+
if (flat) {
|
|
114
|
+
return v2;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return v0 * (1.0 - bary.x - bary.y) + v1 * bary.x + v2 * bary.y;
|
|
118
|
+
}
|
|
119
|
+
`;
|