pelican-engine 0.0.0-stage → 0.3.1
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/AGENTS.md +147 -0
- package/LICENSE +22 -0
- package/README.md +129 -2
- package/distribution.json +197 -0
- package/docs/AI-WORKFLOW.md +517 -0
- package/docs/ARCHITECTURE.md +268 -0
- package/docs/CAPABILITIES.md +264 -0
- package/docs/DECISIONS.md +107 -0
- package/docs/M3-USAGE.md +192 -0
- package/docs/PELICAN-MIGRATION.md +38 -0
- package/docs/RENDER-BACKENDS.md +50 -0
- package/docs/ROADMAP.md +239 -0
- package/docs/THIRD-PARTY-NOTICES.md +151 -0
- package/docs/api/REFERENCE.md +1435 -0
- package/docs/api/index.json +1139 -0
- package/docs/npm/DISTRIBUTION.md +130 -0
- package/docs/npm/GAME-AUTHOR.md +147 -0
- package/engine.manifest.json +15 -0
- package/lib/assets/audio.js +67 -0
- package/lib/assets/errors.js +36 -0
- package/lib/assets/glb.js +277 -0
- package/lib/assets/index.js +53 -0
- package/lib/assets/jpeg.js +199 -0
- package/lib/assets/model.js +558 -0
- package/lib/assets/optimize.js +95 -0
- package/lib/assets/texture.js +187 -0
- package/lib/compiler/distribution.js +17 -0
- package/lib/compiler/extract.js +425 -0
- package/lib/compiler/graph.js +366 -0
- package/lib/compiler/index.js +574 -0
- package/lib/compiler/normalize.js +1194 -0
- package/lib/compiler/source-map.js +25 -0
- package/lib/compiler/types.js +2 -0
- package/lib/compiler/version.js +93 -0
- package/lib/experiments/simulation/index.js +2 -0
- package/lib/experiments/simulation/math.js +41 -0
- package/lib/experiments/simulation/probe.js +215 -0
- package/lib/experiments/simulation/scenes.js +192 -0
- package/lib/experiments/simulation/solver.js +346 -0
- package/lib/experiments/simulation/types.js +1 -0
- package/lib/experiments/world/adapters.js +30 -0
- package/lib/experiments/world/geometry.js +180 -0
- package/lib/experiments/world/index.js +2 -0
- package/lib/experiments/world/probe.js +288 -0
- package/lib/experiments/world/types.js +1 -0
- package/lib/experiments/world/world.js +599 -0
- package/lib/navigation/index.js +157 -0
- package/lib/physics/index.js +909 -0
- package/lib/protocol/index.js +92 -0
- package/lib/renderer/acceleration.js +609 -0
- package/lib/renderer/browser.js +2015 -0
- package/lib/renderer/bsdf.js +260 -0
- package/lib/renderer/checkpoint.js +88 -0
- package/lib/renderer/compute-executor.js +1034 -0
- package/lib/renderer/compute-texture-assets.js +49 -0
- package/lib/renderer/compute-view-contract.js +1 -0
- package/lib/renderer/denoiser.js +361 -0
- package/lib/renderer/dielectric.js +360 -0
- package/lib/renderer/embedded-image-pixels.js +368 -0
- package/lib/renderer/emissive-lights.js +67 -0
- package/lib/renderer/environment-importance.js +55 -0
- package/lib/renderer/environment.js +346 -0
- package/lib/renderer/gameplay.js +464 -0
- package/lib/renderer/geometry.js +103 -0
- package/lib/renderer/graph-expressions.js +485 -0
- package/lib/renderer/graph-tsl.js +141 -0
- package/lib/renderer/graph.js +214 -0
- package/lib/renderer/managed-texture-sampling.js +29 -0
- package/lib/renderer/material-graphs.js +297 -0
- package/lib/renderer/medium.js +348 -0
- package/lib/renderer/models.js +1053 -0
- package/lib/renderer/motion-shader.js +40 -0
- package/lib/renderer/path-shader.js +196 -0
- package/lib/renderer/pathtrace.js +324 -0
- package/lib/renderer/postprocess-contract.js +1 -0
- package/lib/renderer/postprocess.js +494 -0
- package/lib/renderer/render-buffers.js +132 -0
- package/lib/renderer/render-textures.js +155 -0
- package/lib/renderer/startup.js +50 -0
- package/lib/renderer/surface-capture.js +199 -0
- package/lib/renderer/surface-gpu.js +270 -0
- package/lib/renderer/surface-motion.js +84 -0
- package/lib/renderer/surface-ray-shader.js +91 -0
- package/lib/renderer/surface-shader.js +88 -0
- package/lib/renderer/surface-tracer.js +1720 -0
- package/lib/renderer/surface.js +1425 -0
- package/lib/renderer/temporal-filter.js +512 -0
- package/lib/renderer/texture-pixels.js +207 -0
- package/lib/renderer/traversal-nodes.js +54 -0
- package/lib/renderer/ui.js +446 -0
- package/lib/renderer/vertex-bounds.js +127 -0
- package/lib/renderer/vertex-contract.js +1 -0
- package/lib/renderer/vertex-graphs.js +197 -0
- package/lib/renderer/volume-shader.js +50 -0
- package/lib/runtime/compute-contract.js +1 -0
- package/lib/runtime/compute-service.js +674 -0
- package/lib/runtime/contracts.js +1 -0
- package/lib/runtime/error.js +37 -0
- package/lib/runtime/events.js +189 -0
- package/lib/runtime/game-services.js +772 -0
- package/lib/runtime/host.js +839 -0
- package/lib/runtime/material-parameters.js +145 -0
- package/lib/runtime/plugins.js +1 -0
- package/lib/runtime/postprocess-contract.js +1 -0
- package/lib/runtime/postprocess.js +189 -0
- package/lib/runtime/prefabs.js +13 -0
- package/lib/runtime/random.js +88 -0
- package/lib/runtime/render-buffer-contract.js +1 -0
- package/lib/runtime/render-buffers.js +243 -0
- package/lib/runtime/render-capabilities.js +17 -0
- package/lib/runtime/render-settings.js +249 -0
- package/lib/runtime/render-texture-contract.js +1 -0
- package/lib/runtime/render-textures.js +271 -0
- package/lib/runtime/resources.js +648 -0
- package/lib/runtime/scheduler.js +765 -0
- package/lib/runtime/storage.js +169 -0
- package/lib/runtime/world.js +594 -0
- package/lib/schema/index.js +24 -0
- package/lib/sdk/graph-values.js +16 -0
- package/lib/sdk/index.d.ts +800 -0
- package/lib/sdk/index.js +82 -0
- package/lib/tools/api-index.js +113 -0
- package/lib/tools/browser-session.js +89 -0
- package/lib/tools/cli.js +513 -0
- package/lib/tools/compress-dist.js +59 -0
- package/lib/tools/control-client.js +153 -0
- package/lib/tools/control-server.js +633 -0
- package/lib/tools/diagnostic-code.js +3 -0
- package/lib/tools/doctor.js +496 -0
- package/lib/tools/npm/browser-cli.js +6 -0
- package/lib/tools/npm/entry.js +40 -0
- package/lib/tools/npm/layout.js +195 -0
- package/lib/tools/setup.js +153 -0
- package/licenses/@dimforge/rapier2d-compat/LICENSE +201 -0
- package/licenses/@dimforge/rapier3d-compat/LICENSE +201 -0
- package/licenses/@gltf-transform/core/LICENSE.md +21 -0
- package/licenses/@gltf-transform/extensions/LICENSE.md +21 -0
- package/licenses/@gltf-transform/functions/LICENSE.md +21 -0
- package/licenses/@img/colour/LICENSE.md +82 -0
- package/licenses/@img/sharp-win32-x64/LICENSE +191 -0
- package/licenses/@oxc-project/types/LICENSE +22 -0
- package/licenses/@rolldown/pluginutils/LICENSE +21 -0
- package/licenses/@tweenjs/tween.js/LICENSE +23 -0
- package/licenses/@types/ndarray/LICENSE +21 -0
- package/licenses/@types/node/LICENSE +21 -0
- package/licenses/@types/stats.js/LICENSE +21 -0
- package/licenses/@types/three/@dimforge/rapier3d-compat/LICENSE +201 -0
- package/licenses/@types/three/LICENSE +21 -0
- package/licenses/@types/webxr/LICENSE +21 -0
- package/licenses/@webgpu/types/LICENSE +26 -0
- package/licenses/THIRD-PARTY-NOTICES.md +139 -0
- package/licenses/cwise-compiler/LICENSE +22 -0
- package/licenses/detect-libc/LICENSE +201 -0
- package/licenses/esbuild/LICENSE.md +21 -0
- package/licenses/fast-png/LICENSE +21 -0
- package/licenses/fdir/LICENSE +7 -0
- package/licenses/fflate/LICENSE +21 -0
- package/licenses/iobuffer/LICENSE +22 -0
- package/licenses/iota-array/LICENSE +22 -0
- package/licenses/is-buffer/LICENSE +21 -0
- package/licenses/ktx-parse/LICENSE +21 -0
- package/licenses/lightningcss/LICENSE +373 -0
- package/licenses/lightningcss-win32-x64-msvc/LICENSE +373 -0
- package/licenses/meshoptimizer/LICENSE.md +21 -0
- package/licenses/nanoid/LICENSE +20 -0
- package/licenses/ndarray/LICENSE +22 -0
- package/licenses/ndarray-lanczos/LICENSE +21 -0
- package/licenses/ndarray-ops/LICENSE +22 -0
- package/licenses/ndarray-pixels/LICENSE +21 -0
- package/licenses/ngraph.path/LICENSE +21 -0
- package/licenses/picocolors/LICENSE +15 -0
- package/licenses/picomatch/LICENSE +21 -0
- package/licenses/playwright/LICENSE +202 -0
- package/licenses/playwright/NOTICE +5 -0
- package/licenses/playwright-core/LICENSE +202 -0
- package/licenses/playwright-core/NOTICE +5 -0
- package/licenses/postcss/LICENSE +20 -0
- package/licenses/prettier/LICENSE +7 -0
- package/licenses/property-graph/LICENSE +21 -0
- package/licenses/rolldown/LICENSE +25 -0
- package/licenses/semver/LICENSE +15 -0
- package/licenses/sharp/LICENSE +191 -0
- package/licenses/source-map-js/LICENSE +28 -0
- package/licenses/three/LICENSE +21 -0
- package/licenses/tinyglobby/LICENSE +21 -0
- package/licenses/tsx/LICENSE +21 -0
- package/licenses/typescript/LICENSE.txt +55 -0
- package/licenses/undici-types/LICENSE +21 -0
- package/licenses/uniq/LICENSE +22 -0
- package/licenses/vite/LICENSE.md +2084 -0
- package/package.json +49 -4
- package/web/index.html +34 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
export const DIELECTRIC_MIN_ALPHA = 0.0025;
|
|
2
|
+
const dielectricWhite = [1, 1, 1];
|
|
3
|
+
const dielectricZero = [0, 0, 0];
|
|
4
|
+
function dielectricDot(a, b) {
|
|
5
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
6
|
+
}
|
|
7
|
+
function dielectricScale(a, s) {
|
|
8
|
+
return [a[0] * s, a[1] * s, a[2] * s];
|
|
9
|
+
}
|
|
10
|
+
function dielectricAdd(a, b) {
|
|
11
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
12
|
+
}
|
|
13
|
+
function dielectricCross(a, b) {
|
|
14
|
+
return [
|
|
15
|
+
a[1] * b[2] - a[2] * b[1],
|
|
16
|
+
a[2] * b[0] - a[0] * b[2],
|
|
17
|
+
a[0] * b[1] - a[1] * b[0],
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
function dielectricUnit(a) {
|
|
21
|
+
return dielectricScale(a, 1 / Math.hypot(...a));
|
|
22
|
+
}
|
|
23
|
+
function dielectricVector(a, name) {
|
|
24
|
+
if (a.length !== 3 || !a.every(Number.isFinite) || Math.hypot(...a) === 0)
|
|
25
|
+
throw new RangeError(`DIELECTRIC_INPUT: ${name} must be a finite nonzero vec3`);
|
|
26
|
+
}
|
|
27
|
+
function dielectricColor(a) {
|
|
28
|
+
if (a.length !== 3 || !a.every((x) => Number.isFinite(x) && x >= 0 && x <= 1))
|
|
29
|
+
throw new RangeError("DIELECTRIC_INPUT: linear color must be in [0,1]");
|
|
30
|
+
}
|
|
31
|
+
function dielectricIor(x) {
|
|
32
|
+
if (!Number.isFinite(x) || x <= 0)
|
|
33
|
+
throw new RangeError("DIELECTRIC_INPUT: IOR must be finite and positive");
|
|
34
|
+
}
|
|
35
|
+
function dielectricValidate(n, v, m) {
|
|
36
|
+
dielectricVector(n, "normal");
|
|
37
|
+
dielectricVector(v, "view");
|
|
38
|
+
dielectricIor(m.etaOutside);
|
|
39
|
+
dielectricIor(m.etaInside);
|
|
40
|
+
const ratio = m.etaInside / m.etaOutside;
|
|
41
|
+
if (!Number.isFinite(ratio * ratio) || ratio * ratio === 0)
|
|
42
|
+
throw new RangeError("DIELECTRIC_INPUT: IOR ratio squared must be representable");
|
|
43
|
+
if (!Number.isFinite(m.roughness) || m.roughness < 0 || m.roughness > 1)
|
|
44
|
+
throw new RangeError("DIELECTRIC_INPUT: roughness must be in [0,1]");
|
|
45
|
+
dielectricColor(m.tint ?? dielectricWhite);
|
|
46
|
+
if (m.transport !== undefined &&
|
|
47
|
+
m.transport !== "radiance" &&
|
|
48
|
+
m.transport !== "importance")
|
|
49
|
+
throw new RangeError("DIELECTRIC_INPUT: unsupported transport mode");
|
|
50
|
+
}
|
|
51
|
+
/** Exact unpolarized Fresnel, cosIncident in [0,1], eta=etaT/etaI. */
|
|
52
|
+
export function dielectricFresnelCPU(cosIncident, eta) {
|
|
53
|
+
dielectricIor(eta);
|
|
54
|
+
if (!Number.isFinite(cosIncident) || cosIncident < 0 || cosIncident > 1)
|
|
55
|
+
throw new RangeError("DIELECTRIC_INPUT: cosIncident must be in [0,1]");
|
|
56
|
+
return dielectricFresnel(cosIncident, eta);
|
|
57
|
+
}
|
|
58
|
+
function dielectricFresnel(c, e) {
|
|
59
|
+
if (e === 1)
|
|
60
|
+
return 0;
|
|
61
|
+
const s2 = Math.max(0, 1 - c * c) / (e * e);
|
|
62
|
+
if (s2 >= 1)
|
|
63
|
+
return 1;
|
|
64
|
+
const t = Math.sqrt(1 - s2);
|
|
65
|
+
const rs = (c - e * t) / (c + e * t), rp = (e * c - t) / (e * c + t);
|
|
66
|
+
return (rs * rs + rp * rp) * 0.5;
|
|
67
|
+
}
|
|
68
|
+
function dielectricOrient(n, v, m) {
|
|
69
|
+
const outside = dielectricDot(n, v) >= 0;
|
|
70
|
+
return {
|
|
71
|
+
n: outside ? n : dielectricScale(n, -1),
|
|
72
|
+
eta: outside ? m.etaInside / m.etaOutside : m.etaOutside / m.etaInside,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function dielectricD(n, h, a) {
|
|
76
|
+
const nh = dielectricDot(n, h), s = dielectricCross(n, h);
|
|
77
|
+
const d = dielectricDot(s, s) + a * a * nh * nh;
|
|
78
|
+
return nh > 0 ? (a * a) / (Math.PI * d * d) : 0;
|
|
79
|
+
}
|
|
80
|
+
function dielectricLambda(c, a) {
|
|
81
|
+
return (Math.sqrt(1 + (a * a * Math.max(0, 1 - c * c)) / (c * c)) - 1) * 0.5;
|
|
82
|
+
}
|
|
83
|
+
function dielectricTerms(n, v, l, m) {
|
|
84
|
+
const o = dielectricOrient(n, v, m);
|
|
85
|
+
n = o.n;
|
|
86
|
+
const nv = dielectricDot(n, v), nl = dielectricDot(n, l), e = o.eta;
|
|
87
|
+
if (nv === 0 || nl === 0 || m.roughness === 0 || e === 1)
|
|
88
|
+
return undefined;
|
|
89
|
+
const reflection = nl > 0;
|
|
90
|
+
let sum = dielectricAdd(v, dielectricScale(l, reflection ? 1 : e));
|
|
91
|
+
if (Math.hypot(...sum) === 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
// Unit-vector identity: (v x l) x (v-l) = (1-v.l)*(v+l).
|
|
94
|
+
// Avoid amplifying unequal f32 normalization lengths in a grazing bisector.
|
|
95
|
+
if (reflection && dielectricDot(v, l) < 0)
|
|
96
|
+
sum = dielectricCross(dielectricCross(v, l), dielectricAdd(v, dielectricScale(l, -1)));
|
|
97
|
+
let h = dielectricUnit(sum);
|
|
98
|
+
if (dielectricDot(n, h) < 0)
|
|
99
|
+
h = dielectricScale(h, -1);
|
|
100
|
+
return dielectricTermsWithHalfVector(n, v, l, h, e, m);
|
|
101
|
+
}
|
|
102
|
+
// n faces v. Sampling retains its VNDF h instead of inverting a rounded direction.
|
|
103
|
+
function dielectricTermsWithHalfVector(n, v, l, h, e, m) {
|
|
104
|
+
const nv = dielectricDot(n, v), nl = dielectricDot(n, l);
|
|
105
|
+
const reflection = nl > 0;
|
|
106
|
+
if (nv <= 0 || nl === 0)
|
|
107
|
+
return undefined;
|
|
108
|
+
const vh = dielectricDot(v, h), lh = dielectricDot(l, h);
|
|
109
|
+
if (vh <= 0 || lh * nl <= 0)
|
|
110
|
+
return undefined;
|
|
111
|
+
const a = Math.max(DIELECTRIC_MIN_ALPHA, m.roughness * m.roughness);
|
|
112
|
+
const d = dielectricD(n, h, a), lv = dielectricLambda(nv, a), ll = dielectricLambda(Math.abs(nl), a);
|
|
113
|
+
const g = 1 / (1 + lv + ll), pm = (d * vh) / (nv * (1 + lv)), f = dielectricFresnel(Math.min(1, vh), e);
|
|
114
|
+
if (reflection)
|
|
115
|
+
return { f: (d * g * f) / (4 * nv * nl), pdf: (pm * f) / (4 * vh) };
|
|
116
|
+
const den = vh + e * lh;
|
|
117
|
+
if (den === 0)
|
|
118
|
+
return undefined;
|
|
119
|
+
// Walter eq.17: |dh/dl| = eta^2*|LoH|/(VoH+eta*LoH)^2.
|
|
120
|
+
const jac = (e * e * Math.abs(lh)) / (den * den);
|
|
121
|
+
const transport = m.transport === "importance" ? 1 : 1 / (e * e);
|
|
122
|
+
return {
|
|
123
|
+
f: (((1 - f) * d * g * vh * jac) / (nv * Math.abs(nl))) * transport,
|
|
124
|
+
pdf: pm * (1 - f) * jac,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** BSDF/BTDF f in sr^-1, no cosine. Delta distributions evaluate to zero. */
|
|
128
|
+
export function evaluateDielectric(normal, view, light, material) {
|
|
129
|
+
dielectricValidate(normal, view, material);
|
|
130
|
+
dielectricVector(light, "light");
|
|
131
|
+
const n = dielectricUnit(normal), v = dielectricUnit(view), l = dielectricUnit(light);
|
|
132
|
+
const t = dielectricTerms(n, v, l, material);
|
|
133
|
+
if (!t)
|
|
134
|
+
return dielectricZero;
|
|
135
|
+
const tint = dielectricDot(n, v) * dielectricDot(n, l) < 0
|
|
136
|
+
? (material.tint ?? dielectricWhite)
|
|
137
|
+
: dielectricWhite;
|
|
138
|
+
return dielectricScale(tint, t.f);
|
|
139
|
+
}
|
|
140
|
+
/** Full Fresnel-weighted solid-angle PDF. Includes null-event probability loss. */
|
|
141
|
+
export function dielectricPdfCPU(normal, view, light, material) {
|
|
142
|
+
dielectricValidate(normal, view, material);
|
|
143
|
+
dielectricVector(light, "light");
|
|
144
|
+
return (dielectricTerms(dielectricUnit(normal), dielectricUnit(view), dielectricUnit(light), material)?.pdf ?? 0);
|
|
145
|
+
}
|
|
146
|
+
function dielectricNull() {
|
|
147
|
+
return {
|
|
148
|
+
direction: dielectricZero,
|
|
149
|
+
weight: dielectricZero,
|
|
150
|
+
pdf: 0,
|
|
151
|
+
eta: 1,
|
|
152
|
+
event: "null",
|
|
153
|
+
delta: false,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function dielectricVndf(n, v, a, u, w) {
|
|
157
|
+
const t = dielectricUnit(dielectricCross(Math.abs(n[2]) < 0.999 ? [0, 0, 1] : [1, 0, 0], n));
|
|
158
|
+
const b = dielectricCross(n, t);
|
|
159
|
+
const vh = dielectricUnit([
|
|
160
|
+
a * dielectricDot(v, t),
|
|
161
|
+
a * dielectricDot(v, b),
|
|
162
|
+
dielectricDot(v, n),
|
|
163
|
+
]);
|
|
164
|
+
const len = vh[0] * vh[0] + vh[1] * vh[1];
|
|
165
|
+
const t1 = len > 0 ? [-vh[1] / Math.sqrt(len), vh[0] / Math.sqrt(len), 0] : [1, 0, 0];
|
|
166
|
+
const t2 = dielectricCross(vh, t1);
|
|
167
|
+
const r = Math.sqrt(u), phi = 2 * Math.PI * w, x = r * Math.cos(phi), s = (1 + vh[2]) * 0.5;
|
|
168
|
+
const y = (1 - s) * Math.sqrt(Math.max(0, 1 - x * x)) + s * r * Math.sin(phi);
|
|
169
|
+
const z = Math.sqrt(Math.max(0, 1 - x * x - y * y));
|
|
170
|
+
const nh = dielectricAdd(dielectricAdd(dielectricScale(t1, x), dielectricScale(t2, y)), dielectricScale(vh, z));
|
|
171
|
+
const h = dielectricUnit([a * nh[0], a * nh[1], Math.max(0, nh[2])]);
|
|
172
|
+
return dielectricUnit(dielectricAdd(dielectricAdd(dielectricScale(t, h[0]), dielectricScale(b, h[1])), dielectricScale(n, h[2])));
|
|
173
|
+
}
|
|
174
|
+
/** random=[Fresnel branch, VNDF disk radius squared, disk azimuth], each in [0,1). */
|
|
175
|
+
export function sampleDielectric(normal, view, material, random) {
|
|
176
|
+
dielectricValidate(normal, view, material);
|
|
177
|
+
if (random.length !== 3 ||
|
|
178
|
+
!random.every((x) => Number.isFinite(x) && x >= 0 && x < 1))
|
|
179
|
+
throw new RangeError("DIELECTRIC_INPUT: random must be in [0,1)^3");
|
|
180
|
+
const v = dielectricUnit(view), o = dielectricOrient(dielectricUnit(normal), v, material), n = o.n, e = o.eta;
|
|
181
|
+
const nv = dielectricDot(n, v);
|
|
182
|
+
if (nv === 0)
|
|
183
|
+
return dielectricNull();
|
|
184
|
+
const delta = material.roughness === 0 || e === 1;
|
|
185
|
+
const h = delta
|
|
186
|
+
? n
|
|
187
|
+
: dielectricVndf(n, v, Math.max(DIELECTRIC_MIN_ALPHA, material.roughness ** 2), random[1], random[2]);
|
|
188
|
+
const vh = Math.min(1, Math.max(0, dielectricDot(v, h))), f = dielectricFresnel(vh, e);
|
|
189
|
+
const reflection = random[0] < f;
|
|
190
|
+
let l;
|
|
191
|
+
if (reflection)
|
|
192
|
+
l = dielectricAdd(dielectricScale(v, -1), dielectricScale(h, 2 * vh));
|
|
193
|
+
else {
|
|
194
|
+
const sin2 = Math.max(0, 1 - vh * vh) / (e * e);
|
|
195
|
+
if (sin2 >= 1)
|
|
196
|
+
return dielectricNull();
|
|
197
|
+
l = dielectricAdd(dielectricScale(v, -1 / e), dielectricScale(h, vh / e - Math.sqrt(1 - sin2)));
|
|
198
|
+
}
|
|
199
|
+
l = dielectricUnit(l);
|
|
200
|
+
const nl = dielectricDot(n, l);
|
|
201
|
+
if (nl === 0 || (reflection ? nl < 0 : nl > 0))
|
|
202
|
+
return dielectricNull();
|
|
203
|
+
const color = reflection
|
|
204
|
+
? dielectricWhite
|
|
205
|
+
: (material.tint ?? dielectricWhite);
|
|
206
|
+
const transport = reflection || material.transport === "importance" ? 1 : 1 / (e * e);
|
|
207
|
+
if (delta)
|
|
208
|
+
return {
|
|
209
|
+
direction: l,
|
|
210
|
+
weight: dielectricScale(color, transport),
|
|
211
|
+
pdf: reflection ? f : 1 - f,
|
|
212
|
+
eta: reflection ? 1 : e,
|
|
213
|
+
event: reflection ? "reflection" : "transmission",
|
|
214
|
+
delta: true,
|
|
215
|
+
};
|
|
216
|
+
const terms = dielectricTermsWithHalfVector(n, v, l, h, e, material);
|
|
217
|
+
if (!terms || terms.pdf <= 0)
|
|
218
|
+
return dielectricNull();
|
|
219
|
+
return {
|
|
220
|
+
direction: l,
|
|
221
|
+
weight: dielectricScale(color, (terms.f * Math.abs(nl)) / terms.pdf),
|
|
222
|
+
pdf: terms.pdf,
|
|
223
|
+
eta: reflection ? 1 : e,
|
|
224
|
+
event: reflection ? "reflection" : "transmission",
|
|
225
|
+
delta: false,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** Separate homogeneous Beer attenuation: color is transmittance at attenuationDistance. */
|
|
229
|
+
export function dielectricBeerAttenuation(color, attenuationDistance, length) {
|
|
230
|
+
dielectricColor(color);
|
|
231
|
+
if (!Number.isFinite(attenuationDistance) ||
|
|
232
|
+
attenuationDistance <= 0 ||
|
|
233
|
+
!Number.isFinite(length) ||
|
|
234
|
+
length < 0)
|
|
235
|
+
throw new RangeError("DIELECTRIC_INPUT: attenuationDistance must be positive and length nonnegative, both finite");
|
|
236
|
+
return length === 0
|
|
237
|
+
? dielectricWhite
|
|
238
|
+
: color.map((c) => Math.pow(c, length / attenuationDistance));
|
|
239
|
+
}
|
|
240
|
+
/** No bindings, entry points, dependencies, RNG, or integration into the main renderer. */
|
|
241
|
+
export const DIELECTRIC_SHADER = /* wgsl */ `
|
|
242
|
+
struct DielectricMaterial {
|
|
243
|
+
etaOutside: f32,
|
|
244
|
+
etaInside: f32,
|
|
245
|
+
roughness: f32,
|
|
246
|
+
transport: u32, // 0=radiance, 1=importance
|
|
247
|
+
tint: vec3f,
|
|
248
|
+
}
|
|
249
|
+
struct DielectricSample {
|
|
250
|
+
direction: vec3f,
|
|
251
|
+
weight: vec3f,
|
|
252
|
+
pdf: f32,
|
|
253
|
+
eta: f32,
|
|
254
|
+
event: u32, // 0=null, 1=reflection, 2=transmission
|
|
255
|
+
delta: u32,
|
|
256
|
+
}
|
|
257
|
+
struct DielectricTerms { f: f32, pdf: f32 }
|
|
258
|
+
fn dielectricFresnel(c: f32, e: f32) -> f32 {
|
|
259
|
+
if(e==1.0){return 0.0;}
|
|
260
|
+
let s2=max(0.0,1.0-c*c)/(e*e);
|
|
261
|
+
if(s2>=1.0){return 1.0;}
|
|
262
|
+
let t=sqrt(1.0-s2);
|
|
263
|
+
let rs=(c-e*t)/(c+e*t); let rp=(e*c-t)/(e*c+t);
|
|
264
|
+
return 0.5*(rs*rs+rp*rp);
|
|
265
|
+
}
|
|
266
|
+
fn dielectricD(n: vec3f,h: vec3f,a: f32) -> f32 {
|
|
267
|
+
let nh=dot(n,h); let s=cross(n,h); let d=dot(s,s)+a*a*nh*nh;
|
|
268
|
+
if(nh<=0.0){return 0.0;}
|
|
269
|
+
return a*a/(3.141592653589793*d*d);
|
|
270
|
+
}
|
|
271
|
+
fn dielectricLambda(c: f32,a: f32) -> f32 {
|
|
272
|
+
return 0.5*(sqrt(1.0+a*a*max(0.0,1.0-c*c)/(c*c))-1.0);
|
|
273
|
+
}
|
|
274
|
+
fn dielectricTerms(normal: vec3f,v: vec3f,l: vec3f,m: DielectricMaterial) -> DielectricTerms {
|
|
275
|
+
let outside=dot(normal,v)>=0.0;
|
|
276
|
+
let n=select(-normal,normal,outside);
|
|
277
|
+
let e=select(m.etaOutside/m.etaInside,m.etaInside/m.etaOutside,outside);
|
|
278
|
+
let nv=dot(n,v); let nl=dot(n,l);
|
|
279
|
+
if(nv==0.0||nl==0.0||m.roughness==0.0||e==1.0){return DielectricTerms(0.0,0.0);}
|
|
280
|
+
let reflection=nl>0.0;
|
|
281
|
+
var sum=v+select(e,1.0,reflection)*l;
|
|
282
|
+
if(dot(sum,sum)==0.0){return DielectricTerms(0.0,0.0);}
|
|
283
|
+
if(reflection && dot(v,l)<0.0){sum=cross(cross(v,l),v-l);}
|
|
284
|
+
var h=normalize(sum); if(dot(n,h)<0.0){h=-h;}
|
|
285
|
+
return dielectricTermsWithHalfVector(n,v,l,h,e,m);
|
|
286
|
+
}
|
|
287
|
+
fn dielectricTermsWithHalfVector(n: vec3f,v: vec3f,l: vec3f,h: vec3f,e: f32,m: DielectricMaterial) -> DielectricTerms {
|
|
288
|
+
let nv=dot(n,v);let nl=dot(n,l);let reflection=nl>0.0;
|
|
289
|
+
if(nv<=0.0||nl==0.0){return DielectricTerms(0.0,0.0);}
|
|
290
|
+
let vh=dot(v,h); let lh=dot(l,h);
|
|
291
|
+
if(vh<=0.0||lh*nl<=0.0){return DielectricTerms(0.0,0.0);}
|
|
292
|
+
let a=max(0.0025,m.roughness*m.roughness);
|
|
293
|
+
let d=dielectricD(n,h,a); let lv=dielectricLambda(nv,a); let ll=dielectricLambda(abs(nl),a);
|
|
294
|
+
let g=1.0/(1.0+lv+ll); let pm=d*vh/(nv*(1.0+lv));
|
|
295
|
+
let f=dielectricFresnel(min(1.0,vh),e);
|
|
296
|
+
if(reflection){return DielectricTerms(d*g*f/(4.0*nv*nl),pm*f/(4.0*vh));}
|
|
297
|
+
let den=vh+e*lh;
|
|
298
|
+
if(den==0.0){return DielectricTerms(0.0,0.0);}
|
|
299
|
+
let jac=e*e*abs(lh)/(den*den);
|
|
300
|
+
let transport=select(1.0/(e*e),1.0,m.transport==1u);
|
|
301
|
+
return DielectricTerms((1.0-f)*d*g*vh*jac/(nv*abs(nl))*transport,pm*(1.0-f)*jac);
|
|
302
|
+
}
|
|
303
|
+
// Validated finite, nonzero vectors and material inputs required (same CPU contract).
|
|
304
|
+
fn dielectricEvaluate(normal: vec3f,view: vec3f,light: vec3f,m: DielectricMaterial) -> vec3f {
|
|
305
|
+
let n=normalize(normal);let v=normalize(view);let l=normalize(light);
|
|
306
|
+
let color=select(vec3f(1.0),m.tint,dot(n,v)*dot(n,l)<0.0);
|
|
307
|
+
return color*dielectricTerms(n,v,l,m).f;
|
|
308
|
+
}
|
|
309
|
+
fn dielectricPdf(normal: vec3f,view: vec3f,light: vec3f,m: DielectricMaterial) -> f32 {
|
|
310
|
+
return dielectricTerms(normalize(normal),normalize(view),normalize(light),m).pdf;
|
|
311
|
+
}
|
|
312
|
+
fn dielectricNull() -> DielectricSample {
|
|
313
|
+
return DielectricSample(vec3f(0.0),vec3f(0.0),0.0,1.0,0u,0u);
|
|
314
|
+
}
|
|
315
|
+
fn dielectricVndf(n: vec3f,v: vec3f,a: f32,u: f32,w: f32) -> vec3f {
|
|
316
|
+
let axis=select(vec3f(1.0,0.0,0.0),vec3f(0.0,0.0,1.0),abs(n.z)<0.999);
|
|
317
|
+
let t=normalize(cross(axis,n));let b=cross(n,t);
|
|
318
|
+
let vh=normalize(vec3f(a*dot(v,t),a*dot(v,b),dot(v,n)));
|
|
319
|
+
let lensq=vh.x*vh.x+vh.y*vh.y;
|
|
320
|
+
var t1=vec3f(1.0,0.0,0.0);
|
|
321
|
+
if(lensq>0.0){t1=vec3f(-vh.y,vh.x,0.0)/sqrt(lensq);}
|
|
322
|
+
let t2=cross(vh,t1);let r=sqrt(u);let phi=6.283185307179586*w;
|
|
323
|
+
let x=r*cos(phi);let s=0.5*(1.0+vh.z);
|
|
324
|
+
let y=(1.0-s)*sqrt(max(0.0,1.0-x*x))+s*r*sin(phi);
|
|
325
|
+
let z=sqrt(max(0.0,1.0-x*x-y*y));
|
|
326
|
+
let nh=x*t1+y*t2+z*vh;
|
|
327
|
+
let h=normalize(vec3f(a*nh.x,a*nh.y,max(0.0,nh.z)));
|
|
328
|
+
return normalize(t*h.x+b*h.y+n*h.z);
|
|
329
|
+
}
|
|
330
|
+
fn dielectricSample(normal: vec3f,view: vec3f,m: DielectricMaterial,random: vec3f) -> DielectricSample {
|
|
331
|
+
let v=normalize(view);let nn=normalize(normal);let outside=dot(nn,v)>=0.0;
|
|
332
|
+
let n=select(-nn,nn,outside);
|
|
333
|
+
let e=select(m.etaOutside/m.etaInside,m.etaInside/m.etaOutside,outside);
|
|
334
|
+
let nv=dot(n,v);if(nv==0.0){return dielectricNull();}
|
|
335
|
+
let delta=m.roughness==0.0||e==1.0;
|
|
336
|
+
var h=n;
|
|
337
|
+
if(!delta){h=dielectricVndf(n,v,max(0.0025,m.roughness*m.roughness),random.y,random.z);}
|
|
338
|
+
let vh=clamp(dot(v,h),0.0,1.0);let f=dielectricFresnel(vh,e);
|
|
339
|
+
let reflection=random.x<f;var l: vec3f;
|
|
340
|
+
if(reflection){l=-v+2.0*vh*h;}
|
|
341
|
+
else{
|
|
342
|
+
let sin2=max(0.0,1.0-vh*vh)/(e*e);
|
|
343
|
+
if(sin2>=1.0){return dielectricNull();}
|
|
344
|
+
l=-v/e+(vh/e-sqrt(1.0-sin2))*h;
|
|
345
|
+
}
|
|
346
|
+
l=normalize(l);let nl=dot(n,l);
|
|
347
|
+
if(nl==0.0||(reflection&&nl<0.0)||(!reflection&&nl>0.0)){return dielectricNull();}
|
|
348
|
+
let color=select(m.tint,vec3f(1.0),reflection);
|
|
349
|
+
let transport=select(1.0/(e*e),1.0,reflection||m.transport==1u);
|
|
350
|
+
let event=select(2u,1u,reflection);let eta=select(e,1.0,reflection);
|
|
351
|
+
if(delta){return DielectricSample(l,color*transport,select(1.0-f,f,reflection),eta,event,1u);}
|
|
352
|
+
let terms=dielectricTermsWithHalfVector(n,v,l,h,e,m);
|
|
353
|
+
if(terms.pdf<=0.0){return dielectricNull();}
|
|
354
|
+
return DielectricSample(l,color*(terms.f*abs(nl)/terms.pdf),terms.pdf,eta,event,0u);
|
|
355
|
+
}
|
|
356
|
+
fn dielectricBeerAttenuation(color: vec3f,attenuationDistance: f32,length: f32) -> vec3f {
|
|
357
|
+
if(length==0.0){return vec3f(1.0);}
|
|
358
|
+
return pow(color,vec3f(length/attenuationDistance));
|
|
359
|
+
}
|
|
360
|
+
`;
|