@navaramap/three-csm 0.1.1 → 0.2.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/LICENSE +1 -1
- package/dist/index.d.ts +8 -19
- package/dist/index.js +26 -26
- package/dist/index.umd.cjs +4 -4
- package/package.json +3 -3
- package/src/viewSpaceShadowReceive.test.ts +11 -0
- package/src/viewSpaceShadowReceive.ts +9 -0
package/LICENSE
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -29,9 +29,14 @@ import { WebGLRenderer } from 'three';
|
|
|
29
29
|
* left untouched, as are shaders compiled for the shadow-map depth pass
|
|
30
30
|
* (`USE_SHADOWMAP_DEPTH` — they never sample shadows and would upload the
|
|
31
31
|
* uniform before the shadow maps for the frame are rendered).
|
|
32
|
+
*
|
|
33
|
+
* The fragment shader gets `NVR_VIEW_SPACE_SHADOW` defined, so code that
|
|
34
|
+
* computes shadow coordinates there (`chunks/ground_shadow_coord_fragment`)
|
|
35
|
+
* takes the same path.
|
|
32
36
|
*/
|
|
33
37
|
export declare function applyViewSpaceShadowReceive(shader: {
|
|
34
38
|
vertexShader: string;
|
|
39
|
+
fragmentShader: string;
|
|
35
40
|
defines?: Record<string, unknown>;
|
|
36
41
|
uniforms: Record<string, {
|
|
37
42
|
value: unknown;
|
|
@@ -248,29 +253,13 @@ declare class MaterialStates {
|
|
|
248
253
|
|
|
249
254
|
export declare function overrideFragmentShaderForCSM(fragmentShader: string): string;
|
|
250
255
|
|
|
251
|
-
/**
|
|
252
|
-
* Uniform holding per-cascade directional shadow matrices composed against
|
|
253
|
-
* the render camera's world matrix on the CPU: `light.shadow.matrix *
|
|
254
|
-
* camera.matrixWorld` (f64).
|
|
255
|
-
*
|
|
256
|
-
* Sampling shadow maps with `directionalShadowMatrix * worldPosition` loses
|
|
257
|
-
* precision on a globe: the absolute ECEF world position (~6.4e6 m) and the
|
|
258
|
-
* shadow matrix translation cancel in f32 on the GPU, and because the CSM
|
|
259
|
-
* cascade matrices follow the camera the rounding error changes every frame —
|
|
260
|
-
* received shadows jitter while the geometry itself stays put.
|
|
261
|
-
*
|
|
262
|
-
* Folding the camera's world matrix in instead lets the shader use the
|
|
263
|
-
* view-space position `mvPosition`, which is precise by construction for
|
|
264
|
-
* every positioning mode: RTE meshes compute it from camera-relative
|
|
265
|
-
* coordinates, RTC meshes get it from the CPU-composed f64 modelViewMatrix,
|
|
266
|
-
* and instancing folds instanceMatrix into it. Shadow sampling then reuses
|
|
267
|
-
* the exact quantity that renders stably, so it can never jitter relative to
|
|
268
|
-
* the geometry — and a single patch covers every mesh type.
|
|
269
|
-
*/
|
|
270
256
|
export declare type ShadowMatricesViewUniform = {
|
|
271
257
|
value: Matrix4[];
|
|
272
258
|
};
|
|
273
259
|
|
|
274
260
|
export declare function splitFrustum(mode: FrustumSplitMode, count: number, near: number, far: number, lambda?: number, result?: number[]): number[];
|
|
275
261
|
|
|
262
|
+
/** Defined in the fragment shader of a material patched for view-space shadows. */
|
|
263
|
+
export declare const VIEW_SPACE_SHADOW_DEFINE = "NVR_VIEW_SPACE_SHADOW";
|
|
264
|
+
|
|
276
265
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -138,24 +138,24 @@ function M(e) {
|
|
|
138
138
|
}
|
|
139
139
|
//#endregion
|
|
140
140
|
//#region src/viewSpaceShadowReceive.ts
|
|
141
|
-
var N = "\n#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n// Per-cascade shadow matrices composed with the render camera's world matrix\n// on the CPU in f64 (shadow.matrix * camera.matrixWorld). Multiplied with the\n// view-space position instead of the absolute world position to avoid f32\n// cancellation at ECEF magnitudes.\n//\n// Only the CSM cascade lights (the first CSM_CASCADE_COUNT shadow indices,\n// matching the ordering assumption of the CSM fragment shader) get these\n// matrices; additional shadow-casting directional lights keep the stock\n// directionalShadowMatrix path. Outside a CSM material the define is absent —\n// treat every directional shadow as camera-composed then.\n#ifndef CSM_CASCADE_COUNT\n#define CSM_CASCADE_COUNT NUM_DIR_LIGHT_SHADOWS\n#endif\nuniform mat4 nvrCsmShadowMatrixView[ CSM_CASCADE_COUNT ];\n#endif\n";
|
|
142
|
-
function
|
|
141
|
+
var N = "NVR_VIEW_SPACE_SHADOW", P = "\n#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n// Per-cascade shadow matrices composed with the render camera's world matrix\n// on the CPU in f64 (shadow.matrix * camera.matrixWorld). Multiplied with the\n// view-space position instead of the absolute world position to avoid f32\n// cancellation at ECEF magnitudes.\n//\n// Only the CSM cascade lights (the first CSM_CASCADE_COUNT shadow indices,\n// matching the ordering assumption of the CSM fragment shader) get these\n// matrices; additional shadow-casting directional lights keep the stock\n// directionalShadowMatrix path. Outside a CSM material the define is absent —\n// treat every directional shadow as camera-composed then.\n#ifndef CSM_CASCADE_COUNT\n#define CSM_CASCADE_COUNT NUM_DIR_LIGHT_SHADOWS\n#endif\nuniform mat4 nvrCsmShadowMatrixView[ CSM_CASCADE_COUNT ];\n#endif\n";
|
|
142
|
+
function F(e, t, n) {
|
|
143
143
|
if (!e.includes(t)) throw Error(`Failed to replace "${t}" in shader code. The pattern was not found.`);
|
|
144
144
|
return e.replace(t, n);
|
|
145
145
|
}
|
|
146
|
-
function
|
|
146
|
+
function I(e, t) {
|
|
147
147
|
if (e.defines?.USE_SHADOWMAP_DEPTH || !e.vertexShader.includes("#include <shadowmap_vertex>")) return;
|
|
148
|
-
e.uniforms.nvrCsmShadowMatrixView = t
|
|
149
|
-
let n =
|
|
150
|
-
n =
|
|
148
|
+
e.uniforms.nvrCsmShadowMatrixView = t, e.fragmentShader = `#define ${N}\n${e.fragmentShader}`;
|
|
149
|
+
let n = F(h.shadowmap_vertex, "shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );", "#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT\n shadowWorldPosition = vec4( mvPosition.xyz + ( viewMatrix * vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0.0 ) ).xyz, 1.0 );\n #else\n shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n #endif");
|
|
150
|
+
n = F(n, "vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;", "#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT\n vDirectionalShadowCoord[ i ] = nvrCsmShadowMatrixView[ i ] * shadowWorldPosition;\n #else\n vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n #endif"), e.vertexShader = F(F(e.vertexShader, "#include <shadowmap_pars_vertex>", `#include <shadowmap_pars_vertex>\n${P}`), "#include <shadowmap_vertex>", n);
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function L(e, t, n) {
|
|
153
153
|
n.length = e.length;
|
|
154
154
|
for (let r = 0; r < e.length; r++) (n[r] ?? (n[r] = new u())).multiplyMatrices(e[r].shadow.matrix, t);
|
|
155
155
|
}
|
|
156
156
|
//#endregion
|
|
157
157
|
//#region src/MaterialStates.ts
|
|
158
|
-
var
|
|
158
|
+
var R = class {
|
|
159
159
|
map = /* @__PURE__ */ new Map();
|
|
160
160
|
setup(e, t) {
|
|
161
161
|
if (this.map.has(e)) return e;
|
|
@@ -165,7 +165,7 @@ var L = class {
|
|
|
165
165
|
let r = (e, r) => {
|
|
166
166
|
n.parameters = e, t.captureRenderer(r);
|
|
167
167
|
let i = t.cascades, a = t.mainCamera.near, o = Math.min(t.mainCamera.far, t.far), s = e.uniforms;
|
|
168
|
-
s.csmCascades = { value: i }, s.csmNear = { value: a }, s.csmFar = { value: o }, e.fragmentShader = M(e.fragmentShader),
|
|
168
|
+
s.csmCascades = { value: i }, s.csmNear = { value: a }, s.csmFar = { value: o }, e.fragmentShader = M(e.fragmentShader), I(e, t.shadowMatricesViewUniform);
|
|
169
169
|
}, i = e.onBeforeCompile.bind(e), a = e.customProgramCacheKey.bind(e);
|
|
170
170
|
Object.prototype.hasOwnProperty.call(e, "hasOwnProperty") && (n.originalHandler = i), e.customProgramCacheKey = () => i.toString() + a(), e.onBeforeCompile = (...t) => {
|
|
171
171
|
let [n] = t;
|
|
@@ -201,12 +201,12 @@ var L = class {
|
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
203
|
Math.PI / 180, 180 / Math.PI;
|
|
204
|
-
function
|
|
204
|
+
function z(e, t, n) {
|
|
205
205
|
return (1 - n) * e + n * t;
|
|
206
206
|
}
|
|
207
207
|
//#endregion
|
|
208
208
|
//#region src/splitFrustum.ts
|
|
209
|
-
var
|
|
209
|
+
var B = {
|
|
210
210
|
uniform: (e, t, n, r, i = []) => {
|
|
211
211
|
for (let r = 0; r < e; ++r) i[r] = (t + (n - t) * (r + 1) / e) / n;
|
|
212
212
|
return i.length = e, i;
|
|
@@ -218,17 +218,17 @@ var z = {
|
|
|
218
218
|
practical: (e, t, n, r = .5, i = []) => {
|
|
219
219
|
for (let a = 0; a < e; ++a) {
|
|
220
220
|
let o = (t + (n - t) * (a + 1) / e) / n, s = t * (n / t) ** ((a + 1) / e) / n;
|
|
221
|
-
i[a] =
|
|
221
|
+
i[a] = z(o, s, r);
|
|
222
222
|
}
|
|
223
223
|
return i.length = e, i;
|
|
224
224
|
}
|
|
225
|
-
},
|
|
226
|
-
function
|
|
227
|
-
return
|
|
225
|
+
}, V = B;
|
|
226
|
+
function H(e, t, n, r, i, a = []) {
|
|
227
|
+
return B[e](t, n, r, i, a);
|
|
228
228
|
}
|
|
229
229
|
//#endregion
|
|
230
230
|
//#region src/CascadedShadowMaps.ts
|
|
231
|
-
var
|
|
231
|
+
var U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new _(), G = /*#__PURE__*/ new u(), K = /*#__PURE__*/ new u(), q = /*#__PURE__*/ new S(), J = /*#__PURE__*/ new e(), Y = {
|
|
232
232
|
cascadeCount: 4,
|
|
233
233
|
mapSize: 2048,
|
|
234
234
|
far: 5e3,
|
|
@@ -237,11 +237,11 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
237
237
|
margin: 200,
|
|
238
238
|
fade: !0,
|
|
239
239
|
disableLastCascadeCutoff: !1
|
|
240
|
-
},
|
|
240
|
+
}, X = class {
|
|
241
241
|
mainCamera;
|
|
242
242
|
needsUpdateFrusta = !0;
|
|
243
243
|
directionalLights = new y();
|
|
244
|
-
materialStates = new
|
|
244
|
+
materialStates = new R();
|
|
245
245
|
mainFrustum = new S();
|
|
246
246
|
cascadedFrusta = [];
|
|
247
247
|
splits = [];
|
|
@@ -263,7 +263,7 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
263
263
|
return n();
|
|
264
264
|
} };
|
|
265
265
|
let { cascadeCount: r, mapSize: i, far: a, mode: o, lambda: s, margin: c, fade: l, disableLastCascadeCutoff: u } = {
|
|
266
|
-
...
|
|
266
|
+
...Y,
|
|
267
267
|
...t
|
|
268
268
|
};
|
|
269
269
|
this._far = a, this._mode = o, this._lambda = s, this._margin = c, this._fade = l, this._disableLastCascadeCutoff = u, this.cascadeCount = r, this.mapSize = i;
|
|
@@ -279,7 +279,7 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
279
279
|
}
|
|
280
280
|
refreshShadowMatricesView() {
|
|
281
281
|
let e = this._renderer?.info.render.frame ?? -1;
|
|
282
|
-
(e === -1 || e !== this._shadowMatricesViewFrame) && (this._shadowMatricesViewFrame = e,
|
|
282
|
+
(e === -1 || e !== this._shadowMatricesViewFrame) && (this._shadowMatricesViewFrame = e, L(this.directionalLights.cascadedLights, this.mainCamera.matrixWorld, this._shadowMatricesView));
|
|
283
283
|
}
|
|
284
284
|
rollbackMaterial(e) {
|
|
285
285
|
return this.materialStates.rollback(e);
|
|
@@ -292,7 +292,7 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
292
292
|
}
|
|
293
293
|
updateCascades() {
|
|
294
294
|
let e = this.cascadeCount, t = this.splits;
|
|
295
|
-
|
|
295
|
+
H(this.mode, e, this.mainCamera.near, Math.min(this.mainCamera.far, this.far), this.lambda, t), this.mainFrustum.setFromCamera(this.mainCamera, this.far), this.mainFrustum.split(t, this.cascadedFrusta);
|
|
296
296
|
let n = this.cascades;
|
|
297
297
|
for (let r = 0; r < e; ++r) (n[r] ?? (n[r] = new g())).set(t[r - 1] ?? 0, t[r] ?? 0);
|
|
298
298
|
this.disableLastCascadeCutoff && (n[e - 1].y = Infinity), n.length = e;
|
|
@@ -318,11 +318,11 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
318
318
|
}
|
|
319
319
|
update() {
|
|
320
320
|
this.needsUpdateFrusta && (this.needsUpdateFrusta = !1, this.updateFrusta());
|
|
321
|
-
let e = this.directionalLights, t =
|
|
321
|
+
let e = this.directionalLights, t = U.copy(e.direction).normalize(), n = G.lookAt(new _(), t, p.DEFAULT_UP), r = K.multiplyMatrices(K.copy(n).invert(), this.mainCamera.matrixWorld), i = this.margin, a = this.mapSize, o = this.cascadedFrusta, s = e.cascadedLights;
|
|
322
322
|
for (let e = 0; e < o.length; ++e) {
|
|
323
|
-
let { near: c, far: l } =
|
|
323
|
+
let { near: c, far: l } = q.copy(o[e]).applyMatrix4(r), u = J.makeEmpty();
|
|
324
324
|
for (let e = 0; e < 4; e++) u.expandByPoint(c[e]), u.expandByPoint(l[e]);
|
|
325
|
-
let d = u.getCenter(
|
|
325
|
+
let d = u.getCenter(W);
|
|
326
326
|
d.z = u.max.z + i;
|
|
327
327
|
let f = s[e], { left: p, right: m, top: h, bottom: g } = f.shadow.camera, _ = (m - p) / a, v = (h - g) / a;
|
|
328
328
|
d.x = Math.round(d.x / _) * _, d.y = Math.round(d.y / v) * v, d.applyMatrix4(n), f.position.copy(d), f.target.position.copy(d), f.target.position.add(t);
|
|
@@ -442,7 +442,7 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
442
442
|
for (let n = 0; n < t.length; ++n) t[n].shadow.blurSamples = e;
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
|
-
},
|
|
445
|
+
}, Z = class extends s {
|
|
446
446
|
csm;
|
|
447
447
|
displayFrustum = !0;
|
|
448
448
|
displayPlanes = !0;
|
|
@@ -515,4 +515,4 @@ var H = /*#__PURE__*/ new _(), U = /*#__PURE__*/ new _(), W = /*#__PURE__*/ new
|
|
|
515
515
|
}
|
|
516
516
|
};
|
|
517
517
|
//#endregion
|
|
518
|
-
export {
|
|
518
|
+
export { Z as CSMHelper, y as CascadedDirectionalLights, X as CascadedShadowMaps, N as VIEW_SPACE_SHADOW_DEFINE, I as applyViewSpaceShadowReceive, Y as cascadedShadowMapsOptionsDefaults, L as composeViewSpaceShadowMatrices, V as frustumSplitFunctions, M as overrideFragmentShaderForCSM, H as splitFrustum };
|
package/dist/index.umd.cjs
CHANGED
|
@@ -331,7 +331,7 @@ void defaultLights() {
|
|
|
331
331
|
${p(u)}
|
|
332
332
|
#endif // CSM
|
|
333
333
|
#endif // NUM_DIR_LIGHTS > 0 && defined(RE_Direct)
|
|
334
|
-
`;function h({source:e,startLine:t,endLine:n,replacement:r}){let i=`\n${t}\n`,a=e.indexOf(i);if(a<0)return e;let o=`\n${n}\n`,s=e.indexOf(o,a+i.length);return s<0?e:e.slice(0,a+1)+r+e.slice(s+o.length-1)}function g(e){return e.replace(`#include <lights_pars_begin>`,f).replace(`#include <lights_fragment_begin>`,h({source:t.ShaderChunk.lights_fragment_begin,startLine:`#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )`,endLine:`#endif`,replacement:m}))}var _=`
|
|
334
|
+
`;function h({source:e,startLine:t,endLine:n,replacement:r}){let i=`\n${t}\n`,a=e.indexOf(i);if(a<0)return e;let o=`\n${n}\n`,s=e.indexOf(o,a+i.length);return s<0?e:e.slice(0,a+1)+r+e.slice(s+o.length-1)}function g(e){return e.replace(`#include <lights_pars_begin>`,f).replace(`#include <lights_fragment_begin>`,h({source:t.ShaderChunk.lights_fragment_begin,startLine:`#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )`,endLine:`#endif`,replacement:m}))}var _=`NVR_VIEW_SPACE_SHADOW`,v=`
|
|
335
335
|
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
336
336
|
// Per-cascade shadow matrices composed with the render camera's world matrix
|
|
337
337
|
// on the CPU in f64 (shadow.matrix * camera.matrixWorld). Multiplied with the
|
|
@@ -348,12 +348,12 @@ void defaultLights() {
|
|
|
348
348
|
#endif
|
|
349
349
|
uniform mat4 nvrCsmShadowMatrixView[ CSM_CASCADE_COUNT ];
|
|
350
350
|
#endif
|
|
351
|
-
`;function
|
|
351
|
+
`;function y(e,t,n){if(!e.includes(t))throw Error(`Failed to replace "${t}" in shader code. The pattern was not found.`);return e.replace(t,n)}function b(e,n){if(e.defines?.USE_SHADOWMAP_DEPTH||!e.vertexShader.includes(`#include <shadowmap_vertex>`))return;e.uniforms.nvrCsmShadowMatrixView=n,e.fragmentShader=`#define ${_}\n${e.fragmentShader}`;let r=y(t.ShaderChunk.shadowmap_vertex,`shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );`,`#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
352
352
|
shadowWorldPosition = vec4( mvPosition.xyz + ( viewMatrix * vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0.0 ) ).xyz, 1.0 );
|
|
353
353
|
#else
|
|
354
354
|
shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );
|
|
355
|
-
#endif`);r=
|
|
355
|
+
#endif`);r=y(r,`vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;`,`#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
356
356
|
vDirectionalShadowCoord[ i ] = nvrCsmShadowMatrixView[ i ] * shadowWorldPosition;
|
|
357
357
|
#else
|
|
358
358
|
vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;
|
|
359
|
-
#endif`),e.vertexShader=v(v(e.vertexShader,`#include <shadowmap_pars_vertex>`,`#include <shadowmap_pars_vertex>\n${_}`),`#include <shadowmap_vertex>`,r)}function b(e,n,r){r.length=e.length;for(let i=0;i<e.length;i++)(r[i]??(r[i]=new t.Matrix4)).multiplyMatrices(e[i].shadow.matrix,n)}var x=class{map=new Map;setup(e,t){if(this.map.has(e))return e;e.userData.defines??={},e.userData.defines.CSM=1,e.userData.defines.CSM_CASCADE_COUNT=t.cascadeCount,t.fade&&(e.userData.defines.CSM_FADE=1);let n={};this.map.set(e,n);let r=(e,r)=>{n.parameters=e,t.captureRenderer(r);let i=t.cascades,a=t.mainCamera.near,o=Math.min(t.mainCamera.far,t.far),s=e.uniforms;s.csmCascades={value:i},s.csmNear={value:a},s.csmFar={value:o},e.fragmentShader=g(e.fragmentShader),y(e,t.shadowMatricesViewUniform)},i=e.onBeforeCompile.bind(e),a=e.customProgramCacheKey.bind(e);Object.prototype.hasOwnProperty.call(e,`hasOwnProperty`)&&(n.originalHandler=i),e.customProgramCacheKey=()=>i.toString()+a(),e.onBeforeCompile=(...t)=>{let[n]=t;n.defines??={},Object.assign(n.defines,e.userData.defines),i.apply(e,t),r(...t)};let o=()=>{this.map.delete(e),e.removeEventListener(`dispose`,o)};return e.addEventListener(`dispose`,o),e}update(e){let t=e.cascadeCount,n=e.fade,r=e.cascades,i=e.mainCamera.near,a=Math.min(e.mainCamera.far,e.far);for(let[e,o]of this.map.entries()){let s=e.userData.defines;if(s!=null){let r=!1;s.CSM_FADE!=null!==n&&(n?s.CSM_FADE=1:delete s.CSM_FADE,r=!0),s.CSM_CASCADE_COUNT!==t&&(s.CSM_CASCADE_COUNT=t,r=!0),r&&(e.needsUpdate=!0)}let c=o.parameters?.uniforms;c!=null&&(c.csmCascades.value=r,c.csmNear.value=i,c.csmFar.value=a)}}rollback(e){let t=this.map.get(e);return t==null?e:this.rollbackToState(e,t)}rollbackToState(e,{parameters:t,originalHandler:n}){return t!=null&&(delete t.uniforms.csmCascades,delete t.uniforms.csmNear,delete t.uniforms.csmFar,delete t.uniforms.nvrCsmShadowMatrixView),n==null?delete e.onBeforeCompile:e.onBeforeCompile=n,e.userData.defines!=null&&(delete e.userData.defines.CSM,delete e.userData.defines.CSM_CASCADE_COUNT,delete e.userData.defines.CSM_FADE),e.needsUpdate=!0,this.map.delete(e),e}dispose(){for(let[e,t]of this.map)this.rollbackToState(e,t);this.map.clear()}};Math.PI/180,180/Math.PI;function S(e,t,n){return(1-n)*e+n*t}var C={uniform:(e,t,n,r,i=[])=>{for(let r=0;r<e;++r)i[r]=(t+(n-t)*(r+1)/e)/n;return i.length=e,i},logarithmic:(e,t,n,r,i=[])=>{for(let r=0;r<e;++r)i[r]=t*(n/t)**((r+1)/e)/n;return i.length=e,i},practical:(e,t,n,r=.5,i=[])=>{for(let a=0;a<e;++a){let o=(t+(n-t)*(a+1)/e)/n,s=t*(n/t)**((a+1)/e)/n;i[a]=S(o,s,r)}return i.length=e,i}},w=C;function T(e,t,n,r,i,a=[]){return C[e](t,n,r,i,a)}var E=new t.Vector3,D=new t.Vector3,O=new t.Matrix4,k=new t.Matrix4,A=new o,j=new t.Box3,M={cascadeCount:4,mapSize:2048,far:5e3,mode:`practical`,lambda:.5,margin:200,fade:!0,disableLastCascadeCutoff:!1},N=class{mainCamera;needsUpdateFrusta=!0;directionalLights=new r;materialStates=new x;mainFrustum=new o;cascadedFrusta=[];splits=[];cascades=[];_far;_mode;_lambda;_margin;_fade;_disableLastCascadeCutoff;_shadowMatricesView=[];_shadowMatricesViewFrame=-1;_renderer=null;shadowMatricesViewUniform;constructor(e,t){this.mainCamera=e;let n=()=>(this.refreshShadowMatricesView(),this._shadowMatricesView);this.shadowMatricesViewUniform={get value(){return n()}};let{cascadeCount:r,mapSize:i,far:a,mode:o,lambda:s,margin:c,fade:l,disableLastCascadeCutoff:u}={...M,...t};this._far=a,this._mode=o,this._lambda=s,this._margin=c,this._fade=l,this._disableLastCascadeCutoff=u,this.cascadeCount=r,this.mapSize=i}dispose(){this.materialStates.dispose(),this.directionalLights.dispose()}setupMaterial(e){return this.materialStates.setup(e,this)}captureRenderer(e){this._renderer??=e}refreshShadowMatricesView(){let e=this._renderer?.info.render.frame??-1;(e===-1||e!==this._shadowMatricesViewFrame)&&(this._shadowMatricesViewFrame=e,b(this.directionalLights.cascadedLights,this.mainCamera.matrixWorld,this._shadowMatricesView))}rollbackMaterial(e){return this.materialStates.rollback(e)}setupMaterials(e){for(let t=0;t<e.length;++t)this.materialStates.setup(e[t],this);return()=>{for(let t=0;t<e.length;++t)this.materialStates.rollback(e[t])}}updateCascades(){let e=this.cascadeCount,n=this.splits;T(this.mode,e,this.mainCamera.near,Math.min(this.mainCamera.far,this.far),this.lambda,n),this.mainFrustum.setFromCamera(this.mainCamera,this.far),this.mainFrustum.split(n,this.cascadedFrusta);let r=this.cascades;for(let i=0;i<e;++i)(r[i]??(r[i]=new t.Vector2)).set(n[i-1]??0,n[i]??0);this.disableLastCascadeCutoff&&(r[e-1].y=1/0),r.length=e}getFrustumRadius(e){let t=e.near,n=e.far,r=Math.max(n[0].distanceTo(n[2]),n[0].distanceTo(t[2]));if(this.fade){let e=this.mainCamera,t=e.near,i=Math.min(e.far,this.far),a=n[0].z/(i-t);r+=.25*a**2*(i-t)}return r*.5}updateShadowBounds(){let e=this.cascadedFrusta,t=this.directionalLights.cascadedLights;a(e.length===t.length);for(let n=0;n<e.length;++n){let e=this.getFrustumRadius(this.cascadedFrusta[n]),r=t[n].shadow.camera;r.left=-e,r.right=e,r.top=e,r.bottom=-e,r.near=0,r.far=e*2+this.margin,r.updateProjectionMatrix()}}updateFrusta(){this.updateCascades(),this.updateShadowBounds(),this.materialStates.update(this)}update(){this.needsUpdateFrusta&&(this.needsUpdateFrusta=!1,this.updateFrusta());let e=this.directionalLights,n=E.copy(e.direction).normalize(),r=O.lookAt(new t.Vector3,n,t.Object3D.DEFAULT_UP),i=k.multiplyMatrices(k.copy(r).invert(),this.mainCamera.matrixWorld),a=this.margin,o=this.mapSize,s=this.cascadedFrusta,c=e.cascadedLights;for(let e=0;e<s.length;++e){let{near:t,far:l}=A.copy(s[e]).applyMatrix4(i),u=j.makeEmpty();for(let e=0;e<4;e++)u.expandByPoint(t[e]),u.expandByPoint(l[e]);let d=u.getCenter(D);d.z=u.max.z+a;let f=c[e],{left:p,right:m,top:h,bottom:g}=f.shadow.camera,_=(m-p)/o,v=(h-g)/o;d.x=Math.round(d.x/_)*_,d.y=Math.round(d.y/v)*v,d.applyMatrix4(r),f.position.copy(d),f.target.position.copy(d),f.target.position.add(n)}}get cascadeCount(){return this.directionalLights.cascadedLights.length}set cascadeCount(e){e!==this.cascadeCount&&(this.directionalLights.setCount(e),this.needsUpdateFrusta=!0)}get mapSize(){return this.directionalLights.mainLight.shadow.mapSize.width}set mapSize(e){if(e!==this.mapSize){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n){let r=t[n].shadow;r.mapSize.width=e,r.mapSize.height=e,r.map!=null&&(r.map.dispose(),r.map=null)}}}get far(){return this._far}set far(e){e!==this._far&&(this._far=e,this.needsUpdateFrusta=!0)}get mode(){return this._mode}set mode(e){e!==this._mode&&(this._mode=e,this.needsUpdateFrusta=!0)}get lambda(){return this._lambda}set lambda(e){e!==this._lambda&&(this._lambda=e,this.needsUpdateFrusta=!0)}get margin(){return this._margin}set margin(e){e!==this._margin&&(this._margin=e,this.needsUpdateFrusta=!0)}get fade(){return this._fade}set fade(e){e!==this._fade&&(this._fade=e,this.needsUpdateFrusta=!0)}get disableLastCascadeCutoff(){return this._disableLastCascadeCutoff}set disableLastCascadeCutoff(e){e!==this._disableLastCascadeCutoff&&(this._disableLastCascadeCutoff=e,this.needsUpdateFrusta=!0)}get intensity(){return this.directionalLights.mainLight.intensity}set intensity(e){if(e!==this.intensity){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].intensity=e}}get shadowIntensity(){return this.directionalLights.mainLight.shadow.intensity}set shadowIntensity(e){if(e!==this.shadowIntensity){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.intensity=e}}get color(){return this.directionalLights.mainLight.color}set color(e){e!==this.color&&this.directionalLights.mainLight.color.copy(e)}get bias(){return this.directionalLights.mainLight.shadow.bias}set bias(e){if(e!==this.bias){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.bias=e}}get normalBias(){return this.directionalLights.mainLight.shadow.normalBias}set normalBias(e){if(e!==this.normalBias){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.normalBias=e}}get radius(){return this.directionalLights.mainLight.shadow.radius}set radius(e){if(e!==this.radius){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.radius=e}}get blurSamples(){return this.directionalLights.mainLight.shadow.blurSamples}set blurSamples(e){if(e!==this.blurSamples){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.blurSamples=e}}};e.CSMHelper=class extends t.Group{csm;displayFrustum=!0;displayPlanes=!0;displayShadowBounds=!0;frustumLines;cascadeLines=[];cascadePlanes=[];shadowLines=[];constructor(e){super(),this.csm=e;let n=new Uint16Array([0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7]),r=new Float32Array(24),i=new t.BufferGeometry;i.setIndex(new t.BufferAttribute(n,1)),i.setAttribute(`position`,new t.BufferAttribute(r,3,!1));let a=new t.LineSegments(i,new t.LineBasicMaterial);this.add(a),this.frustumLines=a}updateVisibility(){let e=this.displayFrustum,t=this.displayPlanes,n=this.displayShadowBounds,r=this.frustumLines,i=this.cascadeLines,a=this.cascadePlanes,o=this.shadowLines;for(let r=0,s=i.length;r<s;r++){let s=i[r],c=a[r],l=o[r];s.visible=e,c.visible=e&&t,l.visible=n}r.visible=e}update(){let e=this.csm,n=e.mainCamera,r=e.cascadeCount,i=e.mainFrustum,a=e.cascadedFrusta,o=e.directionalLights.cascadedLights,s=this.frustumLines.geometry.getAttribute(`position`),c=this.cascadeLines,l=this.cascadePlanes,u=this.shadowLines;for(this.position.copy(n.position),this.quaternion.copy(n.quaternion),this.scale.copy(n.scale),this.updateMatrixWorld(!0);c.length>r;){let e=c.pop(),t=l.pop(),n=u.pop();e&&this.remove(e),t&&this.remove(t),n&&this.remove(n)}for(;c.length<r;){let e=new t.Box3Helper(new t.Box3,new t.Color(16777215)),n=new t.MeshBasicMaterial({transparent:!0,opacity:.1,depthWrite:!1,side:t.DoubleSide}),r=new t.Mesh(new t.PlaneGeometry,n),i=new t.Group,a=new t.Box3Helper(new t.Box3,new t.Color(16776960));i.add(a),this.add(e),this.add(r),this.add(i),c.push(e),l.push(r),u.push(i)}for(let e=0;e<r;e++){let t=a[e],n=o[e].shadow.camera,r=t.far,i=c[e],s=l[e],d=u[e],f=d.children[0];i.box.min.copy(r[2]),i.box.max.copy(r[0]),i.box.max.z+=1e-4,s.position.addVectors(r[0],r[2]),s.position.multiplyScalar(.5),s.scale.subVectors(r[0],r[2]),s.scale.z=1e-4,this.remove(d),d.position.copy(n.position),d.quaternion.copy(n.quaternion),d.scale.copy(n.scale),d.updateMatrixWorld(!0),this.attach(d),f.box.min.set(n.bottom,n.left,-n.far),f.box.max.set(n.top,n.right,-n.near)}let d=i.near,f=i.far;s.setXYZ(0,f[0].x,f[0].y,f[0].z),s.setXYZ(1,f[3].x,f[3].y,f[3].z),s.setXYZ(2,f[2].x,f[2].y,f[2].z),s.setXYZ(3,f[1].x,f[1].y,f[1].z),s.setXYZ(4,d[0].x,d[0].y,d[0].z),s.setXYZ(5,d[3].x,d[3].y,d[3].z),s.setXYZ(6,d[2].x,d[2].y,d[2].z),s.setXYZ(7,d[1].x,d[1].y,d[1].z),s.needsUpdate=!0}},e.CascadedDirectionalLights=r,e.CascadedShadowMaps=N,e.applyViewSpaceShadowReceive=y,e.cascadedShadowMapsOptionsDefaults=M,e.composeViewSpaceShadowMatrices=b,e.frustumSplitFunctions=w,e.overrideFragmentShaderForCSM=g,e.splitFrustum=T});
|
|
359
|
+
#endif`),e.vertexShader=y(y(e.vertexShader,`#include <shadowmap_pars_vertex>`,`#include <shadowmap_pars_vertex>\n${v}`),`#include <shadowmap_vertex>`,r)}function x(e,n,r){r.length=e.length;for(let i=0;i<e.length;i++)(r[i]??(r[i]=new t.Matrix4)).multiplyMatrices(e[i].shadow.matrix,n)}var S=class{map=new Map;setup(e,t){if(this.map.has(e))return e;e.userData.defines??={},e.userData.defines.CSM=1,e.userData.defines.CSM_CASCADE_COUNT=t.cascadeCount,t.fade&&(e.userData.defines.CSM_FADE=1);let n={};this.map.set(e,n);let r=(e,r)=>{n.parameters=e,t.captureRenderer(r);let i=t.cascades,a=t.mainCamera.near,o=Math.min(t.mainCamera.far,t.far),s=e.uniforms;s.csmCascades={value:i},s.csmNear={value:a},s.csmFar={value:o},e.fragmentShader=g(e.fragmentShader),b(e,t.shadowMatricesViewUniform)},i=e.onBeforeCompile.bind(e),a=e.customProgramCacheKey.bind(e);Object.prototype.hasOwnProperty.call(e,`hasOwnProperty`)&&(n.originalHandler=i),e.customProgramCacheKey=()=>i.toString()+a(),e.onBeforeCompile=(...t)=>{let[n]=t;n.defines??={},Object.assign(n.defines,e.userData.defines),i.apply(e,t),r(...t)};let o=()=>{this.map.delete(e),e.removeEventListener(`dispose`,o)};return e.addEventListener(`dispose`,o),e}update(e){let t=e.cascadeCount,n=e.fade,r=e.cascades,i=e.mainCamera.near,a=Math.min(e.mainCamera.far,e.far);for(let[e,o]of this.map.entries()){let s=e.userData.defines;if(s!=null){let r=!1;s.CSM_FADE!=null!==n&&(n?s.CSM_FADE=1:delete s.CSM_FADE,r=!0),s.CSM_CASCADE_COUNT!==t&&(s.CSM_CASCADE_COUNT=t,r=!0),r&&(e.needsUpdate=!0)}let c=o.parameters?.uniforms;c!=null&&(c.csmCascades.value=r,c.csmNear.value=i,c.csmFar.value=a)}}rollback(e){let t=this.map.get(e);return t==null?e:this.rollbackToState(e,t)}rollbackToState(e,{parameters:t,originalHandler:n}){return t!=null&&(delete t.uniforms.csmCascades,delete t.uniforms.csmNear,delete t.uniforms.csmFar,delete t.uniforms.nvrCsmShadowMatrixView),n==null?delete e.onBeforeCompile:e.onBeforeCompile=n,e.userData.defines!=null&&(delete e.userData.defines.CSM,delete e.userData.defines.CSM_CASCADE_COUNT,delete e.userData.defines.CSM_FADE),e.needsUpdate=!0,this.map.delete(e),e}dispose(){for(let[e,t]of this.map)this.rollbackToState(e,t);this.map.clear()}};Math.PI/180,180/Math.PI;function C(e,t,n){return(1-n)*e+n*t}var w={uniform:(e,t,n,r,i=[])=>{for(let r=0;r<e;++r)i[r]=(t+(n-t)*(r+1)/e)/n;return i.length=e,i},logarithmic:(e,t,n,r,i=[])=>{for(let r=0;r<e;++r)i[r]=t*(n/t)**((r+1)/e)/n;return i.length=e,i},practical:(e,t,n,r=.5,i=[])=>{for(let a=0;a<e;++a){let o=(t+(n-t)*(a+1)/e)/n,s=t*(n/t)**((a+1)/e)/n;i[a]=C(o,s,r)}return i.length=e,i}},T=w;function E(e,t,n,r,i,a=[]){return w[e](t,n,r,i,a)}var D=new t.Vector3,O=new t.Vector3,k=new t.Matrix4,A=new t.Matrix4,j=new o,M=new t.Box3,N={cascadeCount:4,mapSize:2048,far:5e3,mode:`practical`,lambda:.5,margin:200,fade:!0,disableLastCascadeCutoff:!1},P=class{mainCamera;needsUpdateFrusta=!0;directionalLights=new r;materialStates=new S;mainFrustum=new o;cascadedFrusta=[];splits=[];cascades=[];_far;_mode;_lambda;_margin;_fade;_disableLastCascadeCutoff;_shadowMatricesView=[];_shadowMatricesViewFrame=-1;_renderer=null;shadowMatricesViewUniform;constructor(e,t){this.mainCamera=e;let n=()=>(this.refreshShadowMatricesView(),this._shadowMatricesView);this.shadowMatricesViewUniform={get value(){return n()}};let{cascadeCount:r,mapSize:i,far:a,mode:o,lambda:s,margin:c,fade:l,disableLastCascadeCutoff:u}={...N,...t};this._far=a,this._mode=o,this._lambda=s,this._margin=c,this._fade=l,this._disableLastCascadeCutoff=u,this.cascadeCount=r,this.mapSize=i}dispose(){this.materialStates.dispose(),this.directionalLights.dispose()}setupMaterial(e){return this.materialStates.setup(e,this)}captureRenderer(e){this._renderer??=e}refreshShadowMatricesView(){let e=this._renderer?.info.render.frame??-1;(e===-1||e!==this._shadowMatricesViewFrame)&&(this._shadowMatricesViewFrame=e,x(this.directionalLights.cascadedLights,this.mainCamera.matrixWorld,this._shadowMatricesView))}rollbackMaterial(e){return this.materialStates.rollback(e)}setupMaterials(e){for(let t=0;t<e.length;++t)this.materialStates.setup(e[t],this);return()=>{for(let t=0;t<e.length;++t)this.materialStates.rollback(e[t])}}updateCascades(){let e=this.cascadeCount,n=this.splits;E(this.mode,e,this.mainCamera.near,Math.min(this.mainCamera.far,this.far),this.lambda,n),this.mainFrustum.setFromCamera(this.mainCamera,this.far),this.mainFrustum.split(n,this.cascadedFrusta);let r=this.cascades;for(let i=0;i<e;++i)(r[i]??(r[i]=new t.Vector2)).set(n[i-1]??0,n[i]??0);this.disableLastCascadeCutoff&&(r[e-1].y=1/0),r.length=e}getFrustumRadius(e){let t=e.near,n=e.far,r=Math.max(n[0].distanceTo(n[2]),n[0].distanceTo(t[2]));if(this.fade){let e=this.mainCamera,t=e.near,i=Math.min(e.far,this.far),a=n[0].z/(i-t);r+=.25*a**2*(i-t)}return r*.5}updateShadowBounds(){let e=this.cascadedFrusta,t=this.directionalLights.cascadedLights;a(e.length===t.length);for(let n=0;n<e.length;++n){let e=this.getFrustumRadius(this.cascadedFrusta[n]),r=t[n].shadow.camera;r.left=-e,r.right=e,r.top=e,r.bottom=-e,r.near=0,r.far=e*2+this.margin,r.updateProjectionMatrix()}}updateFrusta(){this.updateCascades(),this.updateShadowBounds(),this.materialStates.update(this)}update(){this.needsUpdateFrusta&&(this.needsUpdateFrusta=!1,this.updateFrusta());let e=this.directionalLights,n=D.copy(e.direction).normalize(),r=k.lookAt(new t.Vector3,n,t.Object3D.DEFAULT_UP),i=A.multiplyMatrices(A.copy(r).invert(),this.mainCamera.matrixWorld),a=this.margin,o=this.mapSize,s=this.cascadedFrusta,c=e.cascadedLights;for(let e=0;e<s.length;++e){let{near:t,far:l}=j.copy(s[e]).applyMatrix4(i),u=M.makeEmpty();for(let e=0;e<4;e++)u.expandByPoint(t[e]),u.expandByPoint(l[e]);let d=u.getCenter(O);d.z=u.max.z+a;let f=c[e],{left:p,right:m,top:h,bottom:g}=f.shadow.camera,_=(m-p)/o,v=(h-g)/o;d.x=Math.round(d.x/_)*_,d.y=Math.round(d.y/v)*v,d.applyMatrix4(r),f.position.copy(d),f.target.position.copy(d),f.target.position.add(n)}}get cascadeCount(){return this.directionalLights.cascadedLights.length}set cascadeCount(e){e!==this.cascadeCount&&(this.directionalLights.setCount(e),this.needsUpdateFrusta=!0)}get mapSize(){return this.directionalLights.mainLight.shadow.mapSize.width}set mapSize(e){if(e!==this.mapSize){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n){let r=t[n].shadow;r.mapSize.width=e,r.mapSize.height=e,r.map!=null&&(r.map.dispose(),r.map=null)}}}get far(){return this._far}set far(e){e!==this._far&&(this._far=e,this.needsUpdateFrusta=!0)}get mode(){return this._mode}set mode(e){e!==this._mode&&(this._mode=e,this.needsUpdateFrusta=!0)}get lambda(){return this._lambda}set lambda(e){e!==this._lambda&&(this._lambda=e,this.needsUpdateFrusta=!0)}get margin(){return this._margin}set margin(e){e!==this._margin&&(this._margin=e,this.needsUpdateFrusta=!0)}get fade(){return this._fade}set fade(e){e!==this._fade&&(this._fade=e,this.needsUpdateFrusta=!0)}get disableLastCascadeCutoff(){return this._disableLastCascadeCutoff}set disableLastCascadeCutoff(e){e!==this._disableLastCascadeCutoff&&(this._disableLastCascadeCutoff=e,this.needsUpdateFrusta=!0)}get intensity(){return this.directionalLights.mainLight.intensity}set intensity(e){if(e!==this.intensity){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].intensity=e}}get shadowIntensity(){return this.directionalLights.mainLight.shadow.intensity}set shadowIntensity(e){if(e!==this.shadowIntensity){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.intensity=e}}get color(){return this.directionalLights.mainLight.color}set color(e){e!==this.color&&this.directionalLights.mainLight.color.copy(e)}get bias(){return this.directionalLights.mainLight.shadow.bias}set bias(e){if(e!==this.bias){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.bias=e}}get normalBias(){return this.directionalLights.mainLight.shadow.normalBias}set normalBias(e){if(e!==this.normalBias){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.normalBias=e}}get radius(){return this.directionalLights.mainLight.shadow.radius}set radius(e){if(e!==this.radius){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.radius=e}}get blurSamples(){return this.directionalLights.mainLight.shadow.blurSamples}set blurSamples(e){if(e!==this.blurSamples){let t=this.directionalLights.cascadedLights;for(let n=0;n<t.length;++n)t[n].shadow.blurSamples=e}}};e.CSMHelper=class extends t.Group{csm;displayFrustum=!0;displayPlanes=!0;displayShadowBounds=!0;frustumLines;cascadeLines=[];cascadePlanes=[];shadowLines=[];constructor(e){super(),this.csm=e;let n=new Uint16Array([0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7]),r=new Float32Array(24),i=new t.BufferGeometry;i.setIndex(new t.BufferAttribute(n,1)),i.setAttribute(`position`,new t.BufferAttribute(r,3,!1));let a=new t.LineSegments(i,new t.LineBasicMaterial);this.add(a),this.frustumLines=a}updateVisibility(){let e=this.displayFrustum,t=this.displayPlanes,n=this.displayShadowBounds,r=this.frustumLines,i=this.cascadeLines,a=this.cascadePlanes,o=this.shadowLines;for(let r=0,s=i.length;r<s;r++){let s=i[r],c=a[r],l=o[r];s.visible=e,c.visible=e&&t,l.visible=n}r.visible=e}update(){let e=this.csm,n=e.mainCamera,r=e.cascadeCount,i=e.mainFrustum,a=e.cascadedFrusta,o=e.directionalLights.cascadedLights,s=this.frustumLines.geometry.getAttribute(`position`),c=this.cascadeLines,l=this.cascadePlanes,u=this.shadowLines;for(this.position.copy(n.position),this.quaternion.copy(n.quaternion),this.scale.copy(n.scale),this.updateMatrixWorld(!0);c.length>r;){let e=c.pop(),t=l.pop(),n=u.pop();e&&this.remove(e),t&&this.remove(t),n&&this.remove(n)}for(;c.length<r;){let e=new t.Box3Helper(new t.Box3,new t.Color(16777215)),n=new t.MeshBasicMaterial({transparent:!0,opacity:.1,depthWrite:!1,side:t.DoubleSide}),r=new t.Mesh(new t.PlaneGeometry,n),i=new t.Group,a=new t.Box3Helper(new t.Box3,new t.Color(16776960));i.add(a),this.add(e),this.add(r),this.add(i),c.push(e),l.push(r),u.push(i)}for(let e=0;e<r;e++){let t=a[e],n=o[e].shadow.camera,r=t.far,i=c[e],s=l[e],d=u[e],f=d.children[0];i.box.min.copy(r[2]),i.box.max.copy(r[0]),i.box.max.z+=1e-4,s.position.addVectors(r[0],r[2]),s.position.multiplyScalar(.5),s.scale.subVectors(r[0],r[2]),s.scale.z=1e-4,this.remove(d),d.position.copy(n.position),d.quaternion.copy(n.quaternion),d.scale.copy(n.scale),d.updateMatrixWorld(!0),this.attach(d),f.box.min.set(n.bottom,n.left,-n.far),f.box.max.set(n.top,n.right,-n.near)}let d=i.near,f=i.far;s.setXYZ(0,f[0].x,f[0].y,f[0].z),s.setXYZ(1,f[3].x,f[3].y,f[3].z),s.setXYZ(2,f[2].x,f[2].y,f[2].z),s.setXYZ(3,f[1].x,f[1].y,f[1].z),s.setXYZ(4,d[0].x,d[0].y,d[0].z),s.setXYZ(5,d[3].x,d[3].y,d[3].z),s.setXYZ(6,d[2].x,d[2].y,d[2].z),s.setXYZ(7,d[1].x,d[1].y,d[1].z),s.needsUpdate=!0}},e.CascadedDirectionalLights=r,e.CascadedShadowMaps=P,e.VIEW_SPACE_SHADOW_DEFINE=_,e.applyViewSpaceShadowReceive=b,e.cascadedShadowMapsOptionsDefaults=N,e.composeViewSpaceShadowMatrices=x,e.frustumSplitFunctions=T,e.overrideFragmentShaderForCSM=g,e.splitFrustum=E});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navaramap/three-csm",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"main": "dist/index.umd.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"license": "MIT OR Apache-2.0",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/
|
|
15
|
+
"url": "git+https://github.com/maplibre/navara.git",
|
|
16
16
|
"directory": "web/navara_three_csm"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"three": ">=0.183.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@types/three": "0.185.
|
|
25
|
+
"@types/three": "0.185.4",
|
|
26
26
|
"three": "0.185.1"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
@@ -12,6 +12,7 @@ import { describe, expect, it } from "vitest";
|
|
|
12
12
|
import {
|
|
13
13
|
applyViewSpaceShadowReceive,
|
|
14
14
|
composeViewSpaceShadowMatrices,
|
|
15
|
+
VIEW_SPACE_SHADOW_DEFINE,
|
|
15
16
|
} from "./viewSpaceShadowReceive";
|
|
16
17
|
|
|
17
18
|
function makeLightWithShadowMatrix(matrix: Matrix4): DirectionalLight {
|
|
@@ -100,12 +101,16 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
100
101
|
it("rewrites the directional shadow loop against the view-space matrices", () => {
|
|
101
102
|
const shader = {
|
|
102
103
|
vertexShader: litVertexShader,
|
|
104
|
+
fragmentShader: "",
|
|
103
105
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
applyViewSpaceShadowReceive(shader, uniform);
|
|
107
109
|
|
|
108
110
|
expect(shader.uniforms.nvrCsmShadowMatrixView).toBe(uniform);
|
|
111
|
+
expect(shader.fragmentShader).toContain(
|
|
112
|
+
`#define ${VIEW_SPACE_SHADOW_DEFINE}`,
|
|
113
|
+
);
|
|
109
114
|
// Sized to the cascade count: additional shadow-casting directional
|
|
110
115
|
// lights beyond the cascades are not part of the composed array.
|
|
111
116
|
expect(shader.vertexShader).toContain(
|
|
@@ -127,6 +132,7 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
127
132
|
it("keeps the stock path for extra directional lights and point shadows", () => {
|
|
128
133
|
const shader = {
|
|
129
134
|
vertexShader: litVertexShader,
|
|
135
|
+
fragmentShader: "",
|
|
130
136
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
131
137
|
};
|
|
132
138
|
|
|
@@ -158,6 +164,7 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
158
164
|
it("skips shadow-map depth pass variants", () => {
|
|
159
165
|
const shader = {
|
|
160
166
|
vertexShader: litVertexShader,
|
|
167
|
+
fragmentShader: "",
|
|
161
168
|
defines: { USE_SHADOWMAP_DEPTH: 1 } as Record<string, unknown>,
|
|
162
169
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
163
170
|
};
|
|
@@ -172,12 +179,14 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
172
179
|
const source = "#include <common>\nvoid main() {}";
|
|
173
180
|
const shader = {
|
|
174
181
|
vertexShader: source,
|
|
182
|
+
fragmentShader: "",
|
|
175
183
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
176
184
|
};
|
|
177
185
|
|
|
178
186
|
applyViewSpaceShadowReceive(shader, uniform);
|
|
179
187
|
|
|
180
188
|
expect(shader.vertexShader).toBe(source);
|
|
189
|
+
expect(shader.fragmentShader).toBe("");
|
|
181
190
|
expect(shader.uniforms.nvrCsmShadowMatrixView).toBeUndefined();
|
|
182
191
|
});
|
|
183
192
|
|
|
@@ -208,6 +217,7 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
208
217
|
(name) => {
|
|
209
218
|
const shader = {
|
|
210
219
|
vertexShader: ShaderLib[name].vertexShader,
|
|
220
|
+
fragmentShader: ShaderLib[name].fragmentShader,
|
|
211
221
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
212
222
|
};
|
|
213
223
|
|
|
@@ -233,6 +243,7 @@ describe("applyViewSpaceShadowReceive", () => {
|
|
|
233
243
|
const source = ShaderLib[name as keyof typeof ShaderLib].vertexShader;
|
|
234
244
|
const shader = {
|
|
235
245
|
vertexShader: source,
|
|
246
|
+
fragmentShader: "",
|
|
236
247
|
uniforms: {} as Record<string, { value: unknown }>,
|
|
237
248
|
};
|
|
238
249
|
|
|
@@ -19,6 +19,9 @@ import { Matrix4, ShaderChunk, type DirectionalLight } from "three";
|
|
|
19
19
|
* the exact quantity that renders stably, so it can never jitter relative to
|
|
20
20
|
* the geometry — and a single patch covers every mesh type.
|
|
21
21
|
*/
|
|
22
|
+
/** Defined in the fragment shader of a material patched for view-space shadows. */
|
|
23
|
+
export const VIEW_SPACE_SHADOW_DEFINE = "NVR_VIEW_SPACE_SHADOW";
|
|
24
|
+
|
|
22
25
|
export type ShadowMatricesViewUniform = { value: Matrix4[] };
|
|
23
26
|
|
|
24
27
|
const SHADOW_MATRIX_VIEW_PARS_VERTEX = /* glsl */ `
|
|
@@ -72,10 +75,15 @@ function replaceOrThrow(
|
|
|
72
75
|
* left untouched, as are shaders compiled for the shadow-map depth pass
|
|
73
76
|
* (`USE_SHADOWMAP_DEPTH` — they never sample shadows and would upload the
|
|
74
77
|
* uniform before the shadow maps for the frame are rendered).
|
|
78
|
+
*
|
|
79
|
+
* The fragment shader gets `NVR_VIEW_SPACE_SHADOW` defined, so code that
|
|
80
|
+
* computes shadow coordinates there (`chunks/ground_shadow_coord_fragment`)
|
|
81
|
+
* takes the same path.
|
|
75
82
|
*/
|
|
76
83
|
export function applyViewSpaceShadowReceive(
|
|
77
84
|
shader: {
|
|
78
85
|
vertexShader: string;
|
|
86
|
+
fragmentShader: string;
|
|
79
87
|
defines?: Record<string, unknown>;
|
|
80
88
|
uniforms: Record<string, { value: unknown }>;
|
|
81
89
|
},
|
|
@@ -85,6 +93,7 @@ export function applyViewSpaceShadowReceive(
|
|
|
85
93
|
if (!shader.vertexShader.includes("#include <shadowmap_vertex>")) return;
|
|
86
94
|
|
|
87
95
|
shader.uniforms.nvrCsmShadowMatrixView = uniform;
|
|
96
|
+
shader.fragmentShader = `#define ${VIEW_SPACE_SHADOW_DEFINE}\n${shader.fragmentShader}`;
|
|
88
97
|
|
|
89
98
|
// Rewrite only the directional-light loop of three's shadowmap_vertex
|
|
90
99
|
// chunk, preserving the unroll pragmas. Each line becomes a per-index
|