@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
package/dist/render/ssrPass.js
CHANGED
|
@@ -3,7 +3,7 @@ import { decalScissor } from './decalProjector.js';
|
|
|
3
3
|
import { DEPTH_01_TO_CLIP } from './lightVolumeDraw.js';
|
|
4
4
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
5
5
|
import { FULLSCREEN_VERT } from './shaders/fullscreen.js';
|
|
6
|
-
import { SSR_RESOLVE_FRAG } from './shaders/ssrResolve.js';
|
|
6
|
+
import { SSR_MATERIAL_RESOLVE_FRAG, SSR_RESOLVE_FRAG } from './shaders/ssrResolve.js';
|
|
7
7
|
import { SSR_TRACE_FRAG } from './shaders/ssrTrace.js';
|
|
8
8
|
import { blendKeeping } from './sceneCoverage.js';
|
|
9
9
|
/**
|
|
@@ -32,6 +32,9 @@ export class SsrPass {
|
|
|
32
32
|
width = 0;
|
|
33
33
|
height = 0;
|
|
34
34
|
/** `inverse(viewProjection) * DEPTH_01_TO_CLIP`, rebuilt once a frame rather than per surface. */
|
|
35
|
+
/** The frame's materials' resolve, compiled the first time a frame traces them. */
|
|
36
|
+
materialResolve = null;
|
|
37
|
+
materialUniforms = {};
|
|
35
38
|
depthToWorld = new Float32Array(16);
|
|
36
39
|
scissor = new Int32Array(4);
|
|
37
40
|
constructor(gl,
|
|
@@ -118,6 +121,11 @@ export class SsrPass {
|
|
|
118
121
|
gl.activeTexture(gl.TEXTURE1);
|
|
119
122
|
gl.bindTexture(gl.TEXTURE_2D, scene);
|
|
120
123
|
gl.uniform1i(u['uSsrScene'] ?? null, 1);
|
|
124
|
+
/* The materials' maps unread in this mode, and pointed at a bound texture all the same: a
|
|
125
|
+
driver may fetch a sampler's descriptor before the branch that skips it. */
|
|
126
|
+
gl.uniform1i(u['uSsrProbeMap'] ?? null, 0);
|
|
127
|
+
gl.uniform1i(u['uSsrTintMap'] ?? null, 0);
|
|
128
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 0);
|
|
121
129
|
gl.uniformMatrix4fv(u['uSsrDepthToWorld'] ?? null, false, this.depthToWorld);
|
|
122
130
|
/* No Y flip on this backend: `gl.scissor` and the framebuffer both count rows from the bottom,
|
|
123
131
|
which is the space the raw matrix already emits. See `CLIP_Y_FLIP`. */
|
|
@@ -133,6 +141,7 @@ export class SsrPass {
|
|
|
133
141
|
gl.uniform3fv(u['uSsrAxis'] ?? null, surface.axis);
|
|
134
142
|
gl.uniform3fv(u['uSsrTint'] ?? null, surface.tint);
|
|
135
143
|
gl.uniform1f(u['uSsrStrength'] ?? null, surface.strength);
|
|
144
|
+
gl.uniform2f(u['uSsrFresnel'] ?? null, surface.fresnel ? 1 : 0, surface.roughness);
|
|
136
145
|
gl.uniform1f(u['uSsrFacingCos'] ?? null, surface.facingCos);
|
|
137
146
|
gl.uniform1f(u['uSsrReach'] ?? null, surface.reachM);
|
|
138
147
|
gl.uniform1f(u['uSsrThickness'] ?? null, surface.thicknessM);
|
|
@@ -152,6 +161,100 @@ export class SsrPass {
|
|
|
152
161
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
153
162
|
}
|
|
154
163
|
/** Composite what the trace found over whatever framebuffer is bound. */
|
|
164
|
+
/**
|
|
165
|
+
* Every pixel the lit stage left a reflection for, marched against the frame's depth by the
|
|
166
|
+
* trace's material mode, into this pass's own target; `resolveMaterials` then swaps the probe's
|
|
167
|
+
* share for it. See `frameReflections.ts`. Ends on the default framebuffer, as the surfaces' does.
|
|
168
|
+
*/
|
|
169
|
+
traceMaterials(scene, depth, probe, tint, viewProjection, eye, width, height, edgeFade, reflections) {
|
|
170
|
+
const { gl } = this;
|
|
171
|
+
const u = this.traceUniforms;
|
|
172
|
+
this.ensureSize(width, height);
|
|
173
|
+
mat4.invert(this.depthToWorld, viewProjection);
|
|
174
|
+
mat4.multiply(this.depthToWorld, this.depthToWorld, DEPTH_01_TO_CLIP);
|
|
175
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
176
|
+
gl.viewport(0, 0, width, height);
|
|
177
|
+
gl.disable(gl.DEPTH_TEST);
|
|
178
|
+
gl.depthMask(false);
|
|
179
|
+
gl.disable(gl.CULL_FACE);
|
|
180
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
181
|
+
gl.disable(gl.BLEND);
|
|
182
|
+
gl.clearBufferfv(gl.COLOR, 0, [0, 0, 0, 0]);
|
|
183
|
+
gl.useProgram(this.trace);
|
|
184
|
+
gl.bindVertexArray(this.vao);
|
|
185
|
+
const textures = [depth, scene, probe, tint];
|
|
186
|
+
const names = ['uSsrDepth', 'uSsrScene', 'uSsrProbeMap', 'uSsrTintMap'];
|
|
187
|
+
for (let unit = 0; unit < 4; unit++) {
|
|
188
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
189
|
+
gl.bindTexture(gl.TEXTURE_2D, textures[unit] ?? null);
|
|
190
|
+
gl.uniform1i(u[names[unit] ?? ''] ?? null, unit);
|
|
191
|
+
}
|
|
192
|
+
gl.uniformMatrix4fv(u['uSsrDepthToWorld'] ?? null, false, this.depthToWorld);
|
|
193
|
+
gl.uniformMatrix4fv(u['uSsrViewProj'] ?? null, false, viewProjection);
|
|
194
|
+
gl.uniform3f(u['uSsrEye'] ?? null, eye[0] ?? 0, eye[1] ?? 0, eye[2] ?? 0);
|
|
195
|
+
gl.uniform1f(u['uSsrEdgeFade'] ?? null, edgeFade);
|
|
196
|
+
gl.uniform3f(u['uSsrAxis'] ?? null, 0, 0, 0);
|
|
197
|
+
gl.uniform3f(u['uSsrTint'] ?? null, 1, 1, 1);
|
|
198
|
+
gl.uniform1f(u['uSsrStrength'] ?? null, 1);
|
|
199
|
+
gl.uniform2f(u['uSsrFresnel'] ?? null, 0, 0);
|
|
200
|
+
gl.uniform1f(u['uSsrFacingCos'] ?? null, -1);
|
|
201
|
+
gl.uniform1f(u['uSsrReach'] ?? null, reflections.reachM);
|
|
202
|
+
gl.uniform1f(u['uSsrThickness'] ?? null, reflections.thicknessM);
|
|
203
|
+
gl.uniform1f(u['uSsrSteps'] ?? null, reflections.steps);
|
|
204
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 1);
|
|
205
|
+
gl.uniform1f(u['uSsrMaxRoughness'] ?? null, reflections.maxRoughness);
|
|
206
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
207
|
+
/* Back to the surfaces' mode, which every other trace in the frame is. */
|
|
208
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 0);
|
|
209
|
+
gl.bindVertexArray(null);
|
|
210
|
+
for (let unit = 3; unit >= 0; unit--) {
|
|
211
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
212
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
213
|
+
}
|
|
214
|
+
gl.depthMask(true);
|
|
215
|
+
gl.enable(gl.DEPTH_TEST);
|
|
216
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The probe's share of each pixel swapped for what `traceMaterials` found, blurred by the
|
|
220
|
+
* surface's roughness, added onto whatever framebuffer is bound — the scene's, with the two maps
|
|
221
|
+
* already detached from it. See `SSR_MATERIAL_RESOLVE_FRAG`.
|
|
222
|
+
*/
|
|
223
|
+
resolveMaterials(probe, tint, width, height, reflections) {
|
|
224
|
+
const { gl } = this;
|
|
225
|
+
if (this.reflection === null)
|
|
226
|
+
return;
|
|
227
|
+
if (this.materialResolve === null) {
|
|
228
|
+
this.materialResolve = compileProgram(gl, FULLSCREEN_VERT, SSR_MATERIAL_RESOLVE_FRAG, 'ssrMaterialResolve');
|
|
229
|
+
this.materialUniforms = uniformLocations(gl, this.materialResolve, 'ssrMaterialResolve');
|
|
230
|
+
}
|
|
231
|
+
const u = this.materialUniforms;
|
|
232
|
+
gl.disable(gl.DEPTH_TEST);
|
|
233
|
+
gl.depthMask(false);
|
|
234
|
+
gl.disable(gl.CULL_FACE);
|
|
235
|
+
gl.enable(gl.BLEND);
|
|
236
|
+
/* Added, a negative addend subtracting, and the scene's alpha kept. */
|
|
237
|
+
blendKeeping(gl, gl.ONE, gl.ONE);
|
|
238
|
+
gl.useProgram(this.materialResolve);
|
|
239
|
+
const textures = [this.reflection, probe, tint];
|
|
240
|
+
const names = ['uSsrReflection', 'uSsrProbeMap', 'uSsrTintMap'];
|
|
241
|
+
for (let unit = 0; unit < 3; unit++) {
|
|
242
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
243
|
+
gl.bindTexture(gl.TEXTURE_2D, textures[unit] ?? null);
|
|
244
|
+
gl.uniform1i(u[names[unit] ?? ''] ?? null, unit);
|
|
245
|
+
}
|
|
246
|
+
gl.uniform3f(u['uSsrBlur'] ?? null, 1 / Math.max(width, 1), 1 / Math.max(height, 1), reflections.blur * height);
|
|
247
|
+
gl.bindVertexArray(this.vao);
|
|
248
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
249
|
+
gl.bindVertexArray(null);
|
|
250
|
+
for (let unit = 2; unit >= 0; unit--) {
|
|
251
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
252
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
253
|
+
}
|
|
254
|
+
gl.disable(gl.BLEND);
|
|
255
|
+
gl.depthMask(true);
|
|
256
|
+
gl.enable(gl.DEPTH_TEST);
|
|
257
|
+
}
|
|
155
258
|
resolveReflections() {
|
|
156
259
|
const { gl } = this;
|
|
157
260
|
if (this.reflection === null)
|
|
@@ -188,6 +291,9 @@ export class SsrPass {
|
|
|
188
291
|
gl.deleteFramebuffer(this.framebuffer);
|
|
189
292
|
gl.deleteProgram(this.trace);
|
|
190
293
|
gl.deleteProgram(this.resolve);
|
|
294
|
+
if (this.materialResolve !== null)
|
|
295
|
+
gl.deleteProgram(this.materialResolve);
|
|
296
|
+
this.materialResolve = null;
|
|
191
297
|
gl.deleteVertexArray(this.vao);
|
|
192
298
|
this.reflection = null;
|
|
193
299
|
this.framebuffer = null;
|
|
@@ -50,7 +50,12 @@ export interface SkinModel {
|
|
|
50
50
|
readonly kind: 'skin';
|
|
51
51
|
/** The colour light keeps travelling beneath the surface — the mean free path, as a colour. */
|
|
52
52
|
readonly scatterColor: Vec3;
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Metres: how far light travels beneath it, on average, before it leaves — from where it entered
|
|
55
|
+
* to where it leaves, for a channel whose share of `scatterColor` is 1. Burley's profile, which
|
|
56
|
+
* the lit stage and the screen-space blur both scatter by, does that at a distance `d` two fifths
|
|
57
|
+
* of it, and carries a tail of light out to about three and a half times it.
|
|
58
|
+
*/
|
|
54
59
|
readonly radius: number;
|
|
55
60
|
/** 0 to 1: how much a light behind a thin part shows through it. */
|
|
56
61
|
readonly transmission: number;
|
|
@@ -104,7 +109,21 @@ export declare function hairModel(options?: {
|
|
|
104
109
|
scatter?: number;
|
|
105
110
|
backlit?: number;
|
|
106
111
|
}): HairModel;
|
|
107
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* How far Burley's normalised diffusion carries light on average, in units of its own distance
|
|
114
|
+
* `d`: its radial density `(e^(−r/d) + e^(−r/3d)) / 4d` has the mean `(d² + 9d²) / 4d = 2.5 d`. So a
|
|
115
|
+
* skin whose light travels `radius` scatters by a profile at `d = radius / 2.5`.
|
|
116
|
+
*
|
|
117
|
+
* **Until 4.8.7 `radius` was `d` itself**, so light travelled two and a half times as far as the
|
|
118
|
+
* model said, and the screen-space blur — whose taps reach nine `d` — spread a face at arm's length
|
|
119
|
+
* over sixty pixels: a nose, lips and brows washed into one blur where a centimetre covers a few.
|
|
120
|
+
* The lit stage's pre-integrated fit, transmission and penumbra take the same `d`, so the two paths
|
|
121
|
+
* still agree with each other.
|
|
122
|
+
*/
|
|
123
|
+
export declare const SKIN_MEAN_EXIT = 2.5;
|
|
124
|
+
/** Burley's distance `d` for one channel of a skin, in metres: see `SKIN_MEAN_EXIT`. */
|
|
125
|
+
export declare function skinProfileDistance(model: SkinModel, channel: 0 | 1 | 2): number;
|
|
126
|
+
/** Skin. The defaults are a fair skin's: red light leaving a little over a centimetre away. */
|
|
108
127
|
export declare function skinModel(options?: {
|
|
109
128
|
scatterColor?: Vec3;
|
|
110
129
|
radius?: number;
|
|
@@ -130,16 +149,18 @@ export declare function lightmapModel(options?: {
|
|
|
130
149
|
}): LightmapModel;
|
|
131
150
|
/**
|
|
132
151
|
* A model's numbers into `out` — eight floats, `uModelParams[0]` then `[1]` — and whether a model
|
|
133
|
-
* map is bound into the last.
|
|
134
|
-
*
|
|
152
|
+
* map is bound into the last. An eye's axis goes in with the descriptor's own until the draw turns
|
|
153
|
+
* it: see `eyeAxisInWorld`. `physical` is `SurfaceMaterial.physicalSpecular`, in the lane the
|
|
154
|
+
* models that read it leave free; skin, hair and the eye shade their own highlights and never do.
|
|
135
155
|
*
|
|
136
|
-
* -
|
|
156
|
+
* - standard (null): − · −, −, physical, −
|
|
157
|
+
* - anisotropic: strength, cos and sin of the rotation · −, −, physical, map
|
|
137
158
|
* - hair: shift, scatter, backlit, − · −, −, −, map
|
|
138
|
-
* - skin: the scatter colour, radius · transmission, profile, −, map
|
|
159
|
+
* - skin: the scatter colour, radius / `SKIN_MEAN_EXIT` · transmission, profile, −, map
|
|
139
160
|
* - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
|
|
140
|
-
* - lightmap: the region's scale and bias · −, −,
|
|
161
|
+
* - lightmap: the region's scale and bias · −, −, physical, map
|
|
141
162
|
*/
|
|
142
|
-
export declare function packModel(model: SurfaceModel | null, mapped: boolean, out: Float32Array): void;
|
|
163
|
+
export declare function packModel(model: SurfaceModel | null, mapped: boolean, out: Float32Array, physical?: boolean): void;
|
|
143
164
|
/**
|
|
144
165
|
* An eye's axis in the world for one draw, into `out[at…at+2]`: the descriptor's mesh-local axis
|
|
145
166
|
* turned by its joint's palette entry, where it names one and a palette is set, and then by the
|
|
@@ -46,7 +46,23 @@ export function hairModel(options = {}) {
|
|
|
46
46
|
backlit: check('hairModel', 'backlit', options.backlit ?? 1, 0, 4),
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* How far Burley's normalised diffusion carries light on average, in units of its own distance
|
|
51
|
+
* `d`: its radial density `(e^(−r/d) + e^(−r/3d)) / 4d` has the mean `(d² + 9d²) / 4d = 2.5 d`. So a
|
|
52
|
+
* skin whose light travels `radius` scatters by a profile at `d = radius / 2.5`.
|
|
53
|
+
*
|
|
54
|
+
* **Until 4.8.7 `radius` was `d` itself**, so light travelled two and a half times as far as the
|
|
55
|
+
* model said, and the screen-space blur — whose taps reach nine `d` — spread a face at arm's length
|
|
56
|
+
* over sixty pixels: a nose, lips and brows washed into one blur where a centimetre covers a few.
|
|
57
|
+
* The lit stage's pre-integrated fit, transmission and penumbra take the same `d`, so the two paths
|
|
58
|
+
* still agree with each other.
|
|
59
|
+
*/
|
|
60
|
+
export const SKIN_MEAN_EXIT = 2.5;
|
|
61
|
+
/** Burley's distance `d` for one channel of a skin, in metres: see `SKIN_MEAN_EXIT`. */
|
|
62
|
+
export function skinProfileDistance(model, channel) {
|
|
63
|
+
return (model.scatterColor[channel] * model.radius) / SKIN_MEAN_EXIT;
|
|
64
|
+
}
|
|
65
|
+
/** Skin. The defaults are a fair skin's: red light leaving a little over a centimetre away. */
|
|
50
66
|
export function skinModel(options = {}) {
|
|
51
67
|
const color = options.scatterColor ?? [0.85, 0.35, 0.22];
|
|
52
68
|
for (let c = 0; c < 3; c++)
|
|
@@ -92,25 +108,31 @@ export function lightmapModel(options = {}) {
|
|
|
92
108
|
}
|
|
93
109
|
/**
|
|
94
110
|
* A model's numbers into `out` — eight floats, `uModelParams[0]` then `[1]` — and whether a model
|
|
95
|
-
* map is bound into the last.
|
|
96
|
-
*
|
|
111
|
+
* map is bound into the last. An eye's axis goes in with the descriptor's own until the draw turns
|
|
112
|
+
* it: see `eyeAxisInWorld`. `physical` is `SurfaceMaterial.physicalSpecular`, in the lane the
|
|
113
|
+
* models that read it leave free; skin, hair and the eye shade their own highlights and never do.
|
|
97
114
|
*
|
|
98
|
-
* -
|
|
115
|
+
* - standard (null): − · −, −, physical, −
|
|
116
|
+
* - anisotropic: strength, cos and sin of the rotation · −, −, physical, map
|
|
99
117
|
* - hair: shift, scatter, backlit, − · −, −, −, map
|
|
100
|
-
* - skin: the scatter colour, radius · transmission, profile, −, map
|
|
118
|
+
* - skin: the scatter colour, radius / `SKIN_MEAN_EXIT` · transmission, profile, −, map
|
|
101
119
|
* - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
|
|
102
|
-
* - lightmap: the region's scale and bias · −, −,
|
|
120
|
+
* - lightmap: the region's scale and bias · −, −, physical, map
|
|
103
121
|
*/
|
|
104
|
-
export function packModel(model, mapped, out) {
|
|
122
|
+
export function packModel(model, mapped, out, physical = false) {
|
|
105
123
|
out.fill(0);
|
|
106
|
-
|
|
124
|
+
const highlight = physical ? 1 : 0;
|
|
125
|
+
if (model === null) {
|
|
126
|
+
out[6] = highlight;
|
|
107
127
|
return;
|
|
128
|
+
}
|
|
108
129
|
out[7] = mapped ? 1 : 0;
|
|
109
130
|
switch (model.kind) {
|
|
110
131
|
case 'anisotropic':
|
|
111
132
|
out[0] = model.strength;
|
|
112
133
|
out[1] = Math.cos(model.rotation);
|
|
113
134
|
out[2] = Math.sin(model.rotation);
|
|
135
|
+
out[6] = highlight;
|
|
114
136
|
return;
|
|
115
137
|
case 'hair':
|
|
116
138
|
out[0] = model.shift;
|
|
@@ -121,7 +143,8 @@ export function packModel(model, mapped, out) {
|
|
|
121
143
|
out[0] = model.scatterColor[0];
|
|
122
144
|
out[1] = model.scatterColor[1];
|
|
123
145
|
out[2] = model.scatterColor[2];
|
|
124
|
-
|
|
146
|
+
/* Burley's distance for a channel of share 1, which the shader scales by each share. */
|
|
147
|
+
out[3] = model.radius / SKIN_MEAN_EXIT;
|
|
125
148
|
out[4] = model.transmission;
|
|
126
149
|
out[5] = model.profile;
|
|
127
150
|
return;
|
|
@@ -139,6 +162,7 @@ export function packModel(model, mapped, out) {
|
|
|
139
162
|
out[1] = model.region[1];
|
|
140
163
|
out[2] = model.region[2];
|
|
141
164
|
out[3] = model.region[3];
|
|
165
|
+
out[6] = highlight;
|
|
142
166
|
return;
|
|
143
167
|
}
|
|
144
168
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a draw lays over its surface beyond its material: a rim of light, an emissive overlay that
|
|
3
|
+
* dissolves the surface away, and a second normal map blended in by region. Set per draw with
|
|
4
|
+
* `RendererApi.setSurfaceOverlay`, so one character's whole set of materials changes together
|
|
5
|
+
* while another's does not.
|
|
6
|
+
*
|
|
7
|
+
* **Per draw and held, like `setEnvironmentGain`**: set before a character's draws, cleared after,
|
|
8
|
+
* and every draw between them wears it. **Images come from one atlas** (`maps`), each named by the
|
|
9
|
+
* rectangle it occupies — a scale and an offset in the atlas's uv — because the lit stage binds every
|
|
10
|
+
* sampler a device is guaranteed and has none left for a texture of its own: the atlas is bound in
|
|
11
|
+
* the slot the frame's refraction copy takes, which an opaque surface never reads. A draw that both
|
|
12
|
+
* refracts and wears an overlay's images keeps its refraction and loses the images, said once.
|
|
13
|
+
*
|
|
14
|
+
* **Fifteen vectors of numbers**, in the material's uniform block. WebGPU has the room; WebGL2 has
|
|
15
|
+
* room where its device reports more than the lit stage's 254 vectors, and refuses the overlay in
|
|
16
|
+
* words where it does not. See `packSurfaceOverlay` for the layout.
|
|
17
|
+
*/
|
|
18
|
+
import type { Vec3 } from '../math/color.ts';
|
|
19
|
+
import type { SurfaceTextureHandle } from './backend/api.ts';
|
|
20
|
+
/** Where an image sits in the atlas: its size and its corner, each in the atlas's uv. */
|
|
21
|
+
export interface OverlayRegion {
|
|
22
|
+
readonly scale: readonly [number, number];
|
|
23
|
+
readonly offset: readonly [number, number];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Light along the silhouette: the edge `(1 - N·V)^falloff`, leaning toward normals that face up,
|
|
27
|
+
* raised to a contrast, times a noise laid in screen space, a slow pulse and a mask, in a colour.
|
|
28
|
+
* Added to what the surface emits, and not gated on the night as emission is: it is a glow the
|
|
29
|
+
* surface wears, not light it gives off.
|
|
30
|
+
*/
|
|
31
|
+
export interface SurfaceRim {
|
|
32
|
+
readonly colour: Vec3;
|
|
33
|
+
/** How bright, 0 for none. Multiplied with `alpha`. */
|
|
34
|
+
readonly intensity: number;
|
|
35
|
+
readonly alpha?: number;
|
|
36
|
+
/** The edge's exponent: larger keeps the rim nearer the silhouette. 1.5 unless given. */
|
|
37
|
+
readonly falloff?: number;
|
|
38
|
+
/**
|
|
39
|
+
* How far the rim leans toward normals that face up: the weight is `(N·up · ½ + ½)^upward`, so
|
|
40
|
+
* 0 is no lean. 0.4 unless given.
|
|
41
|
+
*/
|
|
42
|
+
readonly upward?: number;
|
|
43
|
+
/** The exponent the whole edge is raised to. 1 unless given. */
|
|
44
|
+
readonly contrast?: number;
|
|
45
|
+
/** A noise image laid in screen space and scrolled, the rim taken times it, clamped to 1. */
|
|
46
|
+
readonly noise?: {
|
|
47
|
+
readonly region: OverlayRegion;
|
|
48
|
+
/** Screens a second, along x and y. */
|
|
49
|
+
readonly scroll?: readonly [number, number];
|
|
50
|
+
/** Repeats across the screen's height. 1 unless given. */
|
|
51
|
+
readonly tiling?: number;
|
|
52
|
+
};
|
|
53
|
+
/** A pulse `½ + ½ sin(rate · t)`, held between `low` and `high`, on `Environment.surfaceTime`. */
|
|
54
|
+
readonly pulse?: {
|
|
55
|
+
readonly rate: number;
|
|
56
|
+
readonly low: number;
|
|
57
|
+
readonly high: number;
|
|
58
|
+
};
|
|
59
|
+
/** A mask in the mesh's uv where the rim is erased: the rim times `1 - weight · mask`. */
|
|
60
|
+
readonly mask?: {
|
|
61
|
+
readonly region: OverlayRegion;
|
|
62
|
+
readonly weight: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The surface cut away where a noise in its uv falls under `threshold`, with a glowing band just
|
|
67
|
+
* above it, and a colour laid over the surface through the same noise.
|
|
68
|
+
*/
|
|
69
|
+
export interface SurfaceDissolve {
|
|
70
|
+
readonly noise: {
|
|
71
|
+
readonly region: OverlayRegion;
|
|
72
|
+
readonly tiling?: readonly [number, number];
|
|
73
|
+
};
|
|
74
|
+
/** 0 keeps everything; 1 cuts everything. */
|
|
75
|
+
readonly threshold: number;
|
|
76
|
+
/** The band's width, in the noise's own units. 0.05 unless given. */
|
|
77
|
+
readonly edge?: number;
|
|
78
|
+
/** The band's light. */
|
|
79
|
+
readonly edgeColour?: Vec3;
|
|
80
|
+
readonly edgeIntensity?: number;
|
|
81
|
+
/** A colour laid over the surface through the noise, as light, and how much of it. */
|
|
82
|
+
readonly overlay?: {
|
|
83
|
+
readonly colour: Vec3;
|
|
84
|
+
readonly amount: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A second normal map blended into the surface's by six region weights, read through two masks:
|
|
89
|
+
* the first mask's red, green and blue carry regions 1 to 3, the second's regions 4 to 6. A face's
|
|
90
|
+
* expressions, or veins brought up all over.
|
|
91
|
+
*/
|
|
92
|
+
export interface SurfaceWrinkle {
|
|
93
|
+
readonly normal: OverlayRegion;
|
|
94
|
+
readonly masks: readonly [OverlayRegion, OverlayRegion];
|
|
95
|
+
readonly weights: readonly [number, number, number, number, number, number];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* What a draw lays over its surface beyond its material, for `setSurfaceOverlay`: a rim, a dissolve
|
|
99
|
+
* and wrinkles, each optional, and the atlas their images are regions of.
|
|
100
|
+
*/
|
|
101
|
+
export interface SurfaceOverlay {
|
|
102
|
+
/**
|
|
103
|
+
* The atlas every image above is a region of. Absent: no image is read whatever regions are
|
|
104
|
+
* named — rims are smooth, nothing dissolves and nothing wrinkles — because the slot then holds a
|
|
105
|
+
* stand-in whose colour would read as a noise and a mask.
|
|
106
|
+
*/
|
|
107
|
+
readonly maps?: SurfaceTextureHandle;
|
|
108
|
+
readonly rim?: SurfaceRim;
|
|
109
|
+
readonly dissolve?: SurfaceDissolve;
|
|
110
|
+
readonly wrinkle?: SurfaceWrinkle;
|
|
111
|
+
}
|
|
112
|
+
/** Vectors the overlay takes in the material's block: `uOverlay[OVERLAY_VECTORS]`. */
|
|
113
|
+
export declare const OVERLAY_VECTORS = 15;
|
|
114
|
+
/** Floats `packSurfaceOverlay` fills. */
|
|
115
|
+
export declare const OVERLAY_FLOATS: number;
|
|
116
|
+
/**
|
|
117
|
+
* An overlay as the shader reads it, into `out` (`OVERLAY_FLOATS`); all zeros for `null`, which is
|
|
118
|
+
* no overlay. A region with a zero scale is an image not given, and its term reads as none — a
|
|
119
|
+
* noise of 1, a mask of 0, no wrinkle — and every region is one without `maps`. Fifteen vectors:
|
|
120
|
+
*
|
|
121
|
+
* - 0: rim colour, rim strength (intensity times alpha; 0 is no rim)
|
|
122
|
+
* - 1: falloff, upward lean, contrast, mask weight
|
|
123
|
+
* - 2: the rim's noise region; 3: its scroll along x and y, its tiling, the pulse's rate
|
|
124
|
+
* - 4: the pulse's low and high (1 and 1 where none)
|
|
125
|
+
* - 5: the rim's mask region
|
|
126
|
+
* - 6: the dissolve's noise region (a zero scale is no dissolve); 7: its tiling, threshold, edge
|
|
127
|
+
* - 8: the edge's light and its strength; 9: the laid-over colour and its amount
|
|
128
|
+
* - 10: the wrinkle normal's region; 11 and 12: the two masks'; 13 and 14: the six weights
|
|
129
|
+
*/
|
|
130
|
+
export declare function packSurfaceOverlay(overlay: SurfaceOverlay | null, out: Float32Array): void;
|
|
131
|
+
/** Whether an overlay lays anything at all, so a renderer can leave the lit switch off. */
|
|
132
|
+
export declare function overlayLays(overlay: SurfaceOverlay | null): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Vectors the overlay takes in the material's block: `uOverlay[OVERLAY_VECTORS]`. */
|
|
2
|
+
export const OVERLAY_VECTORS = 15;
|
|
3
|
+
/** Floats `packSurfaceOverlay` fills. */
|
|
4
|
+
export const OVERLAY_FLOATS = OVERLAY_VECTORS * 4;
|
|
5
|
+
const finite = (value, fallback) => value !== undefined && Number.isFinite(value) ? value : fallback;
|
|
6
|
+
function region(out, at, r) {
|
|
7
|
+
out[at] = finite(r?.scale[0], 0);
|
|
8
|
+
out[at + 1] = finite(r?.scale[1], 0);
|
|
9
|
+
out[at + 2] = finite(r?.offset[0], 0);
|
|
10
|
+
out[at + 3] = finite(r?.offset[1], 0);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* An overlay as the shader reads it, into `out` (`OVERLAY_FLOATS`); all zeros for `null`, which is
|
|
14
|
+
* no overlay. A region with a zero scale is an image not given, and its term reads as none — a
|
|
15
|
+
* noise of 1, a mask of 0, no wrinkle — and every region is one without `maps`. Fifteen vectors:
|
|
16
|
+
*
|
|
17
|
+
* - 0: rim colour, rim strength (intensity times alpha; 0 is no rim)
|
|
18
|
+
* - 1: falloff, upward lean, contrast, mask weight
|
|
19
|
+
* - 2: the rim's noise region; 3: its scroll along x and y, its tiling, the pulse's rate
|
|
20
|
+
* - 4: the pulse's low and high (1 and 1 where none)
|
|
21
|
+
* - 5: the rim's mask region
|
|
22
|
+
* - 6: the dissolve's noise region (a zero scale is no dissolve); 7: its tiling, threshold, edge
|
|
23
|
+
* - 8: the edge's light and its strength; 9: the laid-over colour and its amount
|
|
24
|
+
* - 10: the wrinkle normal's region; 11 and 12: the two masks'; 13 and 14: the six weights
|
|
25
|
+
*/
|
|
26
|
+
export function packSurfaceOverlay(overlay, out) {
|
|
27
|
+
out.fill(0);
|
|
28
|
+
if (overlay === null)
|
|
29
|
+
return;
|
|
30
|
+
/* No atlas, no region: see `SurfaceOverlay.maps`. */
|
|
31
|
+
const images = overlay.maps !== undefined;
|
|
32
|
+
const rim = overlay.rim;
|
|
33
|
+
if (rim !== undefined) {
|
|
34
|
+
out[0] = finite(rim.colour[0], 0);
|
|
35
|
+
out[1] = finite(rim.colour[1], 0);
|
|
36
|
+
out[2] = finite(rim.colour[2], 0);
|
|
37
|
+
out[3] = Math.max(0, finite(rim.intensity, 0) * finite(rim.alpha, 1));
|
|
38
|
+
out[4] = Math.max(0, finite(rim.falloff, 1.5));
|
|
39
|
+
out[5] = Math.max(0, finite(rim.upward, 0.4));
|
|
40
|
+
out[6] = Math.max(0, finite(rim.contrast, 1));
|
|
41
|
+
out[7] = rim.mask === undefined ? 0 : Math.min(Math.max(finite(rim.mask.weight, 0), 0), 1);
|
|
42
|
+
region(out, 8, images ? rim.noise?.region : null);
|
|
43
|
+
out[12] = finite(rim.noise?.scroll?.[0], 0);
|
|
44
|
+
out[13] = finite(rim.noise?.scroll?.[1], 0);
|
|
45
|
+
out[14] = Math.max(0, finite(rim.noise?.tiling, 1));
|
|
46
|
+
out[15] = finite(rim.pulse?.rate, 0);
|
|
47
|
+
out[16] = rim.pulse === undefined ? 1 : finite(rim.pulse.low, 1);
|
|
48
|
+
out[17] = rim.pulse === undefined ? 1 : finite(rim.pulse.high, 1);
|
|
49
|
+
region(out, 20, images ? rim.mask?.region : null);
|
|
50
|
+
}
|
|
51
|
+
const dissolve = overlay.dissolve;
|
|
52
|
+
if (dissolve !== undefined) {
|
|
53
|
+
region(out, 24, images ? dissolve.noise.region : null);
|
|
54
|
+
out[28] = finite(dissolve.noise.tiling?.[0], 1);
|
|
55
|
+
out[29] = finite(dissolve.noise.tiling?.[1], 1);
|
|
56
|
+
out[30] = Math.min(Math.max(finite(dissolve.threshold, 0), 0), 1);
|
|
57
|
+
out[31] = Math.max(1e-4, finite(dissolve.edge, 0.05));
|
|
58
|
+
const edge = dissolve.edgeColour ?? [0, 0, 0];
|
|
59
|
+
out[32] = finite(edge[0], 0);
|
|
60
|
+
out[33] = finite(edge[1], 0);
|
|
61
|
+
out[34] = finite(edge[2], 0);
|
|
62
|
+
out[35] = Math.max(0, finite(dissolve.edgeIntensity, 1));
|
|
63
|
+
const laid = dissolve.overlay;
|
|
64
|
+
if (laid !== undefined) {
|
|
65
|
+
out[36] = finite(laid.colour[0], 0);
|
|
66
|
+
out[37] = finite(laid.colour[1], 0);
|
|
67
|
+
out[38] = finite(laid.colour[2], 0);
|
|
68
|
+
out[39] = Math.max(0, finite(laid.amount, 0));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const wrinkle = overlay.wrinkle;
|
|
72
|
+
if (wrinkle !== undefined) {
|
|
73
|
+
region(out, 40, images ? wrinkle.normal : null);
|
|
74
|
+
region(out, 44, images ? wrinkle.masks[0] : null);
|
|
75
|
+
region(out, 48, images ? wrinkle.masks[1] : null);
|
|
76
|
+
for (let i = 0; i < 6; i++) {
|
|
77
|
+
out[52 + i] = Math.min(Math.max(finite(wrinkle.weights[i], 0), 0), 1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Whether an overlay lays anything at all, so a renderer can leave the lit switch off. */
|
|
82
|
+
export function overlayLays(overlay) {
|
|
83
|
+
return (overlay !== null &&
|
|
84
|
+
(overlay.rim !== undefined || overlay.dissolve !== undefined || overlay.wrinkle !== undefined));
|
|
85
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { CompressedTextureFormat, SurfaceSource } from './compressedSource.
|
|
|
13
13
|
import type { SurfaceLayerEffect } from './surfaceEffects.ts';
|
|
14
14
|
import type { CutoutMode } from './cutoutDither.ts';
|
|
15
15
|
import type { LightmapTexels } from './lightmap.ts';
|
|
16
|
+
import type { SceneCaptureTexels } from './sceneCapture.ts';
|
|
16
17
|
/**
|
|
17
18
|
* How a texture behaves past its edges and between its texels.
|
|
18
19
|
*
|
|
@@ -161,6 +162,24 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
161
162
|
* that is not a colour.
|
|
162
163
|
*/
|
|
163
164
|
modelMap?: Texture | null;
|
|
165
|
+
/**
|
|
166
|
+
* Whether a lamp's and the sun's highlight on this surface is GGX's own, `π · D · Vis · F · N·L`,
|
|
167
|
+
* as Unreal and this engine's skin and eye shade it, rather than the engine's lobe scaled to a
|
|
168
|
+
* peak of one. **False by default**, every surface as it was.
|
|
169
|
+
*
|
|
170
|
+
* The peak-normalised lobe is a look control: the specular attribute says how strong a highlight
|
|
171
|
+
* is and roughness how wide, apart. Physical, they are entangled as they are in a real surface —
|
|
172
|
+
* the peak is `1 / (4 α²)` of the light head-on, so a polished surface's highlight is many times
|
|
173
|
+
* the look's and a rough one's lower and broader — and the specular attribute is read as the
|
|
174
|
+
* reflectance at normal incidence, F0: Unreal's `0.08 × Specular`, 0.04 at its default. Fresnel
|
|
175
|
+
* then brightens every surface toward grazing, not only a metal. A rectangle's highlight is the
|
|
176
|
+
* lobe integrated over it with the attribute as F0 already, so it is unchanged either way.
|
|
177
|
+
*
|
|
178
|
+
* On the standard, lightmap and anisotropic models. Skin and the eye are physical already; hair
|
|
179
|
+
* keeps its own three lobes. **What it gives up**: the look's independence of strength and width,
|
|
180
|
+
* and an 8-bit frame clips the brighter peak without `hdrScene`.
|
|
181
|
+
*/
|
|
182
|
+
physicalSpecular?: boolean;
|
|
164
183
|
}
|
|
165
184
|
export interface SurfaceTextureOptions {
|
|
166
185
|
/**
|
|
@@ -257,12 +276,19 @@ export declare class SurfaceTexture {
|
|
|
257
276
|
private effectsTable;
|
|
258
277
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
259
278
|
private readonly blockFormat;
|
|
279
|
+
/** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
|
|
280
|
+
private width;
|
|
281
|
+
private height;
|
|
282
|
+
/** The texture's storage format, which `flatLayer`'s copy matches so the copy converts nothing. */
|
|
283
|
+
private readonly storage;
|
|
284
|
+
/** Layer 0 as a plain 2D texture, made by `flatLayer` and dropped when the pixels change. */
|
|
285
|
+
private flat;
|
|
260
286
|
/**
|
|
261
287
|
* One image, or an array of images that must share one size (`layerSize` refuses otherwise).
|
|
262
288
|
* Uploaded unflipped either way; see the constructor body for why. Or blocks, uploaded as they are
|
|
263
289
|
* where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
|
|
264
290
|
*/
|
|
265
|
-
constructor(gl: WebGL2RenderingContext, source: SurfaceSource | readonly SurfaceSource[] | LightmapTexels, options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
|
|
291
|
+
constructor(gl: WebGL2RenderingContext, source: SurfaceSource | readonly SurfaceSource[] | LightmapTexels | SceneCaptureTexels, options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
|
|
266
292
|
/** Whether this texture carries an effects table, which the lit program reads only once one exists. */
|
|
267
293
|
get hasEffects(): boolean;
|
|
268
294
|
bindEffects(gl: WebGL2RenderingContext, unit: number, fallback: WebGLTexture): void;
|
|
@@ -278,7 +304,24 @@ export declare class SurfaceTexture {
|
|
|
278
304
|
* doing this every frame at any size wants to know that it costs what it costs.
|
|
279
305
|
*/
|
|
280
306
|
update(gl: WebGL2RenderingContext, source: TexImageSource): void;
|
|
307
|
+
/**
|
|
308
|
+
* Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
|
|
309
|
+
* by `captureScene`. Engine-internal, as `bind` is.
|
|
310
|
+
*/
|
|
311
|
+
attachColor(gl: WebGL2RenderingContext): void;
|
|
281
312
|
/** Bind to a unit for sampling. Engine-internal: the renderer owns unit assignment. */
|
|
282
313
|
bind(gl: WebGL2RenderingContext, unit: number): void;
|
|
314
|
+
/**
|
|
315
|
+
* Layer 0 as a plain 2D texture: what a `sampler2D` can be handed, which an array cannot. Copied
|
|
316
|
+
* the first time it is asked for, by a framebuffer blit into storage of the same format, and kept
|
|
317
|
+
* until `update` or `dispose`. Engine-internal: a surface overlay's atlas is bound where the
|
|
318
|
+
* lit stage's one 2D image slot is (`surfaceOverlay.ts`).
|
|
319
|
+
*
|
|
320
|
+
* **Null for blocks**, which no framebuffer can read; the caller says so. What it costs is a copy
|
|
321
|
+
* of level 0 in memory beside the array, and the bindings of both framebuffer targets, which are
|
|
322
|
+
* read back before the blit and put back after it — a sync query, paid once per image.
|
|
323
|
+
*/
|
|
324
|
+
flatLayer(gl: WebGL2RenderingContext): WebGLTexture | null;
|
|
325
|
+
private dropFlat;
|
|
283
326
|
dispose(gl: WebGL2RenderingContext): void;
|
|
284
327
|
}
|