@driftengine/core 4.5.0 → 4.6.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 +51 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +238 -121
- package/dist/render/backend/webgpu/buffers.d.ts +10 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +462 -201
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +300 -143
- package/src/render/backend/webgpu/buffers.ts +42 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/renderer.ts +540 -226
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -30,6 +30,29 @@
|
|
|
30
30
|
* the render read bicubically fills the gap. The history keeps colour and weight; the picture shown
|
|
31
31
|
* is it sharpened.
|
|
32
32
|
*
|
|
33
|
+
* **And the temporal resolve's anti-flicker, where the caller carries its records** (`temporalAa.ts`
|
|
34
|
+
* says why it works and what it gives up). A thread finer than a render texel is caught in some
|
|
35
|
+
* phases of the jitter and missed in others, and in a phase that misses it the nine texels can all
|
|
36
|
+
* miss it: the box collapses onto what is behind, the history is clipped out, and a still picture
|
|
37
|
+
* flashes at every fine edge each period. A still camera over a still scene gathers every pixel the
|
|
38
|
+
* same way each period, so each pixel keeps a record of this frame's gathered luma over the period,
|
|
39
|
+
* and once two periods running have repeated its box widens by its own spread and it keeps more of
|
|
40
|
+
* its history. **The record stays at the pixel rather than following the reprojection**, because the
|
|
41
|
+
* proof is only ever for a pixel that has not moved; it starts over wherever the history did not
|
|
42
|
+
* survive, and is forgotten in proportion to motion.
|
|
43
|
+
*
|
|
44
|
+
* **And a still pixel whose depth has been seen to swing is not a disocclusion.** A rail finer than
|
|
45
|
+
* a texel in front of a wall is caught in some phases and missed in others, so last frame can hold
|
|
46
|
+
* the wall where this frame holds the rail with nothing moved — and the depth test threw the history
|
|
47
|
+
* away, leaving the record nothing to keep. So each pixel also keeps the largest relative miss of
|
|
48
|
+
* last frame's depth it has seen lately, kept whatever the test says, and while still its tolerance
|
|
49
|
+
* widens to `DEPTH_SWING_WIDTH` of it — the temporal resolve's answer to a crack of light round a
|
|
50
|
+
* door, as a held peak rather than its running mean: a rail caught in one phase of eighteen misses
|
|
51
|
+
* once a period, and a mean has worn that miss down to a thirtieth by the next. **What it gives up**
|
|
52
|
+
* is the depth test's word at such a pixel while still: a surface arriving there inside the swing
|
|
53
|
+
* keeps the history, and the neighbourhood clip, which the unproven sample of an arrival leaves
|
|
54
|
+
* unwidened, is what takes it out.
|
|
55
|
+
*
|
|
33
56
|
* **Conventions.** Images are rows from `v = 0` upward. Depths are clip depths in the matrices'
|
|
34
57
|
* convention, OpenGL's here — the nearest is the smallest — and the renderer converts its reversed
|
|
35
58
|
* buffer before a sample reaches this arithmetic, as the temporal resolve already does.
|
|
@@ -37,6 +60,7 @@
|
|
|
37
60
|
import { clipToBox, rgbToYCoCg, varianceBox, yCoCgToRgb } from './clamp.js';
|
|
38
61
|
import { disocclusionWeight, normalFromPositions, planarity, surfaceSlope, worldPositionFromDepth, } from './disocclusion.js';
|
|
39
62
|
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.js';
|
|
63
|
+
import { TEMPORAL_FLICKER, flickerBlend, flickerRule, flickerWidening, freshFlicker, nextFlicker, stillness, } from '../temporalAa.js';
|
|
40
64
|
/**
|
|
41
65
|
* A tenth of each frame kept, as the temporal resolve keeps; a box of a standard deviation and a
|
|
42
66
|
* quarter; a light sharpen. At two thirds of the output a side.
|
|
@@ -98,6 +122,12 @@ export function reconstructionWeight(dx, dy) {
|
|
|
98
122
|
* pixel away.
|
|
99
123
|
*/
|
|
100
124
|
export const CONFIDENT_WEIGHT = 1;
|
|
125
|
+
/**
|
|
126
|
+
* How many of its held depth swings a still pixel's depth test tolerates. The peak halves over a
|
|
127
|
+
* period, so the frame before the next once-a-period miss holds just over half of it: tolerating
|
|
128
|
+
* twice the peak passes that miss outright, and three leaves the same room again.
|
|
129
|
+
*/
|
|
130
|
+
export const DEPTH_SWING_WIDTH = 3;
|
|
101
131
|
const SAMPLE = new Float32Array(3);
|
|
102
132
|
const GATHERED = new Float64Array(3);
|
|
103
133
|
const HISTORY = new Float32Array(4);
|
|
@@ -113,6 +143,37 @@ const UP = new Float64Array(3);
|
|
|
113
143
|
const NORMAL = new Float64Array(3);
|
|
114
144
|
const PREVIOUS_NORMAL = new Float64Array(3);
|
|
115
145
|
const CLIP = new Float64Array(4);
|
|
146
|
+
/** The last frame's reprojection, derived once a frame rather than once a pixel. */
|
|
147
|
+
const REPROJECTION = new Float64Array(16);
|
|
148
|
+
let reprojected = null;
|
|
149
|
+
/** `frame.reprojection`, or the product of its two matrices, multiplied once per frame. */
|
|
150
|
+
function reprojectionOf(frame) {
|
|
151
|
+
if (frame.reprojection !== undefined)
|
|
152
|
+
return frame.reprojection;
|
|
153
|
+
if (reprojected !== frame) {
|
|
154
|
+
reprojected = frame;
|
|
155
|
+
const a = frame.previousViewProj;
|
|
156
|
+
const b = frame.inverseViewProj;
|
|
157
|
+
for (let c = 0; c < 4; c += 1) {
|
|
158
|
+
for (let r = 0; r < 4; r += 1) {
|
|
159
|
+
let sum = 0;
|
|
160
|
+
for (let k = 0; k < 4; k += 1)
|
|
161
|
+
sum += a[k * 4 + r] * b[c * 4 + k];
|
|
162
|
+
REPROJECTION[c * 4 + r] = sum;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return REPROJECTION;
|
|
167
|
+
}
|
|
168
|
+
const HELD = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
169
|
+
const CARRIED = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
170
|
+
/** How far, in output pixels, the last `historyWeight` found its surface had moved. */
|
|
171
|
+
let moved = 0;
|
|
172
|
+
/** How far, relative, last frame's depth missed the one expected there; -1 where nothing was held. */
|
|
173
|
+
let missed = -1;
|
|
174
|
+
/** The rule for the last period asked for, kept: one is made per period, not per pixel. */
|
|
175
|
+
let rulePhases = 0;
|
|
176
|
+
let rule = TEMPORAL_FLICKER;
|
|
116
177
|
function clampTexel(value, size) {
|
|
117
178
|
return value < 0 ? 0 : value >= size ? size - 1 : value;
|
|
118
179
|
}
|
|
@@ -184,7 +245,8 @@ function normalAt(depths, inverse, jitter, eye, width, height, x, y, out) {
|
|
|
184
245
|
* was behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it
|
|
185
246
|
* is the same surface. Reads `HISTORY`; writes the history's colour there.
|
|
186
247
|
*/
|
|
187
|
-
function historyWeight(frame, u, v, dx, dy) {
|
|
248
|
+
function historyWeight(frame, u, v, dx, dy, swing) {
|
|
249
|
+
missed = -1;
|
|
188
250
|
if (!frame.hasHistory)
|
|
189
251
|
return 0;
|
|
190
252
|
const rw = frame.renderWidth;
|
|
@@ -199,27 +261,45 @@ function historyWeight(frame, u, v, dx, dy) {
|
|
|
199
261
|
expected = frame.motion[flagAt + 2];
|
|
200
262
|
}
|
|
201
263
|
else {
|
|
202
|
-
|
|
264
|
+
/*
|
|
265
|
+
* **Through one matrix, last frame's view-projection times this frame's inverse**, rather than
|
|
266
|
+
* out to a world position and back. The two are the same arithmetic in exact numbers; in single
|
|
267
|
+
* precision the round trip is not the identity — a near plane a quarter of a metre from a far one
|
|
268
|
+
* five kilometres off — and a still camera measured a few hundredths of a pixel of motion growing
|
|
269
|
+
* out from the middle of the frame, which crept every still history and kept every pixel from
|
|
270
|
+
* the anti-flicker's stillness. The product is taken where the precision is (`reprojection`),
|
|
271
|
+
* and for a still camera it is the identity.
|
|
272
|
+
*/
|
|
273
|
+
const du = (dx + 0.5 - frame.jitter[0]) / rw;
|
|
274
|
+
const dv = (dy + 0.5 - frame.jitter[1]) / rh;
|
|
275
|
+
const x = du * 2 - 1;
|
|
276
|
+
const y = dv * 2 - 1;
|
|
277
|
+
const z = frame.depth[dy * rw + dx];
|
|
278
|
+
const inverse = frame.inverseViewProj;
|
|
279
|
+
/* The homogeneous w of the world position: none, or behind the eye, is no surface. */
|
|
280
|
+
const hw = inverse[3] * x +
|
|
281
|
+
inverse[7] * y +
|
|
282
|
+
inverse[11] * z +
|
|
283
|
+
inverse[15];
|
|
284
|
+
if (!(hw > 1e-12))
|
|
203
285
|
return 0;
|
|
204
|
-
|
|
205
|
-
const m = frame.previousViewProj;
|
|
286
|
+
const m = reprojectionOf(frame);
|
|
206
287
|
for (let r = 0; r < 4; r += 1) {
|
|
207
288
|
CLIP[r] =
|
|
208
|
-
m[r] *
|
|
209
|
-
m[4 + r] *
|
|
210
|
-
m[8 + r] *
|
|
289
|
+
m[r] * x +
|
|
290
|
+
m[4 + r] * y +
|
|
291
|
+
m[8 + r] * z +
|
|
211
292
|
m[12 + r];
|
|
212
293
|
}
|
|
213
294
|
/*
|
|
214
295
|
* A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no
|
|
215
296
|
* such depth, so it needs no refusal of its own here.
|
|
216
297
|
*/
|
|
217
|
-
expected = CLIP[3];
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
mu = (CLIP[0] / expected) * 0.5 + 0.5 - du;
|
|
221
|
-
mv = (CLIP[1] / expected) * 0.5 + 0.5 - dv;
|
|
298
|
+
expected = CLIP[3] / hw;
|
|
299
|
+
mu = (CLIP[0] / CLIP[3]) * 0.5 + 0.5 - du;
|
|
300
|
+
mv = (CLIP[1] / CLIP[3]) * 0.5 + 0.5 - dv;
|
|
222
301
|
}
|
|
302
|
+
moved = Math.hypot(mu * frame.outputWidth, mv * frame.outputHeight);
|
|
223
303
|
const pu = u + mu;
|
|
224
304
|
const pv = v + mv;
|
|
225
305
|
if (!(pu >= 0 && pu <= 1 && pv >= 0 && pv <= 1))
|
|
@@ -284,14 +364,18 @@ function historyWeight(frame, u, v, dx, dy) {
|
|
|
284
364
|
NORMAL[2] * PREVIOUS_NORMAL[2];
|
|
285
365
|
normalDot = 1 - planar * (1 - cosine);
|
|
286
366
|
}
|
|
287
|
-
|
|
367
|
+
if (held > 0 && expected > 0)
|
|
368
|
+
missed = Math.abs(held - expected) / expected;
|
|
369
|
+
/* A still pixel's own depth swing, as wide as the anti-flicker's box: see the header. */
|
|
370
|
+
const swung = swing > 0 ? DEPTH_SWING_WIDTH * swing * stillness(moved) : 0;
|
|
371
|
+
const trust = disocclusionWeight(expected, held, Math.hypot(mu, mv), normalDot, frame.disocclusion, slope + swung);
|
|
288
372
|
return HISTORY[3] * trust;
|
|
289
373
|
}
|
|
290
374
|
/**
|
|
291
375
|
* One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
|
|
292
|
-
* four values into `out`.
|
|
376
|
+
* four values into `out` — and, where the frame carries records, the pixel's next one into `record`.
|
|
293
377
|
*/
|
|
294
|
-
export function resolvePixel(frame, ox, oy, out) {
|
|
378
|
+
export function resolvePixel(frame, ox, oy, out, record, swingOut) {
|
|
295
379
|
const rw = frame.renderWidth;
|
|
296
380
|
const rh = frame.renderHeight;
|
|
297
381
|
const ow = frame.outputWidth;
|
|
@@ -339,8 +423,57 @@ export function resolvePixel(frame, ox, oy, out) {
|
|
|
339
423
|
for (let c = 0; c < 3; c += 1)
|
|
340
424
|
GATHERED[c] = GATHERED[c] / gathered;
|
|
341
425
|
/* 3 and 4. The history, and the weight it still deserves. */
|
|
342
|
-
const
|
|
343
|
-
const
|
|
426
|
+
const alpha = frame.quality.alpha;
|
|
427
|
+
const records = frame.flicker;
|
|
428
|
+
const anti = records !== undefined && records !== null;
|
|
429
|
+
if (anti) {
|
|
430
|
+
const phases = frame.flickerPhases ?? 0;
|
|
431
|
+
if (phases !== rulePhases) {
|
|
432
|
+
rulePhases = phases;
|
|
433
|
+
rule = phases > 0 ? flickerRule(phases) : TEMPORAL_FLICKER;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const swings = frame.depthSwing;
|
|
437
|
+
const swing = anti && swings !== undefined && swings !== null ? swings[oy * ow + ox] : 0;
|
|
438
|
+
const weight = historyWeight(frame, u, v, dx, dy, swing);
|
|
439
|
+
let cap = (1 - alpha) / alpha;
|
|
440
|
+
/* The anti-flicker: a proven pixel's box widened by its spread, and more of its history kept. */
|
|
441
|
+
if (anti) {
|
|
442
|
+
const sample = GATHERED[0] * 0.2126 +
|
|
443
|
+
GATHERED[1] * 0.7152 +
|
|
444
|
+
GATHERED[2] * 0.0722;
|
|
445
|
+
const at = (oy * ow + ox) * 4;
|
|
446
|
+
if (weight > 0) {
|
|
447
|
+
HELD.spread = records[at];
|
|
448
|
+
HELD.sum = records[at + 1];
|
|
449
|
+
HELD.mean = records[at + 2];
|
|
450
|
+
HELD.repeated = records[at + 3];
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
freshFlicker(HELD, sample, rule);
|
|
454
|
+
}
|
|
455
|
+
const still = weight > 0 ? stillness(moved) : 0;
|
|
456
|
+
const widen = flickerWidening(HELD, sample, still, rule);
|
|
457
|
+
for (let c = 0; c < 3; c += 1) {
|
|
458
|
+
LOW[c] = LOW[c] - widen;
|
|
459
|
+
HIGH[c] = HIGH[c] + widen;
|
|
460
|
+
}
|
|
461
|
+
const keep = flickerBlend(1 - alpha, HELD, sample, still, rule);
|
|
462
|
+
cap = keep / (1 - keep);
|
|
463
|
+
nextFlicker(CARRIED, HELD, sample, still, frame.periodStart === true, rule);
|
|
464
|
+
if (record !== undefined) {
|
|
465
|
+
record[0] = CARRIED.spread;
|
|
466
|
+
record[1] = CARRIED.sum;
|
|
467
|
+
record[2] = CARRIED.mean;
|
|
468
|
+
record[3] = CARRIED.repeated;
|
|
469
|
+
}
|
|
470
|
+
/* The peak carried on, halving over a period, whatever the test said; none where nothing was
|
|
471
|
+
held to miss. */
|
|
472
|
+
if (swingOut !== undefined) {
|
|
473
|
+
swingOut[0] = missed >= 0 ? Math.max(missed, swing * 0.5 ** rule.memory) : 0;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const kept = Math.min(cap, weight);
|
|
344
477
|
/* 5. Clipped, mixed by weight, and the gaps filled from the render. */
|
|
345
478
|
const total = kept + gathered;
|
|
346
479
|
if (kept > 0) {
|
|
@@ -359,6 +492,8 @@ export function resolvePixel(frame, ox, oy, out) {
|
|
|
359
492
|
out[3] = total;
|
|
360
493
|
}
|
|
361
494
|
const TAP = new Float32Array(4);
|
|
495
|
+
const RECORD = new Float32Array(4);
|
|
496
|
+
const SWING = new Float32Array(1);
|
|
362
497
|
const CENTRE = new Float32Array(4);
|
|
363
498
|
const AROUND = new Float32Array(12);
|
|
364
499
|
const SHOWN = new Float32Array(3);
|
|
@@ -370,15 +505,21 @@ const OFFSETS = [
|
|
|
370
505
|
];
|
|
371
506
|
/**
|
|
372
507
|
* The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
|
|
373
|
-
* `shown`, three — the history sharpened against the four direct neighbours it has just been given
|
|
508
|
+
* `shown`, three — the history sharpened against the four direct neighbours it has just been given —
|
|
509
|
+
* and, where the frame carries records, the next ones into `records`, four floats a pixel, and the
|
|
510
|
+
* next depth swings into `swings`, one.
|
|
374
511
|
*/
|
|
375
|
-
export function resolveFrame(frame, history, shown) {
|
|
512
|
+
export function resolveFrame(frame, history, shown, records, swings) {
|
|
376
513
|
const ow = frame.outputWidth;
|
|
377
514
|
const oh = frame.outputHeight;
|
|
378
515
|
for (let y = 0; y < oh; y += 1) {
|
|
379
516
|
for (let x = 0; x < ow; x += 1) {
|
|
380
|
-
resolvePixel(frame, x, y, TAP);
|
|
517
|
+
resolvePixel(frame, x, y, TAP, RECORD, SWING);
|
|
381
518
|
history.set(TAP, (y * ow + x) * 4);
|
|
519
|
+
if (records !== undefined)
|
|
520
|
+
records.set(RECORD, (y * ow + x) * 4);
|
|
521
|
+
if (swings !== undefined)
|
|
522
|
+
swings[y * ow + x] = SWING[0];
|
|
382
523
|
}
|
|
383
524
|
}
|
|
384
525
|
for (let y = 0; y < oh; y += 1) {
|
|
@@ -29,6 +29,10 @@ export function recordingGl(options = {}) {
|
|
|
29
29
|
const programFragments = new WeakMap();
|
|
30
30
|
const calls = [];
|
|
31
31
|
const constants = {
|
|
32
|
+
/* The canvas's size, as a real context reports its drawing buffer; a function here made every
|
|
33
|
+
jitter and every per-pixel offset divided by it a NaN. */
|
|
34
|
+
drawingBufferWidth: 640,
|
|
35
|
+
drawingBufferHeight: 480,
|
|
32
36
|
ACTIVE_UNIFORMS: 0x8b86,
|
|
33
37
|
ACTIVE_ATTRIBUTES: 0x8b89,
|
|
34
38
|
COMPILE_STATUS: 0x8b81,
|
|
@@ -293,6 +293,8 @@ export declare class SceneTarget {
|
|
|
293
293
|
/** The projection's x and y scales, which turn a world radius into a screen one. */
|
|
294
294
|
readonly projScale: Float32Array;
|
|
295
295
|
readonly invProjection: Float32Array;
|
|
296
|
+
/** Where the frame's jitter put the occlusion, in uv: see the shader's `uAoOffset`. */
|
|
297
|
+
readonly offset?: Float32Array;
|
|
296
298
|
}, bloom?: {
|
|
297
299
|
readonly strength: number;
|
|
298
300
|
/** In scene units, which is why this wants `keepsRange`. See `BloomPass`. */
|
|
@@ -834,6 +834,8 @@ export class SceneTarget {
|
|
|
834
834
|
gl.bindTexture(gl.TEXTURE_2D, aoTexture ?? scene);
|
|
835
835
|
gl.uniform1i(this.uniforms['uAo'] ?? null, 2);
|
|
836
836
|
gl.uniform1f(this.uniforms['uAoStrength'] ?? null, aoTexture === null ? 0 : (ao?.strength ?? 0));
|
|
837
|
+
const offset = ao?.offset;
|
|
838
|
+
gl.uniform2f(this.uniforms['uAoOffset'] ?? null, offset?.[0] ?? 0, offset?.[1] ?? 0);
|
|
837
839
|
/* Bloom on its own unit, and the placeholder for the same reason as the one above: a
|
|
838
840
|
driver may fetch a texture's descriptor before it evaluates the branch that skips the
|
|
839
841
|
read, and an incomplete texture is not a thing to hand it. */
|
|
@@ -63,14 +63,15 @@ ${CHANNEL_BEND}
|
|
|
63
63
|
* uniforms declared after every other, so no field of the plain variant moves and a binder written
|
|
64
64
|
* against it stays right for this one; and two outputs after vLightPosition, which keeps location 0.
|
|
65
65
|
*/
|
|
66
|
-
layout(location = 5) in
|
|
66
|
+
layout(location = 5) in vec3 aUv;
|
|
67
67
|
uniform vec2 uUvScale;
|
|
68
68
|
uniform float uAlphaCutout;
|
|
69
69
|
#endif
|
|
70
70
|
|
|
71
71
|
out vec4 vLightPosition;
|
|
72
72
|
#if CUTOUT
|
|
73
|
-
|
|
73
|
+
/* The texture coordinate and, in z, the texture-array layer the face wears. */
|
|
74
|
+
out vec3 vUv;
|
|
74
75
|
flat out float vAlphaCutout;
|
|
75
76
|
#endif
|
|
76
77
|
#if GLASS
|
|
@@ -102,7 +103,7 @@ void main() {
|
|
|
102
103
|
vLightPosition = uLightViewProj * vec4(bent, world.w);
|
|
103
104
|
gl_Position = vLightPosition;
|
|
104
105
|
#if CUTOUT
|
|
105
|
-
vUv = aUv * uUvScale;
|
|
106
|
+
vUv = vec3(aUv.xy * uUvScale, aUv.z);
|
|
106
107
|
vAlphaCutout = uAlphaCutout;
|
|
107
108
|
#endif
|
|
108
109
|
#if GLASS
|
|
@@ -168,16 +169,17 @@ void main() {
|
|
|
168
169
|
export const DEPTH_CUTOUT_FRAG = `#version 300 es
|
|
169
170
|
precision highp float;
|
|
170
171
|
in vec4 vLightPosition;
|
|
171
|
-
in
|
|
172
|
+
in vec3 vUv;
|
|
172
173
|
flat in float vAlphaCutout;
|
|
173
174
|
|
|
174
175
|
uniform highp sampler2DArray uPreviousShadowMap;
|
|
175
176
|
uniform int uPeelShadowLayer;
|
|
176
|
-
uniform highp
|
|
177
|
+
uniform highp sampler2DArray uCutoutMap;
|
|
177
178
|
${CUTOUT_COVERAGE_GLSL}
|
|
178
179
|
void main() {
|
|
179
180
|
/* Credited for its mip level as the surface's own test is, so a leaf casts the shape it draws. */
|
|
180
|
-
|
|
181
|
+
vec3 at = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
182
|
+
float alpha = cutoutAlpha(texture(uCutoutMap, at).a, vUv.xy * vec2(textureSize(uCutoutMap, 0).xy));
|
|
181
183
|
if (uPeelShadowLayer != 0) {
|
|
182
184
|
vec3 p = vLightPosition.xyz / vLightPosition.w;
|
|
183
185
|
vec2 uv = p.xy * 0.5 + 0.5;
|
|
@@ -237,19 +239,20 @@ ${GLASS_TINT_BODY}
|
|
|
237
239
|
export const GLASS_TINT_CUTOUT_FRAG = `#version 300 es
|
|
238
240
|
precision highp float;
|
|
239
241
|
in vec4 vLightPosition;
|
|
240
|
-
in
|
|
242
|
+
in vec3 vUv;
|
|
241
243
|
flat in float vAlphaCutout;
|
|
242
244
|
in vec3 vGlassWorld;
|
|
243
245
|
|
|
244
246
|
uniform vec4 uGlassPane;
|
|
245
247
|
uniform vec4 uGlassLight;
|
|
246
|
-
uniform highp
|
|
248
|
+
uniform highp sampler2DArray uCutoutMap;
|
|
247
249
|
${CUTOUT_COVERAGE_GLSL}
|
|
248
250
|
out vec4 outTint;
|
|
249
251
|
|
|
250
252
|
void main() {
|
|
251
253
|
/* First, in uniform control flow, as the depth cutout samples it. */
|
|
252
|
-
|
|
254
|
+
vec3 at = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
255
|
+
float alpha = cutoutAlpha(texture(uCutoutMap, at).a, vUv.xy * vec2(textureSize(uCutoutMap, 0).xy));
|
|
253
256
|
${GLASS_TINT_BODY}
|
|
254
257
|
if (alpha < vAlphaCutout) discard;
|
|
255
258
|
}
|
|
@@ -26,4 +26,4 @@
|
|
|
26
26
|
* `textureLod` throughout, by the 2026-08-07 rule: this is reached through branches no compiler can
|
|
27
27
|
* prove uniform. Both samplers are declared `highp` because GLSL ES gives 3D samplers no default.
|
|
28
28
|
*/
|
|
29
|
-
export declare const DRIFT_LIGHT_GLSL = "\n/** x how much of the summed light is in the frame (0 off), y the radius, z the band, w the scale. */\nuniform vec4 uDriftLight;\n/** xyz the first
|
|
29
|
+
export declare const DRIFT_LIGHT_GLSL = "\n/** x how much of the summed light is in the frame (0 off), y the radius, z the band, w the scale. */\nuniform vec4 uDriftLight;\n/** xyz the first sample; w metres between samples \u2014 a third of a brick, or negated for a dense volume. */\nuniform vec4 uDriftLightOrigin;\nuniform highp usampler3D uDriftLightIndex; // wgsl:share shadow\nuniform highp sampler3D uDriftLightAtlas;\n\n/** The share of this pixel's field light the volume stands in for: 0 inside the radius, 1 past it. */\nfloat driftLightShare(vec3 world) {\n if (uDriftLight.x <= 0.0) return 0.0;\n float away = distance(world, uCameraPos);\n return smoothstep(uDriftLight.y - uDriftLight.z, uDriftLight.y, away) * uDriftLight.x;\n}\n\n/**\n * A sample's light and direction turned into the light on a surface facing `n`: Lambert for a\n * single light, a quarter for light arriving from everywhere (see `bake.ts`). One statement for\n * both kinds of volume.\n */\nvec3 driftLightFacing(vec4 light, vec4 toward, vec3 n) {\n vec3 arriving = light.rgb / light.a;\n vec3 from = toward.xyz / light.a;\n float facing = max(dot(n, from), 0.0) + (1.0 - min(length(from), 1.0)) * 0.25;\n return arriving * facing;\n}\n\n/**\n * The same from a dense volume: one sample a texel, the light in the lower half of y and its\n * direction in the upper. A lookup kept inside the samples never filters across the halves.\n */\nvec3 driftLightDense(vec3 world, vec3 n, float spacing) {\n ivec3 size = textureSize(uDriftLightAtlas, 0);\n vec3 dims = vec3(float(size.x), float(size.y / 2), float(size.z));\n vec3 at = (world + n * (0.5 * spacing) - uDriftLightOrigin.xyz) / spacing;\n if (any(lessThan(at, vec3(0.0))) || any(greaterThan(at, dims - 1.0))) return vec3(0.0);\n vec3 uvw = (at + 0.5) / vec3(dims.x, dims.y * 2.0, dims.z);\n vec4 light = textureLod(uDriftLightAtlas, uvw, 0.0);\n if (light.a < 1e-3) return vec3(0.0);\n vec4 toward = textureLod(uDriftLightAtlas, uvw + vec3(0.0, 0.5, 0.0), 0.0);\n return driftLightFacing(light, toward, n);\n}\n\n/** The summed light arriving on a surface at `world` facing `n`. A negative spacing is dense. */\nvec3 driftLightIrradiance(vec3 world, vec3 n) {\n float spacing = uDriftLightOrigin.w;\n if (spacing < 0.0) return driftLightDense(world, n, -spacing);\n vec3 at = (world + n * (0.5 * spacing) - uDriftLightOrigin.xyz) / (3.0 * spacing);\n ivec3 cell = ivec3(floor(at));\n if (any(lessThan(cell, ivec3(0))) || any(greaterThanEqual(cell, textureSize(uDriftLightIndex, 0)))) {\n return vec3(0.0);\n }\n uint id = texelFetch(uDriftLightIndex, cell, 0).r;\n if (id == 0u) return vec3(0.0);\n /* A brick is eight texels across (its light block, then its direction block) and four up and deep. */\n ivec3 size = textureSize(uDriftLightAtlas, 0);\n int brick = int(id) - 1;\n int across = size.x / 8;\n int down = size.y / 4;\n vec3 corner = vec3(\n float((brick % across) * 8),\n float(((brick / across) % down) * 4),\n float((brick / (across * down)) * 4)\n );\n vec3 texel = 1.0 / vec3(size);\n vec3 inside = (at - vec3(cell)) * 3.0 + 0.5;\n vec4 light = textureLod(uDriftLightAtlas, (corner + inside) * texel, 0.0);\n if (light.a < 1e-3) return vec3(0.0);\n vec4 toward = textureLod(uDriftLightAtlas, (corner + vec3(4.0, 0.0, 0.0) + inside) * texel, 0.0);\n return driftLightFacing(light, toward, n);\n}\n";
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
export const DRIFT_LIGHT_GLSL = /* glsl */ `
|
|
30
30
|
/** x how much of the summed light is in the frame (0 off), y the radius, z the band, w the scale. */
|
|
31
31
|
uniform vec4 uDriftLight;
|
|
32
|
-
/** xyz the first
|
|
32
|
+
/** xyz the first sample; w metres between samples — a third of a brick, or negated for a dense volume. */
|
|
33
33
|
uniform vec4 uDriftLightOrigin;
|
|
34
34
|
uniform highp usampler3D uDriftLightIndex; // wgsl:share shadow
|
|
35
35
|
uniform highp sampler3D uDriftLightAtlas;
|
|
@@ -41,9 +41,38 @@ float driftLightShare(vec3 world) {
|
|
|
41
41
|
return smoothstep(uDriftLight.y - uDriftLight.z, uDriftLight.y, away) * uDriftLight.x;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* A sample's light and direction turned into the light on a surface facing \`n\`: Lambert for a
|
|
46
|
+
* single light, a quarter for light arriving from everywhere (see \`bake.ts\`). One statement for
|
|
47
|
+
* both kinds of volume.
|
|
48
|
+
*/
|
|
49
|
+
vec3 driftLightFacing(vec4 light, vec4 toward, vec3 n) {
|
|
50
|
+
vec3 arriving = light.rgb / light.a;
|
|
51
|
+
vec3 from = toward.xyz / light.a;
|
|
52
|
+
float facing = max(dot(n, from), 0.0) + (1.0 - min(length(from), 1.0)) * 0.25;
|
|
53
|
+
return arriving * facing;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The same from a dense volume: one sample a texel, the light in the lower half of y and its
|
|
58
|
+
* direction in the upper. A lookup kept inside the samples never filters across the halves.
|
|
59
|
+
*/
|
|
60
|
+
vec3 driftLightDense(vec3 world, vec3 n, float spacing) {
|
|
61
|
+
ivec3 size = textureSize(uDriftLightAtlas, 0);
|
|
62
|
+
vec3 dims = vec3(float(size.x), float(size.y / 2), float(size.z));
|
|
63
|
+
vec3 at = (world + n * (0.5 * spacing) - uDriftLightOrigin.xyz) / spacing;
|
|
64
|
+
if (any(lessThan(at, vec3(0.0))) || any(greaterThan(at, dims - 1.0))) return vec3(0.0);
|
|
65
|
+
vec3 uvw = (at + 0.5) / vec3(dims.x, dims.y * 2.0, dims.z);
|
|
66
|
+
vec4 light = textureLod(uDriftLightAtlas, uvw, 0.0);
|
|
67
|
+
if (light.a < 1e-3) return vec3(0.0);
|
|
68
|
+
vec4 toward = textureLod(uDriftLightAtlas, uvw + vec3(0.0, 0.5, 0.0), 0.0);
|
|
69
|
+
return driftLightFacing(light, toward, n);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The summed light arriving on a surface at \`world\` facing \`n\`. A negative spacing is dense. */
|
|
45
73
|
vec3 driftLightIrradiance(vec3 world, vec3 n) {
|
|
46
74
|
float spacing = uDriftLightOrigin.w;
|
|
75
|
+
if (spacing < 0.0) return driftLightDense(world, n, -spacing);
|
|
47
76
|
vec3 at = (world + n * (0.5 * spacing) - uDriftLightOrigin.xyz) / (3.0 * spacing);
|
|
48
77
|
ivec3 cell = ivec3(floor(at));
|
|
49
78
|
if (any(lessThan(cell, ivec3(0))) || any(greaterThanEqual(cell, textureSize(uDriftLightIndex, 0)))) {
|
|
@@ -66,10 +95,6 @@ vec3 driftLightIrradiance(vec3 world, vec3 n) {
|
|
|
66
95
|
vec4 light = textureLod(uDriftLightAtlas, (corner + inside) * texel, 0.0);
|
|
67
96
|
if (light.a < 1e-3) return vec3(0.0);
|
|
68
97
|
vec4 toward = textureLod(uDriftLightAtlas, (corner + vec3(4.0, 0.0, 0.0) + inside) * texel, 0.0);
|
|
69
|
-
|
|
70
|
-
vec3 from = toward.xyz / light.a;
|
|
71
|
-
/* Lambert for a single light, a quarter for light arriving from everywhere: see \`bake.ts\`. */
|
|
72
|
-
float facing = max(dot(n, from), 0.0) + (1.0 - min(length(from), 1.0)) * 0.25;
|
|
73
|
-
return arriving * facing;
|
|
98
|
+
return driftLightFacing(light, toward, n);
|
|
74
99
|
}
|
|
75
100
|
`;
|
|
@@ -13,6 +13,7 @@ import { SKINNING_GLSL } from '../skinning.js';
|
|
|
13
13
|
import { SURFACE_GLSL } from './surface.js';
|
|
14
14
|
import { MAIN_GLSL } from './main.js';
|
|
15
15
|
import { DRIFT_LIGHT_GLSL } from './driftLight.js';
|
|
16
|
+
import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.js';
|
|
16
17
|
import { CHANNEL_ATTRIBUTE, CHANNEL_BEND } from '../vertexChannel.js';
|
|
17
18
|
import { FULL_LIGHT_BUDGET } from '../../uniformVectorBudget.js';
|
|
18
19
|
/**
|
|
@@ -27,7 +28,7 @@ layout(location = 1) in vec3 aNormal;
|
|
|
27
28
|
layout(location = 2) in vec3 aColor;
|
|
28
29
|
layout(location = 3) in float aEmissive;
|
|
29
30
|
layout(location = 4) in float aSpecular;
|
|
30
|
-
layout(location = 5) in
|
|
31
|
+
layout(location = 5) in vec3 aUv;
|
|
31
32
|
layout(location = 6) in vec3 aEmissiveColor;
|
|
32
33
|
layout(location = 7) in float aRoughness;
|
|
33
34
|
layout(location = 8) in float aGrain;
|
|
@@ -145,7 +146,7 @@ out vec3 vWorldPos;
|
|
|
145
146
|
out float vEmissive;
|
|
146
147
|
out float vSpecular;
|
|
147
148
|
out vec4 vLightPos;
|
|
148
|
-
out
|
|
149
|
+
out vec3 vUv;
|
|
149
150
|
out vec3 vEmissiveColor;
|
|
150
151
|
out float vRoughness;
|
|
151
152
|
out float vGrain;
|
|
@@ -242,7 +243,7 @@ void main() {
|
|
|
242
243
|
vColor = aColor * tint;
|
|
243
244
|
vEmissive = aEmissive;
|
|
244
245
|
vSpecular = aSpecular;
|
|
245
|
-
vUv = aUv * uUvScale;
|
|
246
|
+
vUv = vec3(aUv.xy * uUvScale, aUv.z);
|
|
246
247
|
vEmissiveColor = aEmissiveColor;
|
|
247
248
|
vRoughness = aRoughness;
|
|
248
249
|
vGrain = aGrain;
|
|
@@ -386,6 +387,7 @@ export function flatFrag(options) {
|
|
|
386
387
|
/* Unconditional for the same reason, and called only where a cutoff was asked for. */
|
|
387
388
|
CUTOUT_COVERAGE_GLSL,
|
|
388
389
|
DRIFT_LIGHT_GLSL,
|
|
390
|
+
SURFACE_EFFECTS_GLSL,
|
|
389
391
|
MAIN_GLSL,
|
|
390
392
|
].join('\n'), {
|
|
391
393
|
POINT_SHADOWS: options.pointShadows,
|