@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.
- package/package.json +1 -1
- package/src/extras/arrows/index.ts +3 -3
- package/src/extras/caustic.ts +37 -0
- package/src/extras/gradient/index.ts +63 -69
- package/src/extras/index.ts +3 -0
- package/src/extras/lines/index.ts +3 -3
- package/src/extras/skylab/index.ts +314 -0
- package/src/extras/text/font.ts +69 -14
- package/src/extras/text/index.ts +15 -7
- package/src/extras/text/sdf.ts +13 -2
- package/src/extras/water.ts +64 -0
- package/src/standard/defaults.ts +2 -0
- package/src/standard/index.ts +2 -0
- package/src/standard/raster/index.ts +517 -0
- package/src/standard/{render → raytracing}/bvh/blas.ts +3 -3
- package/src/standard/{render → raytracing}/bvh/tlas.ts +3 -0
- package/src/standard/{render → raytracing}/depth.ts +9 -9
- package/src/standard/raytracing/index.ts +380 -0
- package/src/standard/{render → raytracing}/instance.ts +3 -0
- package/src/standard/raytracing/shaders.ts +815 -0
- package/src/standard/{render → raytracing}/triangle.ts +1 -1
- package/src/standard/render/camera.ts +88 -80
- package/src/standard/render/index.ts +68 -208
- package/src/standard/render/indirect.ts +9 -10
- package/src/standard/render/mesh/index.ts +35 -166
- package/src/standard/render/overlay.ts +4 -4
- package/src/standard/render/pass.ts +1 -1
- package/src/standard/render/postprocess.ts +75 -50
- package/src/standard/render/scene.ts +28 -16
- package/src/standard/render/surface/compile.ts +6 -8
- package/src/standard/render/surface/noise.ts +15 -2
- package/src/standard/render/surface/shaders.ts +257 -0
- package/src/standard/render/surface/structs.ts +13 -6
- package/src/standard/render/forward/index.ts +0 -259
- package/src/standard/render/forward/raster.ts +0 -228
- package/src/standard/render/shaders.ts +0 -484
- package/src/standard/render/surface/wgsl.ts +0 -573
- /package/src/standard/{render → raytracing}/bvh/radix.ts +0 -0
- /package/src/standard/{render → raytracing}/bvh/structs.ts +0 -0
- /package/src/standard/{render → raytracing}/bvh/traverse.ts +0 -0
- /package/src/standard/{render → raytracing}/intersection.ts +0 -0
- /package/src/standard/{render → raytracing}/ray.ts +0 -0
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
export function compileVertexBody(vertex?: string): string {
|
|
2
|
-
return vertex
|
|
3
|
-
? `var pos = worldPos;
|
|
4
|
-
${vertex}
|
|
5
|
-
return pos;`
|
|
6
|
-
: "return worldPos;";
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const STARS_WGSL = /* wgsl */ `
|
|
10
|
-
fn hashStar(p: vec2<f32>) -> f32 {
|
|
11
|
-
var p3 = fract(vec3(p.x, p.y, p.x) * 0.1031);
|
|
12
|
-
p3 += dot(p3, p3.yzx + 33.33);
|
|
13
|
-
return fract((p3.x + p3.y) * p3.z);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
fn hash2Star(p: vec2<f32>) -> vec2<f32> {
|
|
17
|
-
var p3 = fract(vec3(p.x, p.y, p.x) * vec3(0.1031, 0.1030, 0.0973));
|
|
18
|
-
p3 += dot(p3, p3.yzx + 33.33);
|
|
19
|
-
return fract((p3.xx + p3.yz) * p3.zy);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
fn sampleStars(dir: vec3<f32>) -> vec3<f32> {
|
|
23
|
-
if (scene.starParams.z <= 0.0 || dir.y < 0.0) {
|
|
24
|
-
return vec3(0.0);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let theta = atan2(dir.z, dir.x);
|
|
28
|
-
let phi = asin(clamp(dir.y, -1.0, 1.0));
|
|
29
|
-
|
|
30
|
-
let gridSize = mix(20.0, 100.0, scene.starParams.y);
|
|
31
|
-
let cell = vec2(theta * gridSize / 3.14159, phi * gridSize / 1.5708);
|
|
32
|
-
let cellId = floor(cell);
|
|
33
|
-
let cellFract = fract(cell);
|
|
34
|
-
|
|
35
|
-
var starColor = vec3(0.0);
|
|
36
|
-
|
|
37
|
-
for (var dy = -1; dy <= 1; dy++) {
|
|
38
|
-
for (var dx = -1; dx <= 1; dx++) {
|
|
39
|
-
let neighbor = cellId + vec2(f32(dx), f32(dy));
|
|
40
|
-
let starHash = hashStar(neighbor);
|
|
41
|
-
|
|
42
|
-
if (starHash > scene.starParams.y * 0.7) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let starPos = hash2Star(neighbor);
|
|
47
|
-
let starCenter = neighbor + starPos;
|
|
48
|
-
let dist = length(cell - starCenter);
|
|
49
|
-
|
|
50
|
-
let brightness = hashStar(neighbor + vec2(100.0, 100.0));
|
|
51
|
-
let radius = 0.02 + brightness * 0.03;
|
|
52
|
-
|
|
53
|
-
if (dist < radius) {
|
|
54
|
-
let twinkle = 0.8 + 0.2 * sin(brightness * 100.0);
|
|
55
|
-
let intensity = scene.starParams.x * brightness * twinkle;
|
|
56
|
-
let falloff = 1.0 - smoothstep(0.0, radius, dist);
|
|
57
|
-
|
|
58
|
-
let temp = hashStar(neighbor + vec2(200.0, 200.0));
|
|
59
|
-
let tint = mix(vec3(1.0, 0.9, 0.8), vec3(0.8, 0.9, 1.0), temp);
|
|
60
|
-
|
|
61
|
-
starColor = max(starColor, tint * intensity * falloff);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return starColor;
|
|
67
|
-
}
|
|
68
|
-
`;
|
|
69
|
-
|
|
70
|
-
import { NOISE_WGSL } from "./noise";
|
|
71
|
-
|
|
72
|
-
export const MOON_WGSL = /* wgsl */ `
|
|
73
|
-
fn sampleMoon(dir: vec3<f32>) -> vec3<f32> {
|
|
74
|
-
if (scene.moonParams.z <= 0.0) {
|
|
75
|
-
return vec3(0.0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let moonDir = scene.moonDirection.xyz;
|
|
79
|
-
let moonDot = dot(dir, moonDir);
|
|
80
|
-
|
|
81
|
-
let moonSize = 0.9995;
|
|
82
|
-
let moonColor = vec3(0.9, 0.9, 0.85);
|
|
83
|
-
|
|
84
|
-
if (moonDot > moonSize) {
|
|
85
|
-
let toCenter = dir - moonDir * moonDot;
|
|
86
|
-
let diskRight = normalize(cross(moonDir, vec3(0.0, 1.0, 0.0)));
|
|
87
|
-
let diskUp = cross(diskRight, moonDir);
|
|
88
|
-
|
|
89
|
-
let diskRadius = sqrt(1.0 - moonSize * moonSize);
|
|
90
|
-
let u = dot(toCenter, diskRight) / diskRadius;
|
|
91
|
-
let v = dot(toCenter, diskUp) / diskRadius;
|
|
92
|
-
|
|
93
|
-
let r2 = u * u + v * v;
|
|
94
|
-
let z = sqrt(max(0.0, 1.0 - r2));
|
|
95
|
-
|
|
96
|
-
let phase = scene.moonParams.x;
|
|
97
|
-
let sunAngle = phase * 6.28318;
|
|
98
|
-
let sunLocalX = sin(sunAngle);
|
|
99
|
-
let sunLocalZ = -cos(sunAngle);
|
|
100
|
-
|
|
101
|
-
let illumination = u * sunLocalX + z * sunLocalZ;
|
|
102
|
-
let lit = select(0.15, 1.0, illumination > 0.0);
|
|
103
|
-
|
|
104
|
-
return moonColor * lit;
|
|
105
|
-
} else {
|
|
106
|
-
let glowFalloff = max(0.0, moonDot - 0.99) / (moonSize - 0.99);
|
|
107
|
-
let glow = pow(glowFalloff, 2.0) * scene.moonParams.y;
|
|
108
|
-
return moonColor * glow * 0.3;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
`;
|
|
112
|
-
|
|
113
|
-
export const CLOUDS_WGSL = /* wgsl */ `
|
|
114
|
-
${NOISE_WGSL}
|
|
115
|
-
|
|
116
|
-
fn sampleClouds(dir: vec3<f32>) -> vec4<f32> {
|
|
117
|
-
if (scene.cloudParams.w <= 0.0 || dir.y < 0.01) {
|
|
118
|
-
return vec4(0.0);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let t = scene.cloudParams.z / max(dir.y, 0.001);
|
|
122
|
-
let uv = dir.xz * t;
|
|
123
|
-
|
|
124
|
-
var n = fbm2(uv, 5);
|
|
125
|
-
|
|
126
|
-
let coverage = scene.cloudParams.x;
|
|
127
|
-
let density = scene.cloudParams.y;
|
|
128
|
-
n = smoothstep(1.0 - coverage, 1.0, n * 0.5 + 0.5) * density;
|
|
129
|
-
|
|
130
|
-
n *= smoothstep(0.0, 0.15, dir.y);
|
|
131
|
-
|
|
132
|
-
return vec4(scene.cloudColor.rgb, n);
|
|
133
|
-
}
|
|
134
|
-
`;
|
|
135
|
-
|
|
136
|
-
const DEG_TO_RAD = Math.PI / 180;
|
|
137
|
-
|
|
138
|
-
export const SKY_DIR_WGSL = /* wgsl */ `
|
|
139
|
-
const DEG_TO_RAD: f32 = ${DEG_TO_RAD};
|
|
140
|
-
|
|
141
|
-
fn computeSkyDir(screenX: f32, screenY: f32) -> vec3<f32> {
|
|
142
|
-
let width = scene.viewport.x;
|
|
143
|
-
let height = scene.viewport.y;
|
|
144
|
-
|
|
145
|
-
let ndcX = screenX * 2.0 - 1.0;
|
|
146
|
-
let ndcY = 1.0 - screenY * 2.0;
|
|
147
|
-
|
|
148
|
-
let aspect = width / height;
|
|
149
|
-
|
|
150
|
-
let cameraWorld = scene.cameraWorld;
|
|
151
|
-
let r00 = cameraWorld[0][0]; let r10 = cameraWorld[0][1]; let r20 = cameraWorld[0][2];
|
|
152
|
-
let r01 = cameraWorld[1][0]; let r11 = cameraWorld[1][1]; let r21 = cameraWorld[1][2];
|
|
153
|
-
let r02 = cameraWorld[2][0]; let r12 = cameraWorld[2][1]; let r22 = cameraWorld[2][2];
|
|
154
|
-
|
|
155
|
-
let skyFov = select(scene.fov, 60.0, scene.cameraMode > 0.5);
|
|
156
|
-
let tanHalfFov = tan((skyFov * DEG_TO_RAD) / 2.0);
|
|
157
|
-
let camDirX = ndcX * aspect * tanHalfFov;
|
|
158
|
-
let camDirY = ndcY * tanHalfFov;
|
|
159
|
-
let camDirZ = -1.0;
|
|
160
|
-
var dirX = r00 * camDirX + r01 * camDirY + r02 * camDirZ;
|
|
161
|
-
var dirY = r10 * camDirX + r11 * camDirY + r12 * camDirZ;
|
|
162
|
-
var dirZ = r20 * camDirX + r21 * camDirY + r22 * camDirZ;
|
|
163
|
-
let len = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
|
|
164
|
-
dirX /= len; dirY /= len; dirZ /= len;
|
|
165
|
-
return vec3(dirX, dirY, dirZ);
|
|
166
|
-
}
|
|
167
|
-
`;
|
|
168
|
-
|
|
169
|
-
export const SKY_WGSL = /* wgsl */ `
|
|
170
|
-
${STARS_WGSL}
|
|
171
|
-
${MOON_WGSL}
|
|
172
|
-
${CLOUDS_WGSL}
|
|
173
|
-
|
|
174
|
-
fn sampleSky(dir: vec3<f32>) -> vec3<f32> {
|
|
175
|
-
if (scene.skyZenith.a <= 0.0) {
|
|
176
|
-
return scene.clearColor.rgb;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
let t = pow(clamp(dir.y, 0.0, 1.0), 0.25);
|
|
180
|
-
var color = mix(scene.skyHorizon.rgb, scene.skyZenith.rgb, t);
|
|
181
|
-
|
|
182
|
-
let horizonBand = 1.0 - abs(dir.y);
|
|
183
|
-
let horizonBlend = pow(horizonBand, 32.0);
|
|
184
|
-
color = mix(color, vec3(1.09), horizonBlend);
|
|
185
|
-
|
|
186
|
-
color += sampleStars(dir);
|
|
187
|
-
|
|
188
|
-
let clouds = sampleClouds(dir);
|
|
189
|
-
color = mix(color, clouds.rgb, clouds.a);
|
|
190
|
-
|
|
191
|
-
let moonContrib = sampleMoon(dir);
|
|
192
|
-
color += moonContrib * (1.0 - clouds.a * 0.7);
|
|
193
|
-
|
|
194
|
-
let sunDir = -scene.sunDirection.xyz;
|
|
195
|
-
let sunDot = dot(dir, sunDir);
|
|
196
|
-
|
|
197
|
-
let sunVisualColor = select(scene.sunColor.rgb, scene.sunVisualColor.rgb, scene.sunParams.z > 0.5);
|
|
198
|
-
|
|
199
|
-
let baseSunSize = 0.9995;
|
|
200
|
-
let sunSizeParam = scene.sunParams.x;
|
|
201
|
-
let sunThreshold = 1.0 - (1.0 - baseSunSize) * sunSizeParam;
|
|
202
|
-
|
|
203
|
-
if (sunDot > sunThreshold) {
|
|
204
|
-
color = sunVisualColor;
|
|
205
|
-
} else {
|
|
206
|
-
let glow = max(0.0, sunDot);
|
|
207
|
-
let glowParam = scene.sunParams.y;
|
|
208
|
-
let glowIntensity = pow(glow, 8.0) * glowParam;
|
|
209
|
-
color += sunVisualColor * glowIntensity;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (scene.hazeDensity > 0.0) {
|
|
213
|
-
let horizonFactor = 1.0 - clamp(dir.y, 0.0, 1.0);
|
|
214
|
-
let hazeAmount = pow(horizonFactor, 2.0) * saturate(scene.hazeDensity * 5.0);
|
|
215
|
-
color = mix(color, scene.hazeColor.rgb, hazeAmount);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return color;
|
|
219
|
-
}
|
|
220
|
-
`;
|
|
221
|
-
|
|
222
|
-
export const HAZE_WGSL = /* wgsl */ `
|
|
223
|
-
fn applyHaze(color: vec3<f32>, dist: f32) -> vec3<f32> {
|
|
224
|
-
if (scene.hazeDensity <= 0.0) {
|
|
225
|
-
return color;
|
|
226
|
-
}
|
|
227
|
-
let haze = 1.0 - exp(-scene.hazeDensity * dist);
|
|
228
|
-
return mix(color, scene.hazeColor.rgb, haze);
|
|
229
|
-
}
|
|
230
|
-
`;
|
|
231
|
-
|
|
232
|
-
export const SPECULAR_WGSL = /* wgsl */ `
|
|
233
|
-
const DIELECTRIC_F0: f32 = 0.04;
|
|
234
|
-
|
|
235
|
-
fn blinnPhongSpecular(N: vec3<f32>, L: vec3<f32>, V: vec3<f32>, roughness: f32) -> f32 {
|
|
236
|
-
let H = normalize(L + V);
|
|
237
|
-
let NdotH = max(dot(N, H), 0.0);
|
|
238
|
-
let shininess = pow(2.0, (1.0 - roughness) * 10.0);
|
|
239
|
-
let intensity = (1.0 - roughness) * (1.0 - roughness);
|
|
240
|
-
return pow(NdotH, shininess) * intensity;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
fn schlickFresnel(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {
|
|
244
|
-
return F0 + (vec3(1.0) - F0) * pow(1.0 - cosTheta, 5.0);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
fn computeF0Vec(baseColor: vec3<f32>, metallic: f32) -> vec3<f32> {
|
|
248
|
-
return mix(vec3(DIELECTRIC_F0), baseColor, metallic);
|
|
249
|
-
}
|
|
250
|
-
`;
|
|
251
|
-
|
|
252
|
-
export const REFRACTION_WGSL = /* wgsl */ `
|
|
253
|
-
fn refractRay(I: vec3<f32>, N: vec3<f32>, eta: f32) -> vec4<f32> {
|
|
254
|
-
let cosI = -dot(I, N);
|
|
255
|
-
let sinT2 = eta * eta * (1.0 - cosI * cosI);
|
|
256
|
-
if (sinT2 > 1.0) {
|
|
257
|
-
return vec4(reflect(I, N), 1.0);
|
|
258
|
-
}
|
|
259
|
-
let cosT = sqrt(1.0 - sinT2);
|
|
260
|
-
return vec4(normalize(eta * I + (eta * cosI - cosT) * N), 0.0);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
fn fresnelSchlickIOR(cosTheta: f32, n1: f32, n2: f32) -> f32 {
|
|
264
|
-
let r0 = pow((n1 - n2) / (n1 + n2), 2.0);
|
|
265
|
-
return r0 + (1.0 - r0) * pow(1.0 - cosTheta, 5.0);
|
|
266
|
-
}
|
|
267
|
-
`;
|
|
268
|
-
|
|
269
|
-
export function reflectionWgsl(shadows: boolean): string {
|
|
270
|
-
const shadowCheck = shadows
|
|
271
|
-
? `var hitShadow = 1.0;
|
|
272
|
-
if (hitNdotL > 0.0) {
|
|
273
|
-
var shadowRay: Ray;
|
|
274
|
-
shadowRay.origin = hit.worldPos + hit.normal * REFLECTION_EPSILON;
|
|
275
|
-
shadowRay.direction = hitL;
|
|
276
|
-
if (traceAnyHit(shadowRay, 1000.0)) { hitShadow = 0.0; }
|
|
277
|
-
}`
|
|
278
|
-
: "let hitShadow = 1.0;";
|
|
279
|
-
|
|
280
|
-
return /* wgsl */ `
|
|
281
|
-
const REFLECTION_EPSILON: f32 = 0.001;
|
|
282
|
-
const MIN_CONTRIBUTION: f32 = 0.02;
|
|
283
|
-
|
|
284
|
-
fn traceReflections(
|
|
285
|
-
startPos: vec3<f32>,
|
|
286
|
-
startNormal: vec3<f32>,
|
|
287
|
-
startDir: vec3<f32>,
|
|
288
|
-
diffuseColor: vec3<f32>,
|
|
289
|
-
startBaseColor: vec3<f32>,
|
|
290
|
-
startRoughness: f32,
|
|
291
|
-
startMetallic: f32,
|
|
292
|
-
maxBounces: u32
|
|
293
|
-
) -> vec3<f32> {
|
|
294
|
-
let V = -startDir;
|
|
295
|
-
let NdotV = max(dot(startNormal, V), 0.0);
|
|
296
|
-
|
|
297
|
-
let F0 = computeF0Vec(startBaseColor, startMetallic);
|
|
298
|
-
var F = schlickFresnel(NdotV, F0);
|
|
299
|
-
|
|
300
|
-
let smoothness = 1.0 - startRoughness;
|
|
301
|
-
let roughnessAtten = smoothness * smoothness;
|
|
302
|
-
F *= roughnessAtten;
|
|
303
|
-
|
|
304
|
-
let avgF = (F.x + F.y + F.z) / 3.0;
|
|
305
|
-
if (avgF < MIN_CONTRIBUTION) {
|
|
306
|
-
return diffuseColor;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
var currentPos = startPos;
|
|
310
|
-
var currentNormal = startNormal;
|
|
311
|
-
var currentDir = startDir;
|
|
312
|
-
var reflectionColor = vec3(0.0);
|
|
313
|
-
var throughput = F;
|
|
314
|
-
|
|
315
|
-
for (var bounce = 0u; bounce < maxBounces; bounce++) {
|
|
316
|
-
let avgThroughput = (throughput.x + throughput.y + throughput.z) / 3.0;
|
|
317
|
-
if (avgThroughput < MIN_CONTRIBUTION) {
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
let reflectDir = reflect(currentDir, currentNormal);
|
|
322
|
-
var reflectRay: Ray;
|
|
323
|
-
reflectRay.origin = currentPos + currentNormal * REFLECTION_EPSILON;
|
|
324
|
-
reflectRay.direction = reflectDir;
|
|
325
|
-
|
|
326
|
-
var reflectColor = vec3(0.0);
|
|
327
|
-
var reflectRemaining = 1.0;
|
|
328
|
-
var opaqueHit: HitResult;
|
|
329
|
-
var foundOpaque = false;
|
|
330
|
-
|
|
331
|
-
for (var t = 0u; t < 2u; t++) {
|
|
332
|
-
let hit = trace(reflectRay);
|
|
333
|
-
if (!hit.hit) {
|
|
334
|
-
reflectColor += sampleSky(reflectDir) * reflectRemaining;
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
let eid = hit.entityId;
|
|
339
|
-
let hitData = getData(eid);
|
|
340
|
-
let hitOpacity = hitData.baseColor.a;
|
|
341
|
-
let hitBaseColor = hitData.baseColor.rgb;
|
|
342
|
-
let hitRoughness = hitData.pbr.x;
|
|
343
|
-
let hitMetallic = hitData.pbr.y;
|
|
344
|
-
let hitEmission = hitData.emission.rgb * hitData.emission.a;
|
|
345
|
-
|
|
346
|
-
let hitV = -reflectDir;
|
|
347
|
-
let hitL = -scene.sunDirection.xyz;
|
|
348
|
-
let hitNdotL = max(dot(hit.normal, hitL), 0.0);
|
|
349
|
-
let hitNdotV = max(dot(hit.normal, hitV), 0.0);
|
|
350
|
-
${shadowCheck}
|
|
351
|
-
|
|
352
|
-
let hitF0 = computeF0Vec(hitBaseColor, hitMetallic);
|
|
353
|
-
let hitF = schlickFresnel(hitNdotV, hitF0);
|
|
354
|
-
let hitiffuseWeight = (1.0 - hitMetallic) * (vec3(1.0) - hitF);
|
|
355
|
-
let hitAmbient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
356
|
-
let hitSun = scene.sunColor.rgb * hitNdotL * hitShadow;
|
|
357
|
-
let hitiffuse = hitBaseColor * (hitAmbient + hitSun) * hitiffuseWeight;
|
|
358
|
-
let hitSpec = blinnPhongSpecular(hit.normal, hitL, hitV, hitRoughness);
|
|
359
|
-
let hitSpecular = scene.sunColor.rgb * hitSpec * hitF * hitNdotL * hitShadow;
|
|
360
|
-
let hitLit = hitiffuse + hitSpecular + hitEmission;
|
|
361
|
-
|
|
362
|
-
reflectColor += applyHaze(hitLit, hit.t) * hitOpacity * reflectRemaining;
|
|
363
|
-
|
|
364
|
-
if (hitOpacity >= 1.0) {
|
|
365
|
-
opaqueHit = hit;
|
|
366
|
-
foundOpaque = true;
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
reflectRemaining *= (1.0 - hitOpacity);
|
|
371
|
-
if (reflectRemaining < 0.02) { break; }
|
|
372
|
-
reflectRay.origin = hit.worldPos + reflectDir * REFLECTION_EPSILON;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
reflectionColor += throughput * reflectColor;
|
|
376
|
-
|
|
377
|
-
if (!foundOpaque) { break; }
|
|
378
|
-
|
|
379
|
-
let opaqueEid = opaqueHit.entityId;
|
|
380
|
-
let opaque = getData(opaqueEid);
|
|
381
|
-
let hitSmoothness = 1.0 - opaque.pbr.x;
|
|
382
|
-
let hitRoughnessAtten = hitSmoothness * hitSmoothness;
|
|
383
|
-
let opaqueF0 = computeF0Vec(opaque.baseColor.rgb, opaque.pbr.y);
|
|
384
|
-
let opaqueNdotV = max(dot(opaqueHit.normal, -reflectDir), 0.0);
|
|
385
|
-
let opaqueF = schlickFresnel(opaqueNdotV, opaqueF0);
|
|
386
|
-
throughput *= opaqueF * hitRoughnessAtten;
|
|
387
|
-
currentPos = opaqueHit.worldPos;
|
|
388
|
-
currentNormal = opaqueHit.normal;
|
|
389
|
-
currentDir = reflectDir;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return diffuseColor * (vec3(1.0) - F) + reflectionColor;
|
|
393
|
-
}
|
|
394
|
-
`;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export const WGSL_LIGHTING_CALC = /* wgsl */ `
|
|
398
|
-
let NdotL = max(dot(surface.normal, -scene.sunDirection.xyz), 0.0);
|
|
399
|
-
let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
400
|
-
let sunDiffuse = scene.sunColor.rgb * NdotL;
|
|
401
|
-
let diffuseWeight = 1.0 - surface.metallic;
|
|
402
|
-
let lighting = (ambient + sunDiffuse) * diffuseWeight;
|
|
403
|
-
`;
|
|
404
|
-
|
|
405
|
-
export const SHADOW_WGSL = /* wgsl */ `
|
|
406
|
-
const GOLDEN_ANGLE: f32 = 2.39996323;
|
|
407
|
-
const SHADOW_TRANSPARENCY_EPSILON: f32 = 0.001;
|
|
408
|
-
const MAX_SHADOW_TRANSPARENT_DEPTH: u32 = 2u;
|
|
409
|
-
|
|
410
|
-
fn buildTangentBasis(dir: vec3<f32>) -> mat3x3<f32> {
|
|
411
|
-
let up = select(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), abs(dir.y) > 0.99);
|
|
412
|
-
let tangent = normalize(cross(dir, up));
|
|
413
|
-
let bitangent = cross(dir, tangent);
|
|
414
|
-
return mat3x3(tangent, bitangent, dir);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
fn traceShadowWithTransparency(origin: vec3<f32>, sunDir: vec3<f32>, tMax: f32) -> f32 {
|
|
418
|
-
var shadowFactor = 1.0;
|
|
419
|
-
var currentPos = origin;
|
|
420
|
-
|
|
421
|
-
for (var i = 0u; i < MAX_SHADOW_TRANSPARENT_DEPTH; i++) {
|
|
422
|
-
var shadowRay: Ray;
|
|
423
|
-
shadowRay.origin = currentPos;
|
|
424
|
-
shadowRay.direction = sunDir;
|
|
425
|
-
|
|
426
|
-
let hit = trace(shadowRay);
|
|
427
|
-
if (!hit.hit || hit.t > tMax) {
|
|
428
|
-
break;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
let shadow = getData(hit.entityId);
|
|
432
|
-
let opacity = shadow.baseColor.a;
|
|
433
|
-
shadowFactor *= (1.0 - opacity);
|
|
434
|
-
|
|
435
|
-
if (opacity >= 1.0 || shadowFactor < 0.02) {
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
currentPos = hit.worldPos + sunDir * SHADOW_TRANSPARENCY_EPSILON;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return shadowFactor;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
fn sampleSoftShadow(origin: vec3<f32>, sunDir: vec3<f32>, softness: f32, samples: u32) -> f32 {
|
|
446
|
-
if (samples == 0u) {
|
|
447
|
-
return 1.0;
|
|
448
|
-
}
|
|
449
|
-
if (samples == 1u || softness <= 0.0) {
|
|
450
|
-
return traceShadowWithTransparency(origin, sunDir, 1000.0);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
let basis = buildTangentBasis(sunDir);
|
|
454
|
-
var totalShadow = 0.0;
|
|
455
|
-
|
|
456
|
-
let noise = fract(sin(dot(origin.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
|
457
|
-
let rotationOffset = noise * 6.28318530718;
|
|
458
|
-
|
|
459
|
-
for (var i = 0u; i < samples; i++) {
|
|
460
|
-
let angle = f32(i) * GOLDEN_ANGLE + rotationOffset;
|
|
461
|
-
let z = (f32(i) + 0.5) / f32(samples);
|
|
462
|
-
let r = sqrt(1.0 - z * z) * softness * 0.1;
|
|
463
|
-
|
|
464
|
-
let localOffset = vec3(cos(angle) * r, sin(angle) * r, 0.0);
|
|
465
|
-
let sampleDir = normalize(sunDir + basis * localOffset);
|
|
466
|
-
|
|
467
|
-
totalShadow += traceShadowWithTransparency(origin, sampleDir, 1000.0);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return totalShadow / f32(samples);
|
|
471
|
-
}
|
|
472
|
-
`;
|
|
473
|
-
|
|
474
|
-
export function compileApplyLighting(
|
|
475
|
-
lit?: boolean,
|
|
476
|
-
shadows?: boolean,
|
|
477
|
-
reflections?: boolean
|
|
478
|
-
): string {
|
|
479
|
-
if (lit === false) {
|
|
480
|
-
return "return surface.baseColor;";
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (shadows && reflections) {
|
|
484
|
-
return `
|
|
485
|
-
let V = -rayDir;
|
|
486
|
-
let L = -scene.sunDirection.xyz;
|
|
487
|
-
let NdotL = max(dot(surface.normal, L), 0.0);
|
|
488
|
-
let NdotV = max(dot(surface.normal, V), 0.0);
|
|
489
|
-
|
|
490
|
-
var shadowFactor = 1.0;
|
|
491
|
-
if (NdotL > 0.0) {
|
|
492
|
-
let shadowOrigin = surface.worldPos + surface.normal * 0.001;
|
|
493
|
-
shadowFactor = sampleSoftShadow(shadowOrigin, L, scene.shadowSoftness, scene.shadowSamples);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
let F0 = computeF0Vec(surface.baseColor, surface.metallic);
|
|
497
|
-
let F = schlickFresnel(NdotV, F0);
|
|
498
|
-
|
|
499
|
-
let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
500
|
-
let sunDiffuse = scene.sunColor.rgb * NdotL * shadowFactor;
|
|
501
|
-
let diffuseWeight = 1.0 - surface.metallic;
|
|
502
|
-
let diffuseColor = surface.baseColor * (ambient + sunDiffuse) * diffuseWeight + surface.emission;
|
|
503
|
-
|
|
504
|
-
let specTerm = blinnPhongSpecular(surface.normal, L, V, surface.roughness);
|
|
505
|
-
let specular = scene.sunColor.rgb * specTerm * F * NdotL * shadowFactor;
|
|
506
|
-
|
|
507
|
-
var finalColor = diffuseColor * (vec3(1.0) - F) + specular;
|
|
508
|
-
|
|
509
|
-
if (scene.reflectionDepth > 0u) {
|
|
510
|
-
finalColor = traceReflections(
|
|
511
|
-
surface.worldPos, surface.normal, rayDir,
|
|
512
|
-
diffuseColor + specular, surface.baseColor,
|
|
513
|
-
surface.roughness, surface.metallic,
|
|
514
|
-
scene.reflectionDepth
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
return finalColor;`;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
if (shadows) {
|
|
521
|
-
return `
|
|
522
|
-
let NdotL = max(dot(surface.normal, -scene.sunDirection.xyz), 0.0);
|
|
523
|
-
let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
524
|
-
|
|
525
|
-
var shadowFactor = 1.0;
|
|
526
|
-
if (NdotL > 0.0) {
|
|
527
|
-
let shadowOrigin = surface.worldPos + surface.normal * 0.001;
|
|
528
|
-
shadowFactor = sampleSoftShadow(
|
|
529
|
-
shadowOrigin, -scene.sunDirection.xyz,
|
|
530
|
-
scene.shadowSoftness, scene.shadowSamples
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
let sunDiffuse = scene.sunColor.rgb * NdotL * shadowFactor;
|
|
535
|
-
let diffuseWeight = 1.0 - surface.metallic;
|
|
536
|
-
let lighting = (ambient + sunDiffuse) * diffuseWeight;
|
|
537
|
-
return surface.baseColor * lighting + surface.emission;`;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (reflections) {
|
|
541
|
-
return `
|
|
542
|
-
let V = -rayDir;
|
|
543
|
-
let L = -scene.sunDirection.xyz;
|
|
544
|
-
let NdotL = max(dot(surface.normal, L), 0.0);
|
|
545
|
-
let NdotV = max(dot(surface.normal, V), 0.0);
|
|
546
|
-
|
|
547
|
-
let F0 = computeF0Vec(surface.baseColor, surface.metallic);
|
|
548
|
-
let F = schlickFresnel(NdotV, F0);
|
|
549
|
-
|
|
550
|
-
let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
551
|
-
let sunDiffuse = scene.sunColor.rgb * NdotL;
|
|
552
|
-
let diffuseWeight = 1.0 - surface.metallic;
|
|
553
|
-
let diffuseColor = surface.baseColor * (ambient + sunDiffuse) * diffuseWeight + surface.emission;
|
|
554
|
-
|
|
555
|
-
let specTerm = blinnPhongSpecular(surface.normal, L, V, surface.roughness);
|
|
556
|
-
let specular = scene.sunColor.rgb * specTerm * F * NdotL;
|
|
557
|
-
|
|
558
|
-
var finalColor = diffuseColor * (vec3(1.0) - F) + specular;
|
|
559
|
-
|
|
560
|
-
if (scene.reflectionDepth > 0u) {
|
|
561
|
-
finalColor = traceReflections(
|
|
562
|
-
surface.worldPos, surface.normal, rayDir,
|
|
563
|
-
diffuseColor + specular, surface.baseColor,
|
|
564
|
-
surface.roughness, surface.metallic,
|
|
565
|
-
scene.reflectionDepth
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
return finalColor;`;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return `${WGSL_LIGHTING_CALC}
|
|
572
|
-
return surface.baseColor * lighting + surface.emission;`;
|
|
573
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|