@driftengine/core 4.8.5 → 4.8.6
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 +4321 -4250
- package/dist/index.d.ts +10 -2
- package/dist/index.js +11 -1
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/areaLights.d.ts +19 -0
- package/dist/render/areaLights.js +20 -0
- package/dist/render/backend/api.d.ts +13 -2
- package/dist/render/backend/webgl2/instanced.d.ts +2 -0
- package/dist/render/backend/webgl2/instanced.js +11 -2
- package/dist/render/backend/webgl2/renderer.d.ts +74 -1
- package/dist/render/backend/webgl2/renderer.js +229 -37
- package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
- package/dist/render/backend/webgl2/skinScatter.js +88 -26
- package/dist/render/backend/webgpu/buffers.js +2 -1
- package/dist/render/backend/webgpu/clusterBinner.js +3 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
- package/dist/render/backend/webgpu/flatPass.js +15 -13
- package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
- package/dist/render/backend/webgpu/particlePass.js +80 -12
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/postPass.js +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +58 -7
- package/dist/render/backend/webgpu/renderer.js +329 -78
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
- package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
- package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +2 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +18 -4
- package/dist/render/bloomChain.d.ts +44 -0
- package/dist/render/bloomChain.js +28 -0
- package/dist/render/bloomPass.d.ts +3 -1
- package/dist/render/bloomPass.js +16 -2
- package/dist/render/clusteredLights.d.ts +55 -1
- package/dist/render/clusteredLights.js +182 -8
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/instanceCull.js +7 -0
- package/dist/render/instances.d.ts +32 -10
- package/dist/render/instances.js +17 -8
- package/dist/render/lightBudget.d.ts +26 -1
- package/dist/render/lightBudget.js +26 -3
- package/dist/render/lightmap.d.ts +102 -0
- package/dist/render/lightmap.js +118 -0
- package/dist/render/mesh.js +2 -1
- package/dist/render/particleBatch.d.ts +59 -3
- package/dist/render/particleBatch.js +53 -2
- package/dist/render/particleMaterial.d.ts +6 -1
- package/dist/render/particleMaterial.js +10 -2
- package/dist/render/particlePool.d.ts +11 -0
- package/dist/render/particlePool.js +2 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +96 -0
- package/dist/render/shaders/flat/index.js +30 -4
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +107 -105
- package/dist/render/shaders/flat/models.d.ts +16 -4
- package/dist/render/shaders/flat/models.js +64 -4
- package/dist/render/shaders/flat/normalMap.d.ts +10 -0
- package/dist/render/shaders/flat/normalMap.js +25 -0
- package/dist/render/shaders/flat/preamble.js +74 -37
- package/dist/render/shaders/flat/skin.js +6 -5
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
- package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1631 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1669 -2082
- package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
- package/dist/render/shaders/generated/particle.wgsl.js +115 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
- package/dist/render/shaders/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/surfaceModel.d.ts +21 -1
- package/dist/render/surfaceModel.js +22 -0
- package/dist/render/surfaceTexture.d.ts +14 -1
- package/dist/render/surfaceTexture.js +15 -2
- package/package.json +6 -6
- package/src/index.ts +20 -1
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/areaLights.ts +39 -0
- package/src/render/backend/api.ts +14 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +292 -35
- package/src/render/backend/webgl2/skinScatter.ts +120 -43
- package/src/render/backend/webgpu/buffers.ts +2 -1
- package/src/render/backend/webgpu/clusterBinner.ts +3 -1
- package/src/render/backend/webgpu/flatPass.ts +18 -13
- package/src/render/backend/webgpu/particlePass.ts +118 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +396 -95
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
- package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
- package/src/render/backend/webgpu/surfaceTexturePass.ts +26 -5
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +221 -7
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/instanceCull.ts +5 -0
- package/src/render/instances.ts +40 -11
- package/src/render/lightBudget.ts +48 -2
- package/src/render/lightmap.ts +202 -0
- package/src/render/mesh.ts +2 -1
- package/src/render/particleBatch.ts +95 -4
- package/src/render/particleMaterial.ts +11 -1
- package/src/render/particlePool.ts +13 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/flat/areaLight.ts +96 -0
- package/src/render/shaders/flat/index.ts +30 -4
- package/src/render/shaders/flat/main.ts +107 -105
- package/src/render/shaders/flat/models.ts +73 -8
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +74 -37
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/flat.wgsl.ts +1669 -2082
- package/src/render/shaders/generated/particle.wgsl.ts +116 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
- package/src/render/shaders/particle.ts +134 -1
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/surfaceModel.ts +36 -1
- package/src/render/surfaceTexture.ts +41 -3
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One rectangle's light on a fragment: its form factor, and what it adds once its occlusion is
|
|
3
|
+
* known. **The one statement of it**, called by the fixed loop over the first `maxAreaLights`
|
|
4
|
+
* rectangles and by the froxel table's for the rest, so the two arms shade a rectangle with one
|
|
5
|
+
* body rather than two transcriptions of it — the zero-pixel gate between them means something
|
|
6
|
+
* only if that is so.
|
|
7
|
+
*
|
|
8
|
+
* Depth zero and after the models, whose `modelLight` it calls; see `flatFrag`.
|
|
9
|
+
*/
|
|
10
|
+
export const AREA_LIGHT_GLSL = /* glsl */ `
|
|
11
|
+
/**
|
|
12
|
+
* The rectangle's signed form factor at this fragment: its corners **wound so a surface on the
|
|
13
|
+
* emitting side sees them counter-clockwise**, which is what makes it positive there. The natural
|
|
14
|
+
* order — minus, plus, plus, minus — winds the other way, and every one-sided light then clamped to
|
|
15
|
+
* zero and the frame was black. The sign is the sidedness: a one-sided rectangle takes the positive
|
|
16
|
+
* part and a two-sided one the magnitude.
|
|
17
|
+
*/
|
|
18
|
+
float areaSignedForm(vec3 n, vec3 centre, vec3 right, vec3 up, vec2 halfSize) {
|
|
19
|
+
vec3 c0 = centre - right * halfSize.x + up * halfSize.y;
|
|
20
|
+
vec3 c1 = centre + right * halfSize.x + up * halfSize.y;
|
|
21
|
+
vec3 c2 = centre + right * halfSize.x - up * halfSize.y;
|
|
22
|
+
vec3 c3 = centre - right * halfSize.x - up * halfSize.y;
|
|
23
|
+
return quadFormFactor(n, vWorldPos, c0, c1, c2, c3);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* What one rectangle of colour \`color\` and form factor \`form\` adds to the lamps' two sums, open
|
|
28
|
+
* and shadowed by \`occl\` and \`glass\`.
|
|
29
|
+
*
|
|
30
|
+
* **A model answers a rectangle as it answers a lamp at its centre**, carried at the rectangle's
|
|
31
|
+
* own energy: the diffuse by the exact form factor over the centre's cosine, the specular by the
|
|
32
|
+
* form factor. An approximation, stated: a model's lobe is not integrated over the rectangle as the
|
|
33
|
+
* standard lobe is, so a glossy model under a large softbox shows a lobe from its centre rather than
|
|
34
|
+
* its shape.
|
|
35
|
+
*
|
|
36
|
+
* **The standard lobe is integrated over the rectangle**, rather than sampled at one point on it.
|
|
37
|
+
* What stood here once handed the closest point on the rectangle to \`sphereLobe\` and scaled it by
|
|
38
|
+
* \`form\`, the *diffuse* form factor. Measured against a brute-force integral by
|
|
39
|
+
* \`scripts/areaSpecular.mjs\`, that lost the reflection rather than blurring it: on polished metal
|
|
40
|
+
* facing a softbox it returned 0.000233 where the integral is 0.9207, and head-on on a rough surface
|
|
41
|
+
* it was 3.7 times too bright. Worst 287% either way; this is 40.1% where the light is what the
|
|
42
|
+
* surface reflects and 79.0% for one overhead. \`quadCoverage\` says what fraction of the specular
|
|
43
|
+
* lobe the rectangle covers and the environment BRDF says how much the surface returns, the split
|
|
44
|
+
* the probe path uses. **\`dfg.x + dfg.y\` and not \`envSpecularEnergy\`**, deliberately: the
|
|
45
|
+
* compensation returns the multiply-scattered share, and every direct lobe in this shader is
|
|
46
|
+
* single-scattering, so adding it here returns energy the light never had. Measured at 211%
|
|
47
|
+
* against 40.1%.
|
|
48
|
+
*/
|
|
49
|
+
void areaLightAdd(
|
|
50
|
+
vec3 centre,
|
|
51
|
+
vec3 right,
|
|
52
|
+
vec3 up,
|
|
53
|
+
vec2 halfSize,
|
|
54
|
+
vec3 color,
|
|
55
|
+
float form,
|
|
56
|
+
float occl,
|
|
57
|
+
vec3 glass,
|
|
58
|
+
vec3 n,
|
|
59
|
+
vec3 albedo,
|
|
60
|
+
float metal,
|
|
61
|
+
vec3 specColor,
|
|
62
|
+
float roughness,
|
|
63
|
+
bool modelled,
|
|
64
|
+
inout vec3 lampOpen,
|
|
65
|
+
inout vec3 lampShadowed
|
|
66
|
+
) {
|
|
67
|
+
if (modelled) {
|
|
68
|
+
vec3 areaL = normalize(centre - vWorldPos);
|
|
69
|
+
modelLight(areaL, 0.0, 0.0);
|
|
70
|
+
vec3 areaModel = (mDiffuse * (form / max(dot(n, areaL), 1e-3)) + mSpecular * form) * color;
|
|
71
|
+
lampOpen += areaModel;
|
|
72
|
+
lampShadowed += areaModel * occl * glass;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
/* Held back with the point lamps', and shadowed by the same term it always was. */
|
|
76
|
+
vec3 areaDiffuse = albedo * color * form * (1.0 - metal);
|
|
77
|
+
lampOpen += areaDiffuse;
|
|
78
|
+
lampShadowed += areaDiffuse * occl * glass;
|
|
79
|
+
|
|
80
|
+
if (vSpecular > 0.0 || metal > 0.0) {
|
|
81
|
+
vec3 c0 = centre - right * halfSize.x + up * halfSize.y;
|
|
82
|
+
vec3 c1 = centre + right * halfSize.x + up * halfSize.y;
|
|
83
|
+
vec3 c2 = centre + right * halfSize.x - up * halfSize.y;
|
|
84
|
+
vec3 c3 = centre - right * halfSize.x - up * halfSize.y;
|
|
85
|
+
vec3 toEyeArea = normalize(uCameraPos - vWorldPos);
|
|
86
|
+
float coverage = quadCoverage(n, vWorldPos, toEyeArea, roughness, c0, c1, c2, c3);
|
|
87
|
+
if (coverage > 0.0) {
|
|
88
|
+
vec2 areaDfg = envBrdfApprox(max(dot(n, toEyeArea), 0.0), roughness);
|
|
89
|
+
vec3 areaSpec = specColor * areaDfg.x + vec3(areaDfg.y);
|
|
90
|
+
vec3 areaHighlight = areaSpec * color * coverage;
|
|
91
|
+
lampOpen += areaHighlight;
|
|
92
|
+
lampShadowed += areaHighlight * occl * glass;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
@@ -15,6 +15,8 @@ import { SKINNING_GLSL } from '../skinning.ts';
|
|
|
15
15
|
import { SURFACE_GLSL } from './surface.ts';
|
|
16
16
|
import { MAIN_GLSL } from './main.ts';
|
|
17
17
|
import { MODELS_GLSL } from './models.ts';
|
|
18
|
+
import { AREA_LIGHT_GLSL } from './areaLight.ts';
|
|
19
|
+
import { NORMAL_MAP_GLSL } from './normalMap.ts';
|
|
18
20
|
import { DRIFT_LIGHT_GLSL } from './driftLight.ts';
|
|
19
21
|
import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.ts';
|
|
20
22
|
import { CHANNEL_ATTRIBUTE, CHANNEL_BEND } from '../vertexChannel.ts';
|
|
@@ -107,7 +109,17 @@ layout(location = 11) in vec4 aInstanceModel0;
|
|
|
107
109
|
layout(location = 12) in vec4 aInstanceModel1;
|
|
108
110
|
layout(location = 13) in vec4 aInstanceModel2;
|
|
109
111
|
layout(location = 14) in vec4 aInstanceModel3;
|
|
110
|
-
|
|
112
|
+
/**
|
|
113
|
+
* The instance's tint, and its opacity in w: \`MeshInstances.alphas\`, 1 where none was given — or,
|
|
114
|
+
* in a lightmapped batch's pipeline, its region of the page (\`MeshInstances.lightmapRegions\`).
|
|
115
|
+
*/
|
|
116
|
+
layout(location = 15) in vec4 aInstanceTint;
|
|
117
|
+
/*
|
|
118
|
+
* On in the pipeline a lightmapped material draws an instanced batch with, where the lane above is
|
|
119
|
+
* a region and not a tint: the second coordinates leave scaled and offset by it, under a tint and an
|
|
120
|
+
* opacity of one. See lightmap.ts.
|
|
121
|
+
*/
|
|
122
|
+
const bool LIGHTMAP_REGIONS = false; // wgsl:override
|
|
111
123
|
#else
|
|
112
124
|
uniform mat4 uModel;
|
|
113
125
|
/**
|
|
@@ -233,7 +245,8 @@ void main() {
|
|
|
233
245
|
#endif
|
|
234
246
|
#if INSTANCED
|
|
235
247
|
mat4 model = mat4(aInstanceModel0, aInstanceModel1, aInstanceModel2, aInstanceModel3);
|
|
236
|
-
vec3 tint = aInstanceTint;
|
|
248
|
+
vec3 tint = aInstanceTint.rgb;
|
|
249
|
+
if (LIGHTMAP_REGIONS) tint = vec3(1.0);
|
|
237
250
|
#else
|
|
238
251
|
mat4 model = uModel;
|
|
239
252
|
vec3 tint = uTint;
|
|
@@ -287,10 +300,19 @@ void main() {
|
|
|
287
300
|
vRelief = aRelief;
|
|
288
301
|
vTangent = vec4(worldTangent, aTangent.w);
|
|
289
302
|
#if INSTANCED
|
|
290
|
-
/*
|
|
303
|
+
/*
|
|
304
|
+
* No channel on this path; the neutral values are what every mesh read before it existed — but
|
|
305
|
+
* for the opacity, which an instance carries of its own, 1 where none was given.
|
|
306
|
+
*/
|
|
291
307
|
vSkyDirect = 1.0;
|
|
292
|
-
vAlpha =
|
|
308
|
+
vAlpha = aInstanceTint.w;
|
|
293
309
|
vThickness = 1.0;
|
|
310
|
+
if (LIGHTMAP_REGIONS) {
|
|
311
|
+
/* Out of the lane's \`-1 - uv\`, through the region, and back into it. See lightmap.ts. */
|
|
312
|
+
vGrain = -1.0 - ((-1.0 - aGrain) * aInstanceTint.x + aInstanceTint.z);
|
|
313
|
+
vRelief = -1.0 - ((-1.0 - aRelief) * aInstanceTint.y + aInstanceTint.w);
|
|
314
|
+
vAlpha = 1.0;
|
|
315
|
+
}
|
|
294
316
|
#else
|
|
295
317
|
vSkyDirect = aChannel.y;
|
|
296
318
|
vAlpha = aChannel.z;
|
|
@@ -563,6 +585,8 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
563
585
|
* and `main` is unconditional by construction.
|
|
564
586
|
*/
|
|
565
587
|
TANGENT_FRAME_GLSL,
|
|
588
|
+
/* After the frame it calls, unconditional for the same reason. See normalMap.ts. */
|
|
589
|
+
NORMAL_MAP_GLSL,
|
|
566
590
|
/* Unconditional for the same reason, and called only where a cutoff was asked for. */
|
|
567
591
|
CUTOUT_COVERAGE_GLSL,
|
|
568
592
|
CUTOUT_DITHER_GLSL,
|
|
@@ -570,6 +594,8 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
570
594
|
SURFACE_EFFECTS_GLSL,
|
|
571
595
|
/* Depth zero and after the tangent frame and the lobes it calls; see models.ts. */
|
|
572
596
|
MODELS_GLSL,
|
|
597
|
+
/* After the models, whose answer to a rectangle it asks for. See areaLight.ts. */
|
|
598
|
+
AREA_LIGHT_GLSL,
|
|
573
599
|
MAIN_GLSL,
|
|
574
600
|
].join('\n'),
|
|
575
601
|
{
|
|
@@ -148,6 +148,12 @@ export const MAIN_GLSL = `void main() {
|
|
|
148
148
|
float glassTransmission = uSeeThrough.z;
|
|
149
149
|
float glassFrost = uSeeThrough.w;
|
|
150
150
|
|
|
151
|
+
/*
|
|
152
|
+
* The normal a refracting draw bends by: the shading normal, a normal map's tilt and every relief
|
|
153
|
+
* in it, as the lit block leaves it before turning a back face — the geometric normal where
|
|
154
|
+
* nothing tilts it, which is what refraction bent by before 4.8.6. See the refraction below.
|
|
155
|
+
*/
|
|
156
|
+
vec3 shadingNormal = normalize(vNormal);
|
|
151
157
|
if (uLightingEnabled != 0) {
|
|
152
158
|
vec3 n = normalize(vNormal);
|
|
153
159
|
/*
|
|
@@ -181,20 +187,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
181
187
|
* right: a mipped colour image, sampled under uniform control flow, exactly as uAlbedo is
|
|
182
188
|
* below. The rule is about non-uniform branches, and this branch is on a uniform.
|
|
183
189
|
*/
|
|
184
|
-
if (uNormalStrength > 0.0)
|
|
185
|
-
vec3 mapped = texture(uNormalMap, surfaceAt).xyz * 2.0 - 1.0;
|
|
186
|
-
/*
|
|
187
|
-
* z rebuilt where none is stored. A tangent-space normal points out of its surface, so a
|
|
188
|
-
* stored z at or below zero is never a real one: it is a two-channel map, BC5, whose blue a
|
|
189
|
-
* device samples as zero and which arrives here as -1. Every map baked from BC5 to PNG before
|
|
190
|
-
* 4.8.4 carried the same zero. A select rather than a flag: no uniform to bind, no material
|
|
191
|
-
* that has to say what format it holds, and continuous at the boundary, since a unit texel
|
|
192
|
-
* lying flat has its z near zero either way. A texel with z above zero is left as it was.
|
|
193
|
-
*/
|
|
194
|
-
mapped.z = mapped.z > 0.0 ? mapped.z : sqrt(max(0.0, 1.0 - dot(mapped.xy, mapped.xy)));
|
|
195
|
-
mat3 tbn = tangentFrame(n, vWorldPos, vUv.xy, vTangent, vHasTangents);
|
|
196
|
-
n = normalize(mix(n, normalize(tbn * mapped), uNormalStrength));
|
|
197
|
-
}
|
|
190
|
+
if (uNormalStrength > 0.0) n = normalMapped(n, surfaceAt);
|
|
198
191
|
|
|
199
192
|
/*
|
|
200
193
|
* Microscopic relief: the surface's own texture, as a turn of the normal rather than a change
|
|
@@ -218,7 +211,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
218
211
|
* widening driven by fwidth was measured making that artefact three times worse, because that
|
|
219
212
|
* derivative spikes at every hard crease. See docs/IMPROVEMENTS.md.
|
|
220
213
|
*/
|
|
221
|
-
|
|
214
|
+
/* Never below zero: a lightmapped mesh's coordinates ride this lane below it. See lightmap.ts. */
|
|
215
|
+
float reliefAmount = uRelief.x * max(vRelief, 0.0);
|
|
222
216
|
/* Grown with the relief, before anything reads it. See the comment above: a normal that
|
|
223
217
|
wanders cannot hold a highlight narrower than the wander. */
|
|
224
218
|
/*
|
|
@@ -259,11 +253,11 @@ export const MAIN_GLSL = `void main() {
|
|
|
259
253
|
* variance is still roughness — a road that faded to smooth at distance would go glossy
|
|
260
254
|
* instead of noisy, which is a worse artefact wearing better clothes.
|
|
261
255
|
*/
|
|
262
|
-
vec3 reliefPixel = fwidth(vWorldPos) *
|
|
256
|
+
vec3 reliefPixel = fwidth(vWorldPos) * uRelief.y;
|
|
263
257
|
float reliefFootprint = max(max(reliefPixel.x, reliefPixel.y), reliefPixel.z);
|
|
264
258
|
float reliefResolved = reliefAmount * (1.0 - smoothstep(0.5, 1.5, reliefFootprint));
|
|
265
259
|
if (reliefResolved > 0.0) {
|
|
266
|
-
vec3 at = vWorldPos *
|
|
260
|
+
vec3 at = vWorldPos * uRelief.y;
|
|
267
261
|
/* One cell of the noise, which is one bump: the gradient over any smaller step is the
|
|
268
262
|
interpolant's own slope rather than the shape's. */
|
|
269
263
|
float here = grain(at);
|
|
@@ -360,6 +354,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
360
354
|
* Turning only the interpolated normal would leave a normal map's tilt pointing the wrong way
|
|
361
355
|
* across the fold of every curtain.
|
|
362
356
|
*/
|
|
357
|
+
shadingNormal = n;
|
|
363
358
|
if (backFace) n = -n;
|
|
364
359
|
/*
|
|
365
360
|
* A model's surface, gathered once, on a branch on the switches alone (models.ts) — and whether
|
|
@@ -369,6 +364,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
369
364
|
bool modelled = modelOn();
|
|
370
365
|
bool modelledBehind = modelSeesBehind();
|
|
371
366
|
if (modelled) modelSurface(n, albedo, surfaceRoughness, metal, surfaceAt);
|
|
367
|
+
/*
|
|
368
|
+
* Skin's colour half leaves here, with the colour its spread light is multiplied by, and a whole
|
|
369
|
+
* alpha a multisampled edge resolves to its coverage. A branch on the switches alone, so uniform.
|
|
370
|
+
*/
|
|
371
|
+
if (modelAlbedoAlone()) {
|
|
372
|
+
outColor = vec4(albedo, 1.0);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
372
375
|
|
|
373
376
|
/*
|
|
374
377
|
* One dot product, used twice: ndl is the lit side and its negative is the night side below.
|
|
@@ -468,6 +471,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
468
471
|
irradiance = mix(irradiance, ambient, probeGridOutside(surfaceN, vWorldPos));
|
|
469
472
|
ambient = mix(ambient, irradiance, uProbeGridAmbient * uEnvironmentEnabled);
|
|
470
473
|
#endif
|
|
474
|
+
/* A draw's own ambient, where one is set, in place of the frame's: \`setAmbientSH\`. */
|
|
475
|
+
if (uAmbientSH[6].w > 0.5) ambient = ambientHarmonics(n);
|
|
471
476
|
/*
|
|
472
477
|
* A metal keeps its ambient and loses its direct diffuse.
|
|
473
478
|
*
|
|
@@ -495,6 +500,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
495
500
|
} else {
|
|
496
501
|
lit = albedo * (ambient + sunColor * direct * (1.0 - metal));
|
|
497
502
|
}
|
|
503
|
+
/* A baked page's light, beside the sky's and the sun's: nothing but where a material has one. */
|
|
504
|
+
lit += modelBaked(n, albedo, metal);
|
|
498
505
|
|
|
499
506
|
/*
|
|
500
507
|
* A sun highlight, for the few surfaces that ask for one.
|
|
@@ -640,7 +647,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
640
647
|
*/
|
|
641
648
|
/* A model reflects its environment whatever the pass asked of the standard model: see
|
|
642
649
|
modelReflectance. On the switches alone, so the derivatives below stay in uniform flow. */
|
|
643
|
-
if (
|
|
650
|
+
if (uEnvironmentDials.y > 0.0 || uMaterialFlags.y != 0 || modelled) {
|
|
644
651
|
vec3 toEye = normalize(uCameraPos - vWorldPos);
|
|
645
652
|
vec3 mirrored = reflect(-toEye, modelled ? modelReflectNormal(n) : n);
|
|
646
653
|
vec3 environment = mix(uAmbientGround, uAmbient, mirrored.y * 0.5 + 0.5);
|
|
@@ -741,7 +748,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
741
748
|
float footprintRough = 0.5 * sqrt(min(swingRad, 1.0));
|
|
742
749
|
float lobeRough = min(1.0, sqrt(surfaceRoughness * surfaceRoughness + footprintRough * footprintRough));
|
|
743
750
|
float envLod = clamp(
|
|
744
|
-
mix(boxLod, lobeRough * uEnvironmentMaxLod,
|
|
751
|
+
mix(boxLod, lobeRough * uEnvironmentMaxLod, uEnvironmentDials.x),
|
|
745
752
|
0.0,
|
|
746
753
|
uEnvironmentMaxLod
|
|
747
754
|
);
|
|
@@ -777,7 +784,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
777
784
|
* \`mix(1, gain, 0)\` is exactly 1, so a dielectric is arithmetically untouched however the
|
|
778
785
|
* caller sets this.
|
|
779
786
|
*/
|
|
780
|
-
environment *= mix(1.0,
|
|
787
|
+
environment *= mix(1.0, uEnvironmentDials.z, metal);
|
|
781
788
|
float facing = 1.0 - max(dot(n, toEye), 0.0);
|
|
782
789
|
/*
|
|
783
790
|
* **A metal's Fresnel base is close to its albedo, not a dielectric's four percent.** Schlick
|
|
@@ -811,7 +818,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
811
818
|
* which is what the old \`max(uReflectivity, metal) * (1 - r)\` came to, and both renderers
|
|
812
819
|
* clamp that uniform non-negative on upload.
|
|
813
820
|
*/
|
|
814
|
-
float reflectAmount = max(
|
|
821
|
+
float reflectAmount = max(uEnvironmentDials.y * (1.0 - surfaceRoughness), metal);
|
|
815
822
|
/*
|
|
816
823
|
* **With a prefiltered environment, how much a surface returns is an integral rather than a
|
|
817
824
|
* curve somebody chose**, and this is where the comment above said the thinning should go
|
|
@@ -881,7 +888,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
881
888
|
*/
|
|
882
889
|
#if ENVIRONMENT_PROBE
|
|
883
890
|
vec2 dfg = envBrdfApprox(ndv, surfaceRoughness);
|
|
884
|
-
float integrated = clamp(envSpecularEnergy(f0, dfg) * max(
|
|
891
|
+
float integrated = clamp(envSpecularEnergy(f0, dfg) * max(uEnvironmentDials.y, metal), 0.0, 1.0);
|
|
885
892
|
/*
|
|
886
893
|
* **The split sum only where a prefiltered chain is what is being sampled.** It is the
|
|
887
894
|
* correct share of a *prefiltered* environment; over a box chain it asserts an integral that
|
|
@@ -893,7 +900,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
893
900
|
float weight = mix(
|
|
894
901
|
clamp(fresnel * reflectAmount, 0.0, 1.0),
|
|
895
902
|
integrated,
|
|
896
|
-
uEnvironmentEnabled *
|
|
903
|
+
uEnvironmentEnabled * uEnvironmentDials.x
|
|
897
904
|
);
|
|
898
905
|
#else
|
|
899
906
|
float weight = clamp(fresnel * reflectAmount, 0.0, 1.0);
|
|
@@ -1030,8 +1037,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
1030
1037
|
bool fixtureProfiles = false;
|
|
1031
1038
|
bool fixtureShapes = false;
|
|
1032
1039
|
if (LIGHT_FIXTURES) {
|
|
1033
|
-
fixtureProfiles =
|
|
1034
|
-
fixtureShapes =
|
|
1040
|
+
fixtureProfiles = uFixtureShape.x > 1.0;
|
|
1041
|
+
fixtureShapes = uFixtureShape.y > 1.0 || uFixtureShape.z > 0.0;
|
|
1035
1042
|
}
|
|
1036
1043
|
|
|
1037
1044
|
// Point lights. Inverse-square-ish falloff clipped to a finite radius, so a
|
|
@@ -1058,6 +1065,15 @@ export const MAIN_GLSL = `void main() {
|
|
|
1058
1065
|
float iesProfile;
|
|
1059
1066
|
vec3 iesAxis;
|
|
1060
1067
|
float cookie;
|
|
1068
|
+
/* The light's lighting channels, a mask: 1 unless it names others. See \`uLightIesAxis\`. */
|
|
1069
|
+
float lightChannels = 1.0;
|
|
1070
|
+
/*
|
|
1071
|
+
* **A rectangle in the froxel table** — one past the fixed arm's \`maxAreaLights\` — which
|
|
1072
|
+
* its record says with a negative radius, its reach negated. Its fields come out of the
|
|
1073
|
+
* fixture texels: sidedness, the right axis, the half extents and the up axis.
|
|
1074
|
+
*/
|
|
1075
|
+
bool clusteredArea = false;
|
|
1076
|
+
float areaTwoSided = 0.0;
|
|
1061
1077
|
if (clusteredArm) {
|
|
1062
1078
|
if (slot >= clusterLights) break;
|
|
1063
1079
|
/*
|
|
@@ -1076,6 +1092,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
1076
1092
|
lightPos = vec3(
|
|
1077
1093
|
uintBitsToFloat(rec0.x), uintBitsToFloat(rec0.y), uintBitsToFloat(rec0.z));
|
|
1078
1094
|
lightRadius = uintBitsToFloat(rec0.w);
|
|
1095
|
+
clusteredArea = lightRadius < 0.0;
|
|
1096
|
+
lightRadius = abs(lightRadius);
|
|
1079
1097
|
/*
|
|
1080
1098
|
* **Out of reach is decided on the first texel, before the rest of the record is read.**
|
|
1081
1099
|
* A froxel holds every light whose sphere touches its box, so most of the lights a fragment
|
|
@@ -1120,7 +1138,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
1120
1138
|
uvec4 rec2 = clusterTexel(record + 2);
|
|
1121
1139
|
uvec4 rec3 = clusterTexel(record + 3);
|
|
1122
1140
|
uvec4 rec4 = clusterTexel(record + 4);
|
|
1123
|
-
shadowSlot = int(uintBitsToFloat(rec2.x));
|
|
1141
|
+
shadowSlot = clusteredArea ? -1 : int(uintBitsToFloat(rec2.x));
|
|
1142
|
+
areaTwoSided = uintBitsToFloat(rec2.x);
|
|
1124
1143
|
lightDir = vec3(
|
|
1125
1144
|
uintBitsToFloat(rec2.y), uintBitsToFloat(rec2.z), uintBitsToFloat(rec2.w));
|
|
1126
1145
|
lightCone = vec2(uintBitsToFloat(rec3.x), uintBitsToFloat(rec3.y));
|
|
@@ -1130,6 +1149,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1130
1149
|
texel rather than being derived from the direction. */
|
|
1131
1150
|
iesAxis = vec3(
|
|
1132
1151
|
uintBitsToFloat(rec4.x), uintBitsToFloat(rec4.y), uintBitsToFloat(rec4.z));
|
|
1152
|
+
lightChannels = uintBitsToFloat(rec4.w);
|
|
1133
1153
|
}
|
|
1134
1154
|
}
|
|
1135
1155
|
} else {
|
|
@@ -1144,9 +1164,42 @@ export const MAIN_GLSL = `void main() {
|
|
|
1144
1164
|
lightDir = uLightDir[i];
|
|
1145
1165
|
lightCone = uLightCone[i];
|
|
1146
1166
|
iesProfile = uLightIesProfile[i];
|
|
1147
|
-
iesAxis = uLightIesAxis[i];
|
|
1167
|
+
iesAxis = uLightIesAxis[i].xyz;
|
|
1168
|
+
lightChannels = uLightIesAxis[i].w;
|
|
1148
1169
|
cookie = uLightCookie[i];
|
|
1149
1170
|
}
|
|
1171
|
+
/*
|
|
1172
|
+
* **A light shades a surface only where their channels meet**: the light's mask against the
|
|
1173
|
+
* material's. Both default to channel 1, so a scene naming none takes every light as before
|
|
1174
|
+
* — and **zero reads as that default on both sides**, so a pass that never writes the lane
|
|
1175
|
+
* loses no light: an unwritten uniform is zero, and zero here would be every lamp switched
|
|
1176
|
+
* off. A branch on a value read from a texture in the clustered arm, as the reach test above
|
|
1177
|
+
* is, and it skips only what the light would add; no derivative is taken past it.
|
|
1178
|
+
*/
|
|
1179
|
+
int litChannels = int(lightChannels);
|
|
1180
|
+
int surfaceChannels = int(uCutout.w);
|
|
1181
|
+
if (((litChannels == 0 ? 1 : litChannels) & (surfaceChannels == 0 ? 1 : surfaceChannels)) == 0) {
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
/*
|
|
1185
|
+
* **A clustered rectangle is shaded here and goes no further**: by the fixed loop's own body
|
|
1186
|
+
* (\`areaLight.ts\`), unshadowed — its occlusion is the fixed arm's, which is why the first
|
|
1187
|
+
* \`maxAreaLights\` stay there — and windowed to nothing at its reach, as a lamp is, so the
|
|
1188
|
+
* froxel it is binned into has no edge in the picture.
|
|
1189
|
+
*/
|
|
1190
|
+
if (clusteredArea) {
|
|
1191
|
+
float areaForm = areaSignedForm(n, lightPos, lightDir, iesAxis, lightCone);
|
|
1192
|
+
float areaWindow = clamp(1.0 - pow(length(lightPos - vWorldPos) / max(lightRadius, 1e-4), 4.0), 0.0, 1.0);
|
|
1193
|
+
areaForm = mix(max(0.0, areaForm), abs(areaForm), areaTwoSided);
|
|
1194
|
+
if (areaForm > 0.0) {
|
|
1195
|
+
areaLightAdd(
|
|
1196
|
+
lightPos, lightDir, iesAxis, lightCone, lightColor * (areaWindow * areaWindow), areaForm,
|
|
1197
|
+
1.0, vec3(1.0), n, albedo, metal, specColor, surfaceRoughness, modelled,
|
|
1198
|
+
lampOpen, lampShadowed
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1150
1203
|
/* A DriftLight field's own light carries a negative weight, and is shaded exactly only for the
|
|
1151
1204
|
share of this pixel the field does not stand in for. See \`driftLight.ts\`. */
|
|
1152
1205
|
if (lightWeight < 0.0) lightWeight = -lightWeight * (1.0 - driftShare);
|
|
@@ -1351,7 +1404,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1351
1404
|
* and the result is selected afterwards with a \`mix\` on a compare — which is arithmetic
|
|
1352
1405
|
* rather than control flow.
|
|
1353
1406
|
*
|
|
1354
|
-
* **And skipped outright where nothing loaded a profile**, behind \`
|
|
1407
|
+
* **And skipped outright where nothing loaded a profile**, behind \`uFixtureShape.x\`, which is
|
|
1355
1408
|
* one while the atlas holds only its row of ones. A uniform is provably uniform control
|
|
1356
1409
|
* flow, so this branch is not the one the rule forbids. It was paid on every scene until a
|
|
1357
1410
|
* 4K frame of 320 lamps and no shadows measured it at 0.7 ms of the main pass, rising to
|
|
@@ -1394,12 +1447,12 @@ export const MAIN_GLSL = `void main() {
|
|
|
1394
1447
|
if (iesRefLen > 1e-5) {
|
|
1395
1448
|
iesRef /= iesRefLen;
|
|
1396
1449
|
vec3 iesBinormal = cross(lightDir, iesRef);
|
|
1397
|
-
if (
|
|
1450
|
+
if (uFixtureShape.y > 1.0) {
|
|
1398
1451
|
/* \`atan\` answers -PI..PI and \`fract\` of a negative wraps forward, so this is 0..1
|
|
1399
1452
|
across the whole turn without a branch on the sign. */
|
|
1400
1453
|
iesTurns = fract(atan(dot(iesRay, iesBinormal), dot(iesRay, iesRef)) / TAU_IES);
|
|
1401
1454
|
}
|
|
1402
|
-
if (
|
|
1455
|
+
if (uFixtureShape.z > 0.0) {
|
|
1403
1456
|
/*
|
|
1404
1457
|
* **A perspective projection onto the plane one unit down the aim**, which is what a
|
|
1405
1458
|
* fixture actually throws: the mask grows with distance rather than being pasted on
|
|
@@ -1446,10 +1499,10 @@ export const MAIN_GLSL = `void main() {
|
|
|
1446
1499
|
*/
|
|
1447
1500
|
float photometric = 1.0;
|
|
1448
1501
|
if (fixtureProfiles) {
|
|
1449
|
-
float iesBase = iesRow * (
|
|
1502
|
+
float iesBase = iesRow * (uFixtureShape.y + 1.0);
|
|
1450
1503
|
/* The profiles are the atlas's bottom rows, below any cookies. */
|
|
1451
|
-
float iesTop = fixtureSize.y -
|
|
1452
|
-
float iesV = (iesTop + iesBase + iesTurns *
|
|
1504
|
+
float iesTop = fixtureSize.y - uFixtureShape.x;
|
|
1505
|
+
float iesV = (iesTop + iesBase + iesTurns * uFixtureShape.y + 0.5) / fixtureSize.y;
|
|
1453
1506
|
float iesGain = textureLod(uFixtureAtlas, vec2(iesU, iesV), 0.0).r;
|
|
1454
1507
|
photometric = mix(1.0, iesGain, step(0.0, iesProfile));
|
|
1455
1508
|
}
|
|
@@ -1586,22 +1639,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
1586
1639
|
vec3 up = uAreaLightUp[a];
|
|
1587
1640
|
vec2 halfSize = uAreaLightSize[a];
|
|
1588
1641
|
|
|
1589
|
-
/*
|
|
1590
|
-
|
|
1591
|
-
* makes the form factor positive there. The natural order — minus, plus, plus, minus — winds
|
|
1592
|
-
* the other way, and every one-sided light then clamped to zero and the frame was black.
|
|
1593
|
-
*/
|
|
1594
|
-
vec3 c0 = centre - right * halfSize.x + up * halfSize.y;
|
|
1595
|
-
vec3 c1 = centre + right * halfSize.x + up * halfSize.y;
|
|
1596
|
-
vec3 c2 = centre + right * halfSize.x - up * halfSize.y;
|
|
1597
|
-
vec3 c3 = centre - right * halfSize.x - up * halfSize.y;
|
|
1598
|
-
|
|
1599
|
-
/*
|
|
1600
|
-
* **The sign is the sidedness.** A one-sided rectangle takes the positive part and a
|
|
1601
|
-
* two-sided one the magnitude — no facing test, because the sign already answers it exactly
|
|
1602
|
-
* per fragment rather than once for the rectangle's centre.
|
|
1603
|
-
*/
|
|
1604
|
-
float signedForm = quadFormFactor(n, vWorldPos, c0, c1, c2, c3);
|
|
1642
|
+
/* The form factor and its sign, which is the sidedness: see \`areaSignedForm\`. */
|
|
1643
|
+
float signedForm = areaSignedForm(n, centre, right, up, halfSize);
|
|
1605
1644
|
float form = mix(max(0.0, signedForm), abs(signedForm), uAreaLightTwoSided[a]);
|
|
1606
1645
|
/* The form factor is linear in the normal, so the one seen from behind is its negation. */
|
|
1607
1646
|
float backForm = glassTransmission > 0.0
|
|
@@ -1716,55 +1755,11 @@ export const MAIN_GLSL = `void main() {
|
|
|
1716
1755
|
|
|
1717
1756
|
glassGlow += uAreaLightColor[a] * backForm * areaOccl * areaGlass;
|
|
1718
1757
|
if (form <= 0.0) continue;
|
|
1719
|
-
/*
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
* from its centre rather than its shape.
|
|
1725
|
-
*/
|
|
1726
|
-
if (modelled) {
|
|
1727
|
-
vec3 areaL = normalize(centre - vWorldPos);
|
|
1728
|
-
modelLight(areaL, 0.0, 0.0);
|
|
1729
|
-
vec3 areaModel = (mDiffuse * (form / max(dot(n, areaL), 1e-3)) + mSpecular * form)
|
|
1730
|
-
* uAreaLightColor[a];
|
|
1731
|
-
lampOpen += areaModel;
|
|
1732
|
-
lampShadowed += areaModel * areaOccl * areaGlass;
|
|
1733
|
-
continue;
|
|
1734
|
-
}
|
|
1735
|
-
/* Held back with the point lamps', and shadowed by the same term it always was. */
|
|
1736
|
-
vec3 areaDiffuse = albedo * uAreaLightColor[a] * form * (1.0 - metal);
|
|
1737
|
-
lampOpen += areaDiffuse;
|
|
1738
|
-
lampShadowed += areaDiffuse * areaOccl * areaGlass;
|
|
1739
|
-
|
|
1740
|
-
if (vSpecular > 0.0 || metal > 0.0) {
|
|
1741
|
-
/*
|
|
1742
|
-
* **The lobe integrated over the rectangle, rather than sampled at one point on it.**
|
|
1743
|
-
*
|
|
1744
|
-
* What stood here handed the closest point on the rectangle to \`sphereLobe\` and scaled it
|
|
1745
|
-
* by \`form\`, the *diffuse* form factor. Measured against a brute-force integral by
|
|
1746
|
-
* \`scripts/areaSpecular.mjs\`, that lost the reflection rather than blurring it: on
|
|
1747
|
-
* polished metal facing a softbox it returned 0.000233 where the integral is 0.9207, and
|
|
1748
|
-
* head-on on a rough surface it was 3.7 times too bright. Worst 287% either way; this is
|
|
1749
|
-
* 40.1% where the light is what the surface reflects and 79.0% for one overhead.
|
|
1750
|
-
*
|
|
1751
|
-
* \`quadCoverage\` says what fraction of the specular lobe the rectangle covers and the
|
|
1752
|
-
* environment BRDF says how much the surface returns, which is the same split the probe
|
|
1753
|
-
* path uses. **\`dfg.x + dfg.y\` and not \`envSpecularEnergy\`**, deliberately and against
|
|
1754
|
-
* the obvious argument: the compensation returns the multiply-scattered share, and every
|
|
1755
|
-
* direct lobe in this shader is single-scattering, so adding it here returns energy the
|
|
1756
|
-
* light never had. Measured at 211% against 40.1%.
|
|
1757
|
-
*/
|
|
1758
|
-
vec3 toEyeArea = normalize(uCameraPos - vWorldPos);
|
|
1759
|
-
float coverage = quadCoverage(n, vWorldPos, toEyeArea, surfaceRoughness, c0, c1, c2, c3);
|
|
1760
|
-
if (coverage > 0.0) {
|
|
1761
|
-
vec2 areaDfg = envBrdfApprox(max(dot(n, toEyeArea), 0.0), surfaceRoughness);
|
|
1762
|
-
vec3 areaSpec = specColor * areaDfg.x + vec3(areaDfg.y);
|
|
1763
|
-
vec3 areaHighlight = areaSpec * uAreaLightColor[a] * coverage;
|
|
1764
|
-
lampOpen += areaHighlight;
|
|
1765
|
-
lampShadowed += areaHighlight * areaOccl * areaGlass;
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1758
|
+
/* The diffuse, the model's answer or the lobe integrated over the rectangle: see areaLight.ts. */
|
|
1759
|
+
areaLightAdd(
|
|
1760
|
+
centre, right, up, halfSize, uAreaLightColor[a], form, areaOccl, areaGlass,
|
|
1761
|
+
n, albedo, metal, specColor, surfaceRoughness, modelled, lampOpen, lampShadowed
|
|
1762
|
+
);
|
|
1768
1763
|
}
|
|
1769
1764
|
|
|
1770
1765
|
/*
|
|
@@ -1906,10 +1901,10 @@ export const MAIN_GLSL = `void main() {
|
|
|
1906
1901
|
}
|
|
1907
1902
|
/* Pulse, flicker and fade from the layer's effects; exactly 1 where it names none. */
|
|
1908
1903
|
float fxGlow = fx4.z + fx5.x + fx5.w > 0.0 ? fxEmission(surfaceAt.z) : 1.0;
|
|
1909
|
-
lit += emissiveTint * emissiveMapped *
|
|
1904
|
+
lit += emissiveTint * emissiveMapped * uEmission.x * vEmissive * uEmission.y
|
|
1910
1905
|
* mix(1.0, min(lightShade, sunShade), EMISSIVE_SHADOW_SHARE) * fxGlow;
|
|
1911
1906
|
/* And a lit window's glow, which the scene's lit share already times: not gated on night. */
|
|
1912
|
-
lit += windowGlow *
|
|
1907
|
+
lit += windowGlow * uEmission.x;
|
|
1913
1908
|
|
|
1914
1909
|
#if NIGHT_EMISSIVE
|
|
1915
1910
|
/*
|
|
@@ -1942,9 +1937,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
1942
1937
|
// Arrival glow: a world-space box lit independently of the clock, so
|
|
1943
1938
|
// finishing a run reads the same at noon and at midnight. Costs one compare
|
|
1944
1939
|
// per fragment and no extra draw call — the box is a uniform, not geometry.
|
|
1945
|
-
if (
|
|
1946
|
-
vec3 inside = step(uHighlightMin, vWorldPos) * step(vWorldPos, uHighlightMax);
|
|
1947
|
-
lit += albedo *
|
|
1940
|
+
if (uHighlightMax.w > 0.0) {
|
|
1941
|
+
vec3 inside = step(uHighlightMin, vWorldPos) * step(vWorldPos, uHighlightMax.xyz);
|
|
1942
|
+
lit += albedo * uHighlightMax.w * inside.x * inside.y * inside.z;
|
|
1948
1943
|
}
|
|
1949
1944
|
}
|
|
1950
1945
|
|
|
@@ -2001,7 +1996,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
2001
1996
|
/*
|
|
2002
1997
|
* **Refraction: the scene behind this surface, bent and absorbed.**
|
|
2003
1998
|
*
|
|
2004
|
-
* The offset is along the
|
|
1999
|
+
* The offset is along the shading normal in screen space, which is the cheap approximation every
|
|
2005
2000
|
* real-time renderer ships rather than a ray traced through two interfaces of a solid. What it
|
|
2006
2001
|
* cannot do is show anything the frame had not already drawn, so a pane at the edge of the frame
|
|
2007
2002
|
* refracts what is beside it -- and the clamp below is what keeps that from being worse than
|
|
@@ -2036,7 +2031,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
2036
2031
|
* draw uses.
|
|
2037
2032
|
*/
|
|
2038
2033
|
if (uSeeThrough.x > 0.0 || glassTransmission > 0.0) {
|
|
2039
|
-
|
|
2034
|
+
/*
|
|
2035
|
+
* **Bent by the shading normal, so a normal map shimmers what is seen through it** — a heat
|
|
2036
|
+
* haze, a distortion sphere, a rippled pane. Until 4.8.6 this was the geometric normal and a
|
|
2037
|
+
* mapped surface refracted as a smooth lens. An unlit draw shaded no normal, so it reads the
|
|
2038
|
+
* map here, on a branch on uniforms alone, which keeps its derivatives legal.
|
|
2039
|
+
*/
|
|
2040
|
+
vec3 refractN = shadingNormal;
|
|
2041
|
+
if (uLightingEnabled == 0 && uNormalStrength > 0.0) refractN = normalMapped(refractN, surfaceAt);
|
|
2040
2042
|
vec3 refractV = normalize(uCameraPos - vWorldPos);
|
|
2041
2043
|
ivec2 snapSize = textureSize(uRefractScene, 0);
|
|
2042
2044
|
vec2 screenUv = gl_FragCoord.xy / vec2(snapSize);
|