@driftengine/core 4.8.6 → 4.9.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/CHANGELOG.json +132 -0
- package/README.md +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -1
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +35 -0
- package/dist/render/areaLights.js +35 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +41 -3
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/webgl2/renderer.d.ts +140 -16
- package/dist/render/backend/webgl2/renderer.js +436 -44
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +16 -1
- package/dist/render/backend/webgpu/flatPass.js +58 -26
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +166 -3
- package/dist/render/backend/webgpu/renderer.js +738 -83
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +34 -8
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/clusteredLights.d.ts +23 -5
- package/dist/render/clusteredLights.js +35 -8
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- package/dist/render/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +5 -0
- package/dist/render/instances.d.ts +19 -5
- package/dist/render/instances.js +9 -3
- package/dist/render/lightBudget.d.ts +12 -1
- package/dist/render/lightBudget.js +23 -3
- package/dist/render/lightmap.d.ts +23 -9
- package/dist/render/lightmap.js +40 -18
- package/dist/render/particlePool.d.ts +21 -0
- package/dist/render/particlePool.js +7 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +47 -15
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +32 -1
- package/dist/render/screenSpaceReflection.js +14 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.js +11 -0
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +46 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +5 -1
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +22 -2
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +85 -22
- package/dist/render/shaders/flat/models.d.ts +5 -2
- package/dist/render/shaders/flat/models.js +100 -16
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +1 -1
- package/dist/render/shaders/flat/preamble.js +10 -3
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +192 -16
- package/dist/render/shaders/generated/flat.wgsl.js +230 -54
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +32 -7
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +32 -7
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- package/dist/render/shaders/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +46 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +107 -1
- package/dist/render/surfaceModel.d.ts +29 -8
- package/dist/render/surfaceModel.js +33 -9
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +44 -1
- package/dist/render/surfaceTexture.js +96 -5
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +22 -2
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +58 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +58 -2
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +548 -50
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +927 -100
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +44 -14
- package/src/render/clusteredLights.ts +41 -8
- package/src/render/displayRange.ts +61 -0
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +4 -0
- package/src/render/instances.ts +26 -6
- package/src/render/lightBudget.ts +40 -2
- package/src/render/lightmap.ts +64 -23
- package/src/render/particlePool.ts +23 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +52 -15
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +38 -1
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +46 -0
- package/src/render/shaders/flat/glassTint.ts +13 -2
- package/src/render/shaders/flat/index.ts +37 -2
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +85 -22
- package/src/render/shaders/flat/models.ts +100 -16
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +10 -3
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +4 -4
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +230 -54
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +32 -7
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +46 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +139 -1
- package/src/render/surfaceModel.ts +47 -10
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +136 -9
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** Fixed point-light budget shared by CPU selection and the flat shader. */
|
|
2
|
+
import { MAX_AREA_LIGHTS, packAreaSizesAndDoors } from './areaLights.js';
|
|
3
|
+
import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER, lampSourceRadius, lampWeight, lightChannelsOf, lightFalloffExponentOf, } from './clusteredLights.js';
|
|
2
4
|
/**
|
|
3
5
|
* How many point lights the shading pass shades against at once.
|
|
4
6
|
*
|
|
@@ -302,6 +304,8 @@ export const SDF_TEXT_TEXTURE_UNIT = SURFACE_TEXTURE_UNIT;
|
|
|
302
304
|
export const POINT_SHADOW_POOL = MAX_POINT_LIGHTS + 4;
|
|
303
305
|
/** Weights are optional per consumer; a short array means every light is fully present. */
|
|
304
306
|
const FULLY_PRESENT = new Float32Array(MAX_POINT_LIGHTS).fill(1);
|
|
307
|
+
/** The fixed arm's rectangles as `uAreaLightSize` holds them; refilled per bind, allocated once. */
|
|
308
|
+
const AREA_SIZES_AND_DOORS = new Float32Array(MAX_AREA_LIGHTS * 4);
|
|
305
309
|
/** No size at all, which is the behaviour every scene had before a light could have one. */
|
|
306
310
|
const POINT_SOURCES = new Float32Array(MAX_POINT_LIGHTS);
|
|
307
311
|
/**
|
|
@@ -394,7 +398,8 @@ export function bindPointLights(gl, uniforms, lights, falloff) {
|
|
|
394
398
|
* every lamp switched off.
|
|
395
399
|
*/
|
|
396
400
|
gl.uniform3fv(uniforms['uLightDir[0]'] ?? null, r.directions);
|
|
397
|
-
|
|
401
|
+
/* The cone and, in z, the light's own falloff exponent. See `conesAndFalloffs`. */
|
|
402
|
+
gl.uniform4fv(uniforms['uLightCone[0]'] ?? null, r.conesAndFalloffs);
|
|
398
403
|
gl.uniform1fv(uniforms['uLightIesProfile[0]'] ?? null, r.iesProfiles);
|
|
399
404
|
/* The azimuth and, in w, the channels. See `axesAndChannels`. */
|
|
400
405
|
gl.uniform4fv(uniforms['uLightIesAxis[0]'] ?? null, r.axesAndChannels);
|
|
@@ -421,7 +426,9 @@ export function bindAreaLights(gl, uniforms, lights) {
|
|
|
421
426
|
gl.uniform3fv(uniforms['uAreaLightColor[0]'] ?? null, lights.colors);
|
|
422
427
|
gl.uniform3fv(uniforms['uAreaLightRight[0]'] ?? null, lights.right);
|
|
423
428
|
gl.uniform3fv(uniforms['uAreaLightUp[0]'] ?? null, lights.up);
|
|
424
|
-
|
|
429
|
+
/* The half extents and, in zw, the barn doors. See `packAreaSizesAndDoors`. */
|
|
430
|
+
packAreaSizesAndDoors(lights, AREA_SIZES_AND_DOORS);
|
|
431
|
+
gl.uniform4fv(uniforms['uAreaLightSize[0]'] ?? null, AREA_SIZES_AND_DOORS);
|
|
425
432
|
gl.uniform1fv(uniforms['uAreaLightTwoSided[0]'] ?? null, lights.twoSided);
|
|
426
433
|
}
|
|
427
434
|
/**
|
|
@@ -463,6 +470,7 @@ const glLights = {
|
|
|
463
470
|
iesAxes: NO_IES_AXES,
|
|
464
471
|
cookies: NO_COOKIES,
|
|
465
472
|
axesAndChannels: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
473
|
+
conesAndFalloffs: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
466
474
|
heldSourceRadii: new Float32Array(MAX_POINT_LIGHTS),
|
|
467
475
|
heldWeights: new Float32Array(MAX_POINT_LIGHTS),
|
|
468
476
|
};
|
|
@@ -493,6 +501,7 @@ export function resolvePointLights(lights, falloff, out) {
|
|
|
493
501
|
out.iesAxes = NO_IES_AXES;
|
|
494
502
|
out.cookies = NO_COOKIES;
|
|
495
503
|
packAxesAndChannels(NO_IES_AXES, undefined, out.axesAndChannels);
|
|
504
|
+
packConesAndFalloffs(OPEN_CONES, undefined, out.conesAndFalloffs);
|
|
496
505
|
return out;
|
|
497
506
|
}
|
|
498
507
|
/*
|
|
@@ -574,8 +583,19 @@ export function resolvePointLights(lights, falloff, out) {
|
|
|
574
583
|
out.iesAxes = iesAxes;
|
|
575
584
|
out.cookies = cookies;
|
|
576
585
|
packAxesAndChannels(iesAxes, lights.lightChannels, out.axesAndChannels);
|
|
586
|
+
packConesAndFalloffs(coneCos, lights.lightFalloffExponents, out.conesAndFalloffs);
|
|
577
587
|
return out;
|
|
578
588
|
}
|
|
589
|
+
/** The cone and the falloff exponent, four a light, as `uLightCone` holds them. */
|
|
590
|
+
function packConesAndFalloffs(cones, exponents, out) {
|
|
591
|
+
const named = (exponents?.length ?? 0) >= MAX_POINT_LIGHTS ? exponents : undefined;
|
|
592
|
+
for (let light = 0; light < MAX_POINT_LIGHTS; light++) {
|
|
593
|
+
out[light * 4] = cones[light * 2] ?? POINT_LIGHT_COS_INNER;
|
|
594
|
+
out[light * 4 + 1] = cones[light * 2 + 1] ?? POINT_LIGHT_COS_OUTER;
|
|
595
|
+
out[light * 4 + 2] = lightFalloffExponentOf(named?.[light]);
|
|
596
|
+
out[light * 4 + 3] = 0;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
579
599
|
/** The azimuth and the channels, four a light, as `uLightIesAxis` holds them. */
|
|
580
600
|
function packAxesAndChannels(axes, channels, out) {
|
|
581
601
|
const named = (channels?.length ?? 0) >= MAX_POINT_LIGHTS ? channels : undefined;
|
|
@@ -35,10 +35,12 @@ export interface LightmapPage {
|
|
|
35
35
|
readonly width: number;
|
|
36
36
|
readonly height: number;
|
|
37
37
|
/**
|
|
38
|
-
* Linear irradiance,
|
|
39
|
-
*
|
|
38
|
+
* Linear irradiance, row-major from the top, in the same units every other light here is in — a
|
|
39
|
+
* bake from another engine scaled by that engine's exposure, as its lights are. Either three
|
|
40
|
+
* floats a texel, or one word a texel already packed as rgb9e5 (`rgb9e5.ts`), which is how such a
|
|
41
|
+
* bake usually arrives and which uploads with no conversion at all.
|
|
40
42
|
*/
|
|
41
|
-
readonly irradiance: Float32Array;
|
|
43
|
+
readonly irradiance: Float32Array | Uint32Array;
|
|
42
44
|
/**
|
|
43
45
|
* The direction the light arrives from, four bytes a texel: a first-order spherical harmonic in
|
|
44
46
|
* this engine's world axes, each value stored as `v * 0.5 + 0.5` — xyz the axis, w the constant.
|
|
@@ -54,20 +56,32 @@ export declare const LIGHTMAP_LAYERS = 2;
|
|
|
54
56
|
export type LightmapRegion = readonly [number, number, number, number];
|
|
55
57
|
/** The whole page. */
|
|
56
58
|
export declare const WHOLE_PAGE: LightmapRegion;
|
|
57
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* A page as both backends upload it: two layers of four bytes a texel, row-major from the top. The
|
|
61
|
+
* first is the irradiance's rgb9e5 word, byte by byte from its lowest; the second is the direction.
|
|
62
|
+
*
|
|
63
|
+
* **Eight bytes a texel, and the shader filters it.** Half floats were sixteen: a stage of 49 pages
|
|
64
|
+
* and 21.8 million texels held 348 MB, where this holds 174. The price is that the irradiance cannot
|
|
65
|
+
* be filtered as it is stored — a bilinear blend of two words with different exponents is not the
|
|
66
|
+
* blend of the colours they hold — so the lit stage fetches a texel's four neighbours, decodes each
|
|
67
|
+
* and blends the colours (`modelBaked` in `shaders/flat/models.ts`): four fetches a layer where one
|
|
68
|
+
* filtered sample was, on lightmapped surfaces alone. **What would make it wrong** is every device
|
|
69
|
+
* a page reaches sampling `rgb9e5ufloat`, which WebGL2 does as `RGB9_E5` too; it would then be a
|
|
70
|
+
* texture of its own, filtered by the device, and a binding the lit stage has no room for.
|
|
71
|
+
*/
|
|
58
72
|
export interface LightmapTexels {
|
|
59
73
|
readonly kind: 'lightmap-texels';
|
|
60
74
|
readonly width: number;
|
|
61
75
|
readonly height: number;
|
|
62
|
-
|
|
63
|
-
readonly
|
|
76
|
+
/** Each layer's bytes, in layer order: the irradiance, then the direction. */
|
|
77
|
+
readonly layers: readonly Uint8Array[];
|
|
64
78
|
}
|
|
65
79
|
/** Whether a surface texture's source is a page's texels rather than an image or blocks. */
|
|
66
80
|
export declare function isLightmapTexels(source: unknown): source is LightmapTexels;
|
|
67
81
|
/**
|
|
68
|
-
* A page as both backends upload it
|
|
69
|
-
*
|
|
70
|
-
*
|
|
82
|
+
* A page as both backends upload it (see `LightmapTexels`). Refuses a page whose arrays are not its
|
|
83
|
+
* size, naming the one that is short. Packed irradiance is read where it lies, with no copy; float
|
|
84
|
+
* irradiance is packed once, here.
|
|
71
85
|
*/
|
|
72
86
|
export declare function lightmapTexels(page: LightmapPage): LightmapTexels;
|
|
73
87
|
/**
|
package/dist/render/lightmap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { packRgb9e5 } from './rgb9e5.js';
|
|
2
2
|
/** Layers a page is uploaded as: the irradiance, then the direction. */
|
|
3
3
|
export const LIGHTMAP_LAYERS = 2;
|
|
4
4
|
/** The whole page. */
|
|
@@ -10,9 +10,9 @@ export function isLightmapTexels(source) {
|
|
|
10
10
|
source.kind === 'lightmap-texels');
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* A page as both backends upload it
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* A page as both backends upload it (see `LightmapTexels`). Refuses a page whose arrays are not its
|
|
14
|
+
* size, naming the one that is short. Packed irradiance is read where it lies, with no copy; float
|
|
15
|
+
* irradiance is packed once, here.
|
|
16
16
|
*/
|
|
17
17
|
export function lightmapTexels(page) {
|
|
18
18
|
const { width, height } = page;
|
|
@@ -20,32 +20,54 @@ export function lightmapTexels(page) {
|
|
|
20
20
|
throw new Error(`createLightmap: a ${width} by ${height} page has nothing in it`);
|
|
21
21
|
}
|
|
22
22
|
const texels = width * height;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const irradiance = page.irradiance;
|
|
24
|
+
const packed = irradiance instanceof Uint32Array;
|
|
25
|
+
const wanted = packed ? texels : texels * 3;
|
|
26
|
+
if (irradiance.length !== wanted) {
|
|
27
|
+
throw new Error(`createLightmap: ${irradiance.length} irradiance ${packed ? 'words' : 'floats'} for a ` +
|
|
28
|
+
`${width} by ${height} page, which wants ${wanted}: ` +
|
|
29
|
+
(packed ? 'one rgb9e5 word a texel' : 'three a texel'));
|
|
26
30
|
}
|
|
27
31
|
if (page.direction.length !== texels * 4) {
|
|
28
32
|
throw new Error(`createLightmap: ${page.direction.length} direction bytes for a ${width} by ${height} ` +
|
|
29
33
|
`page, which wants ${texels * 4}: four a texel`);
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
let words;
|
|
36
|
+
if (packed) {
|
|
37
|
+
words = irradiance;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
words = new Uint32Array(texels);
|
|
41
|
+
for (let t = 0; t < texels; t++) {
|
|
42
|
+
words[t] = packRgb9e5(irradiance[t * 3], irradiance[t * 3 + 1], irradiance[t * 3 + 2]);
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
return {
|
|
42
46
|
kind: 'lightmap-texels',
|
|
43
47
|
width,
|
|
44
48
|
height,
|
|
45
|
-
layers:
|
|
46
|
-
texels: toHalfFloats(floats),
|
|
49
|
+
layers: [littleEndianBytes(words), page.direction],
|
|
47
50
|
};
|
|
48
51
|
}
|
|
52
|
+
/** Whether this platform lays a word out lowest byte first, which the shader's decode assumes. */
|
|
53
|
+
const LITTLE_ENDIAN = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
54
|
+
/**
|
|
55
|
+
* A page's words as bytes, lowest first: a view of the same memory on every little-endian platform,
|
|
56
|
+
* which is every one a browser runs on, and a reordered copy anywhere else.
|
|
57
|
+
*/
|
|
58
|
+
function littleEndianBytes(words) {
|
|
59
|
+
const bytes = new Uint8Array(words.buffer, words.byteOffset, words.length * 4);
|
|
60
|
+
if (LITTLE_ENDIAN)
|
|
61
|
+
return bytes;
|
|
62
|
+
const swapped = new Uint8Array(bytes.length);
|
|
63
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
64
|
+
swapped[i] = bytes[i + 3];
|
|
65
|
+
swapped[i + 1] = bytes[i + 2];
|
|
66
|
+
swapped[i + 2] = bytes[i + 1];
|
|
67
|
+
swapped[i + 3] = bytes[i];
|
|
68
|
+
}
|
|
69
|
+
return swapped;
|
|
70
|
+
}
|
|
49
71
|
/**
|
|
50
72
|
* A mesh with its second coordinates moved onto the grain and relief streams they ride, or the
|
|
51
73
|
* mesh itself where it has none. Called by both backends' `createMesh`, before anything records
|
|
@@ -50,6 +50,27 @@ export interface ParticleInstances {
|
|
|
50
50
|
count: number;
|
|
51
51
|
capacity: number;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Floats one particle takes in a buffer a caller's own compute writes (`DeviceParticles`), in this
|
|
55
|
+
* order: position 3, half-width 1, roll 1, colour 3, opacity 1, age 1, seed 1, velocity 3, a
|
|
56
|
+
* sprite's frame 1 and its half-height 1 — `ParticleInstances`' streams, interleaved as the pool's
|
|
57
|
+
* own upload interleaves them. 64 bytes a particle.
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEVICE_PARTICLE_FLOATS = 16;
|
|
60
|
+
/**
|
|
61
|
+
* Particles a caller's own compute shader wrote into a buffer of its own (`registerCompute`), drawn
|
|
62
|
+
* where they lie by `drawDeviceParticles` rather than read back and uploaded again. WebGPU only.
|
|
63
|
+
*
|
|
64
|
+
* **Not sorted**, as a pool's are where its batch asks: the device holds them, so an alpha-blended
|
|
65
|
+
* set that must be drawn far to near sorts itself. A slot with a half-width of 0 draws nothing, which
|
|
66
|
+
* is how a fixed-size buffer holds fewer live particles than it has room for.
|
|
67
|
+
*/
|
|
68
|
+
export interface DeviceParticles {
|
|
69
|
+
/** A buffer with `VERTEX` usage, `DEVICE_PARTICLE_FLOATS` floats a particle. */
|
|
70
|
+
readonly buffer: GPUBuffer;
|
|
71
|
+
/** How many particles from its start to draw; no more than the buffer holds. */
|
|
72
|
+
readonly count: number;
|
|
73
|
+
}
|
|
53
74
|
/**
|
|
54
75
|
* A pool of short-lived particles, simulated on the CPU and drawn as instances.
|
|
55
76
|
*
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { createInstanceData, writeInstance } from './instancedMesh.js';
|
|
2
|
+
/**
|
|
3
|
+
* Floats one particle takes in a buffer a caller's own compute writes (`DeviceParticles`), in this
|
|
4
|
+
* order: position 3, half-width 1, roll 1, colour 3, opacity 1, age 1, seed 1, velocity 3, a
|
|
5
|
+
* sprite's frame 1 and its half-height 1 — `ParticleInstances`' streams, interleaved as the pool's
|
|
6
|
+
* own upload interleaves them. 64 bytes a particle.
|
|
7
|
+
*/
|
|
8
|
+
export const DEVICE_PARTICLE_FLOATS = 16;
|
|
2
9
|
function createParticleInstances(capacity) {
|
|
3
10
|
return {
|
|
4
11
|
positions: new Float32Array(capacity * 3),
|
package/dist/render/pass.d.ts
CHANGED
|
@@ -117,6 +117,19 @@ export type PassContext = {
|
|
|
117
117
|
} & ({
|
|
118
118
|
readonly backend: 'webgl2';
|
|
119
119
|
readonly gl: WebGL2RenderingContext;
|
|
120
|
+
/**
|
|
121
|
+
* The frame's colour with everything drawn before this pass, for a pass declaring
|
|
122
|
+
* `reads: ['colorSnapshot']`, and null for one that does not or where the frame keeps no
|
|
123
|
+
* scene target (`screenEffects` off). A copy, never the attachment being drawn into, which
|
|
124
|
+
* WebGL2 cannot sample; taken afresh at each such pass's draw. WebGPU hands the same copy
|
|
125
|
+
* over in `PrepareContext.sceneColor`, where a bind group has to be built before the pass.
|
|
126
|
+
*/
|
|
127
|
+
readonly sceneColor: WebGLTexture | null;
|
|
128
|
+
/**
|
|
129
|
+
* The frame's depth as it stands, for a pass declaring `reads: ['depthSnapshot']`: a copy, at
|
|
130
|
+
* the buffer's own values and reversed as it is (1 near, 0 far). Null as `sceneColor` is.
|
|
131
|
+
*/
|
|
132
|
+
readonly sceneDepth: WebGLTexture | null;
|
|
120
133
|
} | {
|
|
121
134
|
readonly backend: 'webgpu';
|
|
122
135
|
readonly pass: GPURenderPassEncoder;
|
|
@@ -213,6 +226,42 @@ export type PrepareContext = {
|
|
|
213
226
|
* should not apply it either; the renderer's own verbs do not jitter a mirror.
|
|
214
227
|
*/
|
|
215
228
|
readonly jitter: Float32Array;
|
|
229
|
+
/**
|
|
230
|
+
* The frame's single-sample copy of its depth, or null where the frame keeps none (no
|
|
231
|
+
* composite: `screenEffects` off).
|
|
232
|
+
*
|
|
233
|
+
* One `r32float` texel a pixel holding the depth buffer's own value, reversed as the
|
|
234
|
+
* buffer is (`REVERSED_DEPTH`: 1 at the near plane, 0 at the far one). Unfilterable, so a
|
|
235
|
+
* pass binds it as `'unfilterable-float'` and reads it with a non-filtering sampler or
|
|
236
|
+
* `textureLoad`.
|
|
237
|
+
*
|
|
238
|
+
* **Handed over here, filled at the draw.** The view is the one this frame's copy will be
|
|
239
|
+
* written into, so a pass builds its bind groups with it before the frame's pass opens; the
|
|
240
|
+
* copy itself is taken when a pass that declares `reads: ['depthSnapshot']` is drawn, with
|
|
241
|
+
* everything drawn before it, which is what the soft edge of a translucent particle
|
|
242
|
+
* needs. A pass that does not declare that read finds whatever the frame last wrote there.
|
|
243
|
+
*/
|
|
244
|
+
readonly sceneDepth: GPUTextureView | null;
|
|
245
|
+
/**
|
|
246
|
+
* The frame's colour, for a pass that declares `reads: ['colorSnapshot']`, or null where no
|
|
247
|
+
* registered pass does or the frame keeps no composite (`screenEffects` off).
|
|
248
|
+
*
|
|
249
|
+
* At the scene's own size and format — under a reconstruction, the render's rather than the
|
|
250
|
+
* output's, since that is the picture a registered pass draws into — and filterable.
|
|
251
|
+
* **Handed over here, refilled at the draw**: the view is the texture each reading pass's
|
|
252
|
+
* draw copies the frame into, with everything drawn before it, so a distortion samples the
|
|
253
|
+
* scene behind it and a full-screen effect drawn last reads the whole frame and writes over
|
|
254
|
+
* it, in scene light, before the tone curve. A copy a reading pass, where `sceneDepth` is one
|
|
255
|
+
* a frame.
|
|
256
|
+
*
|
|
257
|
+
* **Under a reconstruction a pass that reads it is drawn after the upscale**, over the
|
|
258
|
+
* reconstructed picture and after every blended draw, whatever order it was asked in — at the
|
|
259
|
+
* output's size and the reconstruction's format, which `PassContext.format` names at that
|
|
260
|
+
* draw, so a pass drawn there keeps a pipeline for it. The view is then the output-size copy.
|
|
261
|
+
* `sceneDepth` stays the render's opaque depth: at a ratio above 1 its texels are coarser by
|
|
262
|
+
* the ratio, and a blended draw is in neither depth, as it never is.
|
|
263
|
+
*/
|
|
264
|
+
readonly sceneColor: GPUTextureView | null;
|
|
216
265
|
};
|
|
217
266
|
/**
|
|
218
267
|
* The world's distance field on the device, as the two buffers and four numbers that address it.
|
|
@@ -69,6 +69,14 @@ export interface PointLightSource {
|
|
|
69
69
|
* whose uniform budget has none to spare.
|
|
70
70
|
*/
|
|
71
71
|
inLightField?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* How the light fades with distance when it names its own way: `(1 - (d/R)^2)^n` with no distance
|
|
74
|
+
* term, `R` its radius and `n` this. Unreal's falloff for a light without inverse-square falloff,
|
|
75
|
+
* so a rig authored there — a fill of brightness 160 across 164 m, a key at exponent 8 — lights
|
|
76
|
+
* the space it was authored for rather than one that ends a few metres out. Absent or 0, the
|
|
77
|
+
* frame's own falloff (`pointLightFalloff`), which is every light before 4.8.7.
|
|
78
|
+
*/
|
|
79
|
+
falloffExponent?: number;
|
|
72
80
|
}
|
|
73
81
|
export interface PointLightBuffer {
|
|
74
82
|
readonly positions: Float32Array;
|
|
@@ -126,6 +134,11 @@ export interface PointLightBuffer {
|
|
|
126
134
|
readonly directions: Float32Array;
|
|
127
135
|
/** Two per slot: the cosine of the inner cone angle, then of the outer. */
|
|
128
136
|
readonly coneCos: Float32Array;
|
|
137
|
+
/**
|
|
138
|
+
* One per slot: the light's own falloff exponent, or 0 for the frame's falloff. See
|
|
139
|
+
* `PointLightSource.falloffExponent`.
|
|
140
|
+
*/
|
|
141
|
+
readonly falloffExponents: Float32Array;
|
|
129
142
|
/** One per slot: a row of the photometric atlas, or −1 for a light with no profile. */
|
|
130
143
|
readonly iesProfiles: Float32Array;
|
|
131
144
|
/** Internal insertion-sort storage; exposed only to keep one flat object. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Allocation-free selection and packing of point lights for a fixed shader budget. */
|
|
2
|
-
import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER } from './clusteredLights.js';
|
|
2
|
+
import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER, lightFalloffExponentOf, } from './clusteredLights.js';
|
|
3
3
|
import { MAX_POINT_LIGHTS, POINT_SHADOW_POOL } from './lightBudget.js';
|
|
4
4
|
import { gatherLights } from './lightGrid.js';
|
|
5
5
|
export const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
|
|
@@ -150,6 +150,7 @@ export function createPointLightBuffer(capacity = MAX_POINT_LIGHTS) {
|
|
|
150
150
|
weights: new Float32Array(shaded),
|
|
151
151
|
directions: new Float32Array(shaded * 3),
|
|
152
152
|
coneCos: new Float32Array(shaded * 2),
|
|
153
|
+
falloffExponents: new Float32Array(shaded),
|
|
153
154
|
/* −1 rather than 0, because row 0 is a real fixture and a light that asked for none must not
|
|
154
155
|
be shaped by whichever profile happened to load first. */
|
|
155
156
|
iesProfiles: new Float32Array(shaded).fill(-1),
|
|
@@ -382,6 +383,7 @@ function choose(sources, visit, visitCount, x, y, z, out, timeSeconds, viewRange
|
|
|
382
383
|
* Restricting it to spots would be a smaller feature for no saving.
|
|
383
384
|
*/
|
|
384
385
|
out.iesProfiles[slot] = light.iesProfile ?? -1;
|
|
386
|
+
out.falloffExponents[slot] = lightFalloffExponentOf(light.falloffExponent);
|
|
385
387
|
}
|
|
386
388
|
out.count = chosen;
|
|
387
389
|
/*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare class ReflectionSurfaceTargets {
|
|
2
|
+
private readonly gl;
|
|
3
|
+
private probe;
|
|
4
|
+
private tint;
|
|
5
|
+
private width;
|
|
6
|
+
private height;
|
|
7
|
+
private readonly all;
|
|
8
|
+
private readonly colourAlone;
|
|
9
|
+
constructor(gl: WebGL2RenderingContext);
|
|
10
|
+
/** The environment as the frame shows it, and the roughness in alpha. */
|
|
11
|
+
get probeTexture(): WebGLTexture | null;
|
|
12
|
+
/** What a found reflection is multiplied by. */
|
|
13
|
+
get tintTexture(): WebGLTexture | null;
|
|
14
|
+
/**
|
|
15
|
+
* At the frame's start, with the scene's framebuffer bound: both at its size, attached, cleared to
|
|
16
|
+
* nothing reflected, and left disabled.
|
|
17
|
+
*/
|
|
18
|
+
begin(width: number, height: number): void;
|
|
19
|
+
/** Around an opaque lit draw into the frame: its two extra outputs land. */
|
|
20
|
+
on(): void;
|
|
21
|
+
/** And after it: only the colour does. */
|
|
22
|
+
off(): void;
|
|
23
|
+
/** With the scene's framebuffer bound, after the trace has read them: see the header. */
|
|
24
|
+
detach(): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
private make;
|
|
27
|
+
private release;
|
|
28
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's materials' reflection maps on WebGL2: two half-float textures on the scene's own
|
|
3
|
+
* framebuffer, beside its colour, written by the opaque lit draws as they are drawn. What the two
|
|
4
|
+
* hold is `shaders/flat/reflectionSurface.ts`'s to say; this is where they live on this backend.
|
|
5
|
+
*
|
|
6
|
+
* **Written in the frame's own pass, where WebGPU replays the draws in a second one.** A WebGL2
|
|
7
|
+
* program writing an output its framebuffer has no buffer enabled for loses that output and nothing
|
|
8
|
+
* else, so the maps are enabled around an opaque lit draw (`on`, `off`) and stay disabled for every
|
|
9
|
+
* other: the sky, glass, text and particles write no material, and a draw into a mirror, a probe or
|
|
10
|
+
* a capture is into another framebuffer, whose draw buffers this never touches.
|
|
11
|
+
*
|
|
12
|
+
* **Detached once the trace has read them** (`detach`), because the resolve then draws into the
|
|
13
|
+
* scene's framebuffer while sampling both, and a texture sampled while attached to the framebuffer
|
|
14
|
+
* being drawn is a feedback loop whether or not its buffer is enabled. `begin` attaches them again.
|
|
15
|
+
*/
|
|
16
|
+
const ZERO = new Float32Array(4);
|
|
17
|
+
export class ReflectionSurfaceTargets {
|
|
18
|
+
gl;
|
|
19
|
+
probe = null;
|
|
20
|
+
tint = null;
|
|
21
|
+
width = 0;
|
|
22
|
+
height = 0;
|
|
23
|
+
all;
|
|
24
|
+
colourAlone;
|
|
25
|
+
constructor(gl) {
|
|
26
|
+
this.gl = gl;
|
|
27
|
+
this.all = [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2];
|
|
28
|
+
this.colourAlone = [gl.COLOR_ATTACHMENT0];
|
|
29
|
+
}
|
|
30
|
+
/** The environment as the frame shows it, and the roughness in alpha. */
|
|
31
|
+
get probeTexture() {
|
|
32
|
+
return this.probe;
|
|
33
|
+
}
|
|
34
|
+
/** What a found reflection is multiplied by. */
|
|
35
|
+
get tintTexture() {
|
|
36
|
+
return this.tint;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* At the frame's start, with the scene's framebuffer bound: both at its size, attached, cleared to
|
|
40
|
+
* nothing reflected, and left disabled.
|
|
41
|
+
*/
|
|
42
|
+
begin(width, height) {
|
|
43
|
+
const { gl } = this;
|
|
44
|
+
if (this.probe === null || this.width !== width || this.height !== height) {
|
|
45
|
+
this.release();
|
|
46
|
+
this.probe = this.make(width, height);
|
|
47
|
+
this.tint = this.make(width, height);
|
|
48
|
+
this.width = width;
|
|
49
|
+
this.height = height;
|
|
50
|
+
}
|
|
51
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.probe, 0);
|
|
52
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, this.tint, 0);
|
|
53
|
+
gl.drawBuffers(this.all);
|
|
54
|
+
gl.clearBufferfv(gl.COLOR, 1, ZERO);
|
|
55
|
+
gl.clearBufferfv(gl.COLOR, 2, ZERO);
|
|
56
|
+
gl.drawBuffers(this.colourAlone);
|
|
57
|
+
}
|
|
58
|
+
/** Around an opaque lit draw into the frame: its two extra outputs land. */
|
|
59
|
+
on() {
|
|
60
|
+
this.gl.drawBuffers(this.all);
|
|
61
|
+
}
|
|
62
|
+
/** And after it: only the colour does. */
|
|
63
|
+
off() {
|
|
64
|
+
this.gl.drawBuffers(this.colourAlone);
|
|
65
|
+
}
|
|
66
|
+
/** With the scene's framebuffer bound, after the trace has read them: see the header. */
|
|
67
|
+
detach() {
|
|
68
|
+
const { gl } = this;
|
|
69
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, null, 0);
|
|
70
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, null, 0);
|
|
71
|
+
}
|
|
72
|
+
dispose() {
|
|
73
|
+
this.release();
|
|
74
|
+
}
|
|
75
|
+
make(width, height) {
|
|
76
|
+
const { gl } = this;
|
|
77
|
+
const texture = gl.createTexture();
|
|
78
|
+
if (texture === null)
|
|
79
|
+
throw new Error('ReflectionSurfaceTargets: createTexture failed');
|
|
80
|
+
/* Unit 0, chosen rather than inherited, for the reason `SsrPass` gives. */
|
|
81
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
82
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
83
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA16F, width, height);
|
|
84
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
85
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
86
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
87
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
88
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
89
|
+
return texture;
|
|
90
|
+
}
|
|
91
|
+
release() {
|
|
92
|
+
if (this.probe !== null)
|
|
93
|
+
this.gl.deleteTexture(this.probe);
|
|
94
|
+
if (this.tint !== null)
|
|
95
|
+
this.gl.deleteTexture(this.tint);
|
|
96
|
+
this.probe = null;
|
|
97
|
+
this.tint = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Construction-time GPU quality controls shared by every renderer resource. */
|
|
2
|
+
import type { FrameReflections } from './frameReflections.ts';
|
|
2
3
|
/**
|
|
3
4
|
* What happens to a shaded colour on its way to the framebuffer.
|
|
4
5
|
*
|
|
@@ -20,8 +21,14 @@
|
|
|
20
21
|
* red where `srgb` clips each channel and turns its core pink and wide. It is the curve a ported
|
|
21
22
|
* game's own resolve applied — for a port that has to match one — and it has none of ACES's toe or
|
|
22
23
|
* desaturation. See `HIGHLIGHT_SHOULDER_GLSL` for the arithmetic.
|
|
24
|
+
*
|
|
25
|
+
* `filmic` is a film curve shaped by five numbers — a slope, a toe, a shoulder and how far black
|
|
26
|
+
* and white clip — that holds a mid grey of 0.18 at 0.18 where `aces` gives about 0.11, so a look
|
|
27
|
+
* graded against such a curve is matched by its numbers (`setFilmicCurve`) rather than by a lookup
|
|
28
|
+
* table. Applied by the composite alone, so it needs `screenEffects` and `hdrScene`; without them
|
|
29
|
+
* every pass grades with `aces` and the renderer says so once. See `filmicCurve.ts`.
|
|
23
30
|
*/
|
|
24
|
-
export type OutputTransform = 'none' | 'srgb' | 'aces' | 'shoulder';
|
|
31
|
+
export type OutputTransform = 'none' | 'srgb' | 'aces' | 'shoulder' | 'filmic';
|
|
25
32
|
/**
|
|
26
33
|
* The three settings whose accepted values are a closed set, written as one.
|
|
27
34
|
*
|
|
@@ -209,13 +216,14 @@ export interface RenderQuality {
|
|
|
209
216
|
* **`'pre-integrated'`, the default**: the lit stage's fit alone — a terminator softened and
|
|
210
217
|
* reddened by the surface's curvature, light through thin parts, a penumbra each channel crosses
|
|
211
218
|
* as far as it travels — at no cost beyond the skin pipeline itself. **`'screen-space'`** adds
|
|
212
|
-
* Burley's blur in the picture as well: a skin draw writes its diffuse
|
|
213
|
-
* is drawn
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
+
* Burley's blur in the picture as well: a skin draw writes the light its diffuse is made of to a
|
|
220
|
+
* target of its own and its colour to another (it is drawn three times to do so), and the light is
|
|
221
|
+
* spread across the pixels its scatter distance covers, multiplied by the colour and added back
|
|
222
|
+
* before anything blended is drawn, so light crosses a shadow's edge and a nostril's rim in the
|
|
223
|
+
* picture rather than in a fit. See `skinBlur.ts`.
|
|
224
|
+
*
|
|
225
|
+
* Construction-time: three half-float targets the size of the frame, and two multisampled ones
|
|
226
|
+
* under multisampling, made at the first skin draw. It needs the composite (`screenEffects`), which is
|
|
219
227
|
* where the frame is still linear light to add to; without one, skin is pre-integrated and the
|
|
220
228
|
* renderer says so once. What would make the default wrong is a close-up face that reads as wax
|
|
221
229
|
* at a shadow's edge — that is what the blur is for.
|
|
@@ -344,6 +352,18 @@ export interface RenderQuality {
|
|
|
344
352
|
* eight-bit and this buys only the consistent grading.
|
|
345
353
|
*/
|
|
346
354
|
readonly hdrScene: boolean;
|
|
355
|
+
/**
|
|
356
|
+
* Ask for a high dynamic range display: a canvas whose highlights run past paper white to as
|
|
357
|
+
* far as the display allows, where it reports one. Off by default.
|
|
358
|
+
*
|
|
359
|
+
* **Asked for, not assumed.** It needs WebGPU, a display reporting a high dynamic range, and a
|
|
360
|
+
* composite that holds the light to give it (`screenEffects` and `hdrScene`); where any of those
|
|
361
|
+
* is missing the frame goes out as it always did and the renderer says which in
|
|
362
|
+
* `displayRangeReason`. `displayRange` is the answer a game reads to choose a look graded for the
|
|
363
|
+
* output it got. `setDisplayLuminance` sets how far above paper white the highlights may reach.
|
|
364
|
+
* WebGL2 has no extended-range canvas to draw into, so there it is always the standard range.
|
|
365
|
+
*/
|
|
366
|
+
readonly highDynamicRange: boolean;
|
|
347
367
|
/** Camera-depth water tint and scattering across every visible pass. */
|
|
348
368
|
readonly underwaterAtmosphere: boolean;
|
|
349
369
|
/** Procedural noise octaves evaluated by plume shaders. */
|
|
@@ -511,14 +531,14 @@ export interface RenderQuality {
|
|
|
511
531
|
*/
|
|
512
532
|
readonly sceneSamples: number;
|
|
513
533
|
/**
|
|
514
|
-
*
|
|
534
|
+
* Motion blur, 0 to 1. Off by default.
|
|
515
535
|
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* needs only the previous view-projection,
|
|
521
|
-
*
|
|
536
|
+
* By the camera's motion everywhere, and **by each drawn object's own motion where a
|
|
537
|
+
* reconstruction draws one** (DriftTR, WebGPU): a mover's pixels smear along its path while a
|
|
538
|
+
* still stage stays sharp. Elsewhere a velocity buffer would be every mesh writing its motion into
|
|
539
|
+
* a second target — a structural change to a forward renderer — so those frames blur by the
|
|
540
|
+
* camera alone, which needs only the previous view-projection, and say so once when a draw names
|
|
541
|
+
* where it was. `setCameraMotionBlur` is the per-frame dial and the longest smear.
|
|
522
542
|
*
|
|
523
543
|
* Runs inside the composite pass, so it costs a depth sample and eight taps on the pixels
|
|
524
544
|
* that are moving and nothing on a still frame. Needs `screenEffects`, since without the
|
|
@@ -569,6 +589,18 @@ export interface RenderQuality {
|
|
|
569
589
|
* Needs `screenEffects`, since the resolve composites over the finished scene.
|
|
570
590
|
*/
|
|
571
591
|
readonly orderIndependent: boolean;
|
|
592
|
+
/**
|
|
593
|
+
* Every opaque lit surface reflects the frame by its own material: a ray marched against the
|
|
594
|
+
* frame's depth swaps the probes' share of each pixel for what it finds, at the pixel's own
|
|
595
|
+
* roughness and reflectance, and a surface past `maxRoughness` keeps the probes alone. Off by
|
|
596
|
+
* default; `true` for the defaults, or the numbers. See `frameReflections.ts`.
|
|
597
|
+
*
|
|
598
|
+
* Needs `screenEffects` and `hdrScene`, since the swap is made in scene light, and a frame of one
|
|
599
|
+
* sample; it says once where it cannot be drawn. What it costs on WebGPU is every opaque lit draw
|
|
600
|
+
* a second time without its lamps, into two half-float targets, and a march per reflective pixel;
|
|
601
|
+
* on WebGL2 the same two targets, written beside the colour by the opaque lit draws themselves.
|
|
602
|
+
*/
|
|
603
|
+
readonly screenSpaceReflections: boolean | FrameReflections;
|
|
572
604
|
/**
|
|
573
605
|
* Depth of field: how wide the blur may reach, as a fraction of the frame's height. Off by
|
|
574
606
|
* default.
|
|
@@ -85,9 +85,11 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
85
85
|
sceneSamples: 1,
|
|
86
86
|
planarReflections: false,
|
|
87
87
|
hdrScene: false,
|
|
88
|
+
highDynamicRange: false,
|
|
88
89
|
cameraMotionBlur: 0,
|
|
89
90
|
temporalAa: false,
|
|
90
91
|
orderIndependent: false,
|
|
92
|
+
screenSpaceReflections: false,
|
|
91
93
|
depthOfField: 0,
|
|
92
94
|
reconstruction: 0,
|
|
93
95
|
indirectLight: false,
|
|
@@ -227,9 +229,11 @@ export function resolveRenderQuality(options = {}) {
|
|
|
227
229
|
sceneSamples: Math.max(1, Math.round(options.sceneSamples ?? DEFAULT_RENDER_QUALITY.sceneSamples)),
|
|
228
230
|
planarReflections: options.planarReflections ?? DEFAULT_RENDER_QUALITY.planarReflections,
|
|
229
231
|
hdrScene: options.hdrScene ?? DEFAULT_RENDER_QUALITY.hdrScene,
|
|
232
|
+
highDynamicRange: options.highDynamicRange ?? DEFAULT_RENDER_QUALITY.highDynamicRange,
|
|
230
233
|
cameraMotionBlur: Math.min(1, Math.max(0, options.cameraMotionBlur ?? DEFAULT_RENDER_QUALITY.cameraMotionBlur)),
|
|
231
234
|
temporalAa: options.temporalAa ?? DEFAULT_RENDER_QUALITY.temporalAa,
|
|
232
235
|
orderIndependent: options.orderIndependent ?? DEFAULT_RENDER_QUALITY.orderIndependent,
|
|
236
|
+
screenSpaceReflections: options.screenSpaceReflections ?? DEFAULT_RENDER_QUALITY.screenSpaceReflections,
|
|
233
237
|
/* Off, or a ratio at least 1.3 and at most 2 — never a number between zero and the range. */
|
|
234
238
|
reconstruction: resolveReconstruction(options.reconstruction),
|
|
235
239
|
indirectLight: options.indirectLight ?? DEFAULT_RENDER_QUALITY.indirectLight,
|
|
@@ -76,6 +76,9 @@ export function recordingGl(options = {}) {
|
|
|
76
76
|
ONE: 1,
|
|
77
77
|
ONE_MINUS_SRC_ALPHA: 0x0303,
|
|
78
78
|
TRIANGLES: 0x0004,
|
|
79
|
+
/* The winding a pass names, so a test can tell which one it chose. */
|
|
80
|
+
CW: 0x0900,
|
|
81
|
+
CCW: 0x0901,
|
|
79
82
|
TEXTURE0: 0x84c0,
|
|
80
83
|
DEPTH_BUFFER_BIT: 0x00000100,
|
|
81
84
|
DEPTH_TEST: 0x0b71,
|