@navaramap/three-csm 0.0.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 +8 -0
- package/README.md +5 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +483 -0
- package/dist/index.umd.cjs +306 -0
- package/package.json +38 -0
- package/src/CSMHelper.ts +199 -0
- package/src/CascadedDirectionalLights.ts +80 -0
- package/src/CascadedShadowMaps.ts +475 -0
- package/src/FrustumCorners.ts +111 -0
- package/src/MaterialStates.ts +154 -0
- package/src/createFragmentShader.ts +92 -0
- package/src/index.ts +5 -0
- package/src/shaders/cascadedFadedLights.glsl +102 -0
- package/src/shaders/cascadedLights.glsl +81 -0
- package/src/shaders/cascadedLightsWithoutShadows.glsl +35 -0
- package/src/shaders/defaultLights.glsl +36 -0
- package/src/shaders/nonShadowCastingLights.glsl +18 -0
- package/src/splitFrustum.ts +59 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Based on the following work:
|
|
2
|
+
// https://github.com/StrandedKitty/three-csm/tree/master/src
|
|
3
|
+
// https://github.com/mrdoob/three.js/tree/r169/examples/jsm/csm
|
|
4
|
+
|
|
5
|
+
import { type Material, type WebGLProgramParametersWithUniforms } from "three";
|
|
6
|
+
|
|
7
|
+
import { type CascadedShadowMaps } from "./CascadedShadowMaps";
|
|
8
|
+
import { createFragmentShader } from "./createFragmentShader";
|
|
9
|
+
|
|
10
|
+
type OnBeforeCompile = Material["onBeforeCompile"];
|
|
11
|
+
|
|
12
|
+
export type MaterialState = {
|
|
13
|
+
parameters?: WebGLProgramParametersWithUniforms;
|
|
14
|
+
originalHandler?: OnBeforeCompile;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class MaterialStates {
|
|
18
|
+
private readonly map = new Map<Material, MaterialState>();
|
|
19
|
+
|
|
20
|
+
setup<T extends Material>(material: T, csm: CascadedShadowMaps): T {
|
|
21
|
+
if (this.map.has(material)) {
|
|
22
|
+
return material;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
material.userData.defines ??= {};
|
|
26
|
+
material.userData.defines.CSM = 1;
|
|
27
|
+
material.userData.defines.CSM_CASCADE_COUNT = csm.cascadeCount;
|
|
28
|
+
if (csm.fade) {
|
|
29
|
+
material.userData.defines.CSM_FADE = 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const stateRef: MaterialState = {};
|
|
33
|
+
this.map.set(material, stateRef);
|
|
34
|
+
|
|
35
|
+
const handleBeforeCompile: OnBeforeCompile = (parameters) => {
|
|
36
|
+
stateRef.parameters = parameters;
|
|
37
|
+
|
|
38
|
+
const cascades = csm.cascades;
|
|
39
|
+
const near = csm.mainCamera.near;
|
|
40
|
+
const far = Math.min(csm.mainCamera.far, csm.far);
|
|
41
|
+
const uniforms = parameters.uniforms;
|
|
42
|
+
uniforms.csmCascades = { value: cascades };
|
|
43
|
+
uniforms.csmNear = { value: near };
|
|
44
|
+
uniforms.csmFar = { value: far };
|
|
45
|
+
|
|
46
|
+
parameters.fragmentShader = createFragmentShader(
|
|
47
|
+
parameters.fragmentShader,
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const originalHandler = material.onBeforeCompile.bind(material);
|
|
52
|
+
const originalCustomProgramCacheKey =
|
|
53
|
+
material.customProgramCacheKey.bind(material);
|
|
54
|
+
if (Object.prototype.hasOwnProperty.call(material, "hasOwnProperty")) {
|
|
55
|
+
stateRef.originalHandler = originalHandler;
|
|
56
|
+
}
|
|
57
|
+
material.customProgramCacheKey = () =>
|
|
58
|
+
originalHandler.toString() + originalCustomProgramCacheKey();
|
|
59
|
+
material.onBeforeCompile = (...args) => {
|
|
60
|
+
const [shader] = args;
|
|
61
|
+
shader.defines ??= {};
|
|
62
|
+
Object.assign(shader.defines, material.userData.defines);
|
|
63
|
+
originalHandler.apply(material, args);
|
|
64
|
+
handleBeforeCompile(...args);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const handleDispose = (): void => {
|
|
68
|
+
// Just discard it from our state map without rolling it back.
|
|
69
|
+
this.map.delete(material);
|
|
70
|
+
material.removeEventListener("dispose", handleDispose);
|
|
71
|
+
};
|
|
72
|
+
material.addEventListener("dispose", handleDispose);
|
|
73
|
+
|
|
74
|
+
return material;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
update(csm: CascadedShadowMaps): void {
|
|
78
|
+
const cascadeCount = csm.cascadeCount;
|
|
79
|
+
const fade = csm.fade;
|
|
80
|
+
const cascades = csm.cascades;
|
|
81
|
+
const near = csm.mainCamera.near;
|
|
82
|
+
const far = Math.min(csm.mainCamera.far, csm.far);
|
|
83
|
+
|
|
84
|
+
for (const [material, state] of this.map.entries()) {
|
|
85
|
+
const defines = material.userData.defines;
|
|
86
|
+
if (defines != null) {
|
|
87
|
+
let needsUpdate = false;
|
|
88
|
+
if ((defines.CSM_FADE != null) !== fade) {
|
|
89
|
+
if (fade) {
|
|
90
|
+
defines.CSM_FADE = 1;
|
|
91
|
+
} else {
|
|
92
|
+
delete defines.CSM_FADE;
|
|
93
|
+
}
|
|
94
|
+
needsUpdate = true;
|
|
95
|
+
}
|
|
96
|
+
if (defines.CSM_CASCADE_COUNT !== cascadeCount) {
|
|
97
|
+
defines.CSM_CASCADE_COUNT = cascadeCount;
|
|
98
|
+
needsUpdate = true;
|
|
99
|
+
}
|
|
100
|
+
if (needsUpdate) {
|
|
101
|
+
material.needsUpdate = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const uniforms = state.parameters?.uniforms;
|
|
106
|
+
if (uniforms != null) {
|
|
107
|
+
uniforms.csmCascades.value = cascades;
|
|
108
|
+
uniforms.csmNear.value = near;
|
|
109
|
+
uniforms.csmFar.value = far;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
rollback<T extends Material>(material: T): T {
|
|
115
|
+
const state = this.map.get(material);
|
|
116
|
+
return state != null ? this.rollbackToState(material, state) : material;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private rollbackToState<T extends Material>(
|
|
120
|
+
material: T,
|
|
121
|
+
{ parameters, originalHandler }: MaterialState,
|
|
122
|
+
): T {
|
|
123
|
+
if (parameters != null) {
|
|
124
|
+
// TODO: Do we really need to delete uniforms when materials are going to
|
|
125
|
+
// be updated anyways?
|
|
126
|
+
delete parameters.uniforms.csmCascades;
|
|
127
|
+
delete parameters.uniforms.csmNear;
|
|
128
|
+
delete parameters.uniforms.csmFar;
|
|
129
|
+
}
|
|
130
|
+
if (originalHandler != null) {
|
|
131
|
+
material.onBeforeCompile = originalHandler;
|
|
132
|
+
} else {
|
|
133
|
+
// @ts-expect-error Restore the prototype function by deleting the handler
|
|
134
|
+
// we've assigned above.
|
|
135
|
+
delete material.onBeforeCompile;
|
|
136
|
+
}
|
|
137
|
+
if (material.userData.defines != null) {
|
|
138
|
+
delete material.userData.defines.CSM;
|
|
139
|
+
delete material.userData.defines.CSM_CASCADE_COUNT;
|
|
140
|
+
delete material.userData.defines.CSM_FADE;
|
|
141
|
+
}
|
|
142
|
+
material.needsUpdate = true;
|
|
143
|
+
|
|
144
|
+
this.map.delete(material);
|
|
145
|
+
return material;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
dispose(): void {
|
|
149
|
+
for (const [material, state] of this.map) {
|
|
150
|
+
this.rollbackToState(material, state);
|
|
151
|
+
}
|
|
152
|
+
this.map.clear();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ShaderChunk } from "three";
|
|
2
|
+
|
|
3
|
+
import cascadedFadedLights from "./shaders/cascadedFadedLights.glsl?raw";
|
|
4
|
+
import cascadedLights from "./shaders/cascadedLights.glsl?raw";
|
|
5
|
+
import cascadedLightsWithoutShadows from "./shaders/cascadedLightsWithoutShadows.glsl?raw";
|
|
6
|
+
import defaultLights from "./shaders/defaultLights.glsl?raw";
|
|
7
|
+
import nonShadowCastingLights from "./shaders/nonShadowCastingLights.glsl?raw";
|
|
8
|
+
|
|
9
|
+
const uniforms = /* glsl */ `
|
|
10
|
+
#include <lights_pars_begin>
|
|
11
|
+
|
|
12
|
+
#ifdef CSM
|
|
13
|
+
uniform vec2 csmCascades[CSM_CASCADE_COUNT];
|
|
14
|
+
uniform float csmNear;
|
|
15
|
+
uniform float csmFar;
|
|
16
|
+
#endif // CSM
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
function body(shader: string): string {
|
|
20
|
+
return shader.replace(/^\s*void\s+\w+\s*\(\s*\)\s*/m, "");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const directionalLights = /*#__PURE__*/ (() => /* glsl */ `
|
|
24
|
+
#if NUM_DIR_LIGHTS > 0 && defined(RE_Direct)
|
|
25
|
+
DirectionalLight directionalLight;
|
|
26
|
+
#if defined(USE_SHADOWMAP) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
27
|
+
DirectionalLightShadow directionalLightShadow;
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
#ifdef CSM
|
|
31
|
+
#ifdef USE_SHADOWMAP
|
|
32
|
+
#ifdef CSM_FADE
|
|
33
|
+
${body(cascadedFadedLights)}
|
|
34
|
+
#else
|
|
35
|
+
${body(cascadedLights)}
|
|
36
|
+
#endif // CSM_FADE
|
|
37
|
+
#elif NUM_DIR_LIGHT_SHADOWS > 0
|
|
38
|
+
${body(cascadedLightsWithoutShadows)}
|
|
39
|
+
#endif // USE_SHADOWMAP
|
|
40
|
+
|
|
41
|
+
#if NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS
|
|
42
|
+
${body(nonShadowCastingLights)}
|
|
43
|
+
#endif
|
|
44
|
+
#else
|
|
45
|
+
${body(defaultLights)}
|
|
46
|
+
#endif // CSM
|
|
47
|
+
#endif // NUM_DIR_LIGHTS > 0 && defined(RE_Direct)
|
|
48
|
+
`)();
|
|
49
|
+
|
|
50
|
+
type ReplaceRangeParams = {
|
|
51
|
+
source: string;
|
|
52
|
+
startLine: string;
|
|
53
|
+
endLine: string;
|
|
54
|
+
replacement: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function findAndReplace({
|
|
58
|
+
source,
|
|
59
|
+
startLine,
|
|
60
|
+
endLine,
|
|
61
|
+
replacement,
|
|
62
|
+
}: ReplaceRangeParams): string {
|
|
63
|
+
const startText = `\n${startLine}\n`;
|
|
64
|
+
const startIndex = source.indexOf(startText);
|
|
65
|
+
if (startIndex < 0) {
|
|
66
|
+
return source;
|
|
67
|
+
}
|
|
68
|
+
const endText = `\n${endLine}\n`;
|
|
69
|
+
const endIndex = source.indexOf(endText, startIndex + startText.length);
|
|
70
|
+
if (endIndex < 0) {
|
|
71
|
+
return source;
|
|
72
|
+
}
|
|
73
|
+
return (
|
|
74
|
+
source.slice(0, startIndex + 1) +
|
|
75
|
+
replacement +
|
|
76
|
+
source.slice(endIndex + endText.length - 1)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createFragmentShader(fragmentShader: string): string {
|
|
81
|
+
return fragmentShader
|
|
82
|
+
.replace("#include <lights_pars_begin>", uniforms)
|
|
83
|
+
.replace(
|
|
84
|
+
"#include <lights_fragment_begin>",
|
|
85
|
+
findAndReplace({
|
|
86
|
+
source: ShaderChunk.lights_fragment_begin,
|
|
87
|
+
startLine: "#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )",
|
|
88
|
+
endLine: "#endif",
|
|
89
|
+
replacement: directionalLights,
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Based on the following work:
|
|
2
|
+
// https://github.com/StrandedKitty/three-csm/tree/master/src
|
|
3
|
+
// https://github.com/mrdoob/three.js/tree/r169/examples/jsm/csm
|
|
4
|
+
|
|
5
|
+
void cascadedFadedLights() {
|
|
6
|
+
directionalLight = directionalLights[0];
|
|
7
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
8
|
+
|
|
9
|
+
float clipDepth = vViewPosition.z / (csmFar - csmNear);
|
|
10
|
+
bool lastCascade;
|
|
11
|
+
vec2 cascade;
|
|
12
|
+
float cascadeCenter;
|
|
13
|
+
float closestEdge;
|
|
14
|
+
float margin;
|
|
15
|
+
float shadow = 1.0;
|
|
16
|
+
float nextShadow = 1.0;
|
|
17
|
+
|
|
18
|
+
#pragma unroll_loop_start
|
|
19
|
+
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
|
|
20
|
+
#if UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
21
|
+
#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
22
|
+
|
|
23
|
+
lastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADE_COUNT - 1;
|
|
24
|
+
cascade = csmCascades[i];
|
|
25
|
+
cascadeCenter = (cascade.x + cascade.y) * 0.5;
|
|
26
|
+
closestEdge = clipDepth < cascadeCenter ? cascade.x : cascade.y;
|
|
27
|
+
margin = 0.25 * pow(closestEdge, 2.0);
|
|
28
|
+
cascade += margin * vec2(-0.5, 0.5);
|
|
29
|
+
|
|
30
|
+
if (clipDepth >= cascade.x && (clipDepth < cascade.y || lastCascade)) {
|
|
31
|
+
directionalLightShadow = directionalLightShadows[i];
|
|
32
|
+
float prevShadow = nextShadow;
|
|
33
|
+
nextShadow =
|
|
34
|
+
directLight.visible && receiveShadow
|
|
35
|
+
? getShadow(
|
|
36
|
+
directionalShadowMap[i],
|
|
37
|
+
directionalLightShadow.shadowMapSize,
|
|
38
|
+
directionalLightShadow.shadowIntensity,
|
|
39
|
+
directionalLightShadow.shadowBias,
|
|
40
|
+
directionalLightShadow.shadowRadius,
|
|
41
|
+
vDirectionalShadowCoord[i]
|
|
42
|
+
)
|
|
43
|
+
: 1.0;
|
|
44
|
+
|
|
45
|
+
shadow = mix(
|
|
46
|
+
prevShadow,
|
|
47
|
+
nextShadow,
|
|
48
|
+
saturate(min(clipDepth - cascade.x, cascade.y - clipDepth) / margin)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
#endif // UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
52
|
+
#endif // UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
53
|
+
}
|
|
54
|
+
#pragma unroll_loop_end
|
|
55
|
+
|
|
56
|
+
// No loop here: all cascaded lights are in fact one light only.
|
|
57
|
+
directLight.color *= shadow;
|
|
58
|
+
RE_Direct(
|
|
59
|
+
directLight,
|
|
60
|
+
geometryPosition,
|
|
61
|
+
geometryNormal,
|
|
62
|
+
geometryViewDir,
|
|
63
|
+
geometryClearcoatNormal,
|
|
64
|
+
material,
|
|
65
|
+
reflectedLight
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// The rest of directional lights.
|
|
69
|
+
#pragma unroll_loop_start
|
|
70
|
+
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
|
|
71
|
+
#if UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
72
|
+
#if UNROLLED_LOOP_INDEX >= CSM_CASCADE_COUNT
|
|
73
|
+
directionalLight = directionalLights[i];
|
|
74
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
75
|
+
|
|
76
|
+
directionalLightShadow = directionalLightShadows[i];
|
|
77
|
+
directLight.color *=
|
|
78
|
+
directLight.visible && receiveShadow
|
|
79
|
+
? getShadow(
|
|
80
|
+
directionalShadowMap[i],
|
|
81
|
+
directionalLightShadow.shadowMapSize,
|
|
82
|
+
directionalLightShadow.shadowIntensity,
|
|
83
|
+
directionalLightShadow.shadowBias,
|
|
84
|
+
directionalLightShadow.shadowRadius,
|
|
85
|
+
vDirectionalShadowCoord[i]
|
|
86
|
+
)
|
|
87
|
+
: 1.0;
|
|
88
|
+
|
|
89
|
+
RE_Direct(
|
|
90
|
+
directLight,
|
|
91
|
+
geometryPosition,
|
|
92
|
+
geometryNormal,
|
|
93
|
+
geometryViewDir,
|
|
94
|
+
geometryClearcoatNormal,
|
|
95
|
+
material,
|
|
96
|
+
reflectedLight
|
|
97
|
+
);
|
|
98
|
+
#endif // UNROLLED_LOOP_INDEX >= CSM_CASCADE_COUNT
|
|
99
|
+
#endif // UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
100
|
+
}
|
|
101
|
+
#pragma unroll_loop_end
|
|
102
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Based on the following work:
|
|
2
|
+
// https://github.com/StrandedKitty/three-csm/tree/master/src
|
|
3
|
+
// https://github.com/mrdoob/three.js/tree/r169/examples/jsm/csm
|
|
4
|
+
|
|
5
|
+
void cascadedLights() {
|
|
6
|
+
directionalLight = directionalLights[0];
|
|
7
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
8
|
+
|
|
9
|
+
float clipDepth = vViewPosition.z / (csmFar - csmNear);
|
|
10
|
+
vec2 cascade;
|
|
11
|
+
|
|
12
|
+
#pragma unroll_loop_start
|
|
13
|
+
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
|
|
14
|
+
#if UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
15
|
+
#if UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
16
|
+
cascade = csmCascades[i];
|
|
17
|
+
directionalLightShadow = directionalLightShadows[i];
|
|
18
|
+
if (clipDepth >= cascade.x && clipDepth < cascade.y) {
|
|
19
|
+
directLight.color *=
|
|
20
|
+
directLight.visible && receiveShadow
|
|
21
|
+
? getShadow(
|
|
22
|
+
directionalShadowMap[i],
|
|
23
|
+
directionalLightShadow.shadowMapSize,
|
|
24
|
+
directionalLightShadow.shadowIntensity,
|
|
25
|
+
directionalLightShadow.shadowBias,
|
|
26
|
+
directionalLightShadow.shadowRadius,
|
|
27
|
+
vDirectionalShadowCoord[i]
|
|
28
|
+
)
|
|
29
|
+
: 1.0;
|
|
30
|
+
}
|
|
31
|
+
#endif // UNROLLED_LOOP_INDEX < CSM_CASCADE_COUNT
|
|
32
|
+
#endif // UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
33
|
+
}
|
|
34
|
+
#pragma unroll_loop_end
|
|
35
|
+
|
|
36
|
+
// No loop here: all cascaded lights are in fact one light only.
|
|
37
|
+
RE_Direct(
|
|
38
|
+
directLight,
|
|
39
|
+
geometryPosition,
|
|
40
|
+
geometryNormal,
|
|
41
|
+
geometryViewDir,
|
|
42
|
+
geometryClearcoatNormal,
|
|
43
|
+
material,
|
|
44
|
+
reflectedLight
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// The rest of directional lights.
|
|
48
|
+
#pragma unroll_loop_start
|
|
49
|
+
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
|
|
50
|
+
#if UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
51
|
+
#if UNROLLED_LOOP_INDEX >= CSM_CASCADE_COUNT
|
|
52
|
+
directionalLight = directionalLights[i];
|
|
53
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
54
|
+
|
|
55
|
+
directionalLightShadow = directionalLightShadows[i];
|
|
56
|
+
directLight.color *=
|
|
57
|
+
directLight.visible && receiveShadow
|
|
58
|
+
? getShadow(
|
|
59
|
+
directionalShadowMap[i],
|
|
60
|
+
directionalLightShadow.shadowMapSize,
|
|
61
|
+
directionalLightShadow.shadowIntensity,
|
|
62
|
+
directionalLightShadow.shadowBias,
|
|
63
|
+
directionalLightShadow.shadowRadius,
|
|
64
|
+
vDirectionalShadowCoord[i]
|
|
65
|
+
)
|
|
66
|
+
: 1.0;
|
|
67
|
+
|
|
68
|
+
RE_Direct(
|
|
69
|
+
directLight,
|
|
70
|
+
geometryPosition,
|
|
71
|
+
geometryNormal,
|
|
72
|
+
geometryViewDir,
|
|
73
|
+
geometryClearcoatNormal,
|
|
74
|
+
material,
|
|
75
|
+
reflectedLight
|
|
76
|
+
);
|
|
77
|
+
#endif // UNROLLED_LOOP_INDEX >= CSM_CASCADE_COUNT
|
|
78
|
+
#endif // UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
79
|
+
}
|
|
80
|
+
#pragma unroll_loop_end
|
|
81
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Based on the following work:
|
|
2
|
+
// https://github.com/StrandedKitty/three-csm/tree/master/src
|
|
3
|
+
// https://github.com/mrdoob/three.js/tree/r169/examples/jsm/csm
|
|
4
|
+
|
|
5
|
+
void cascadedLightsWithoutShadows() {
|
|
6
|
+
// No loop here: all cascaded lights are in fact one light only.
|
|
7
|
+
getDirectionalLightInfo(directionalLights[0], directLight);
|
|
8
|
+
RE_Direct(
|
|
9
|
+
directLight,
|
|
10
|
+
geometryPosition,
|
|
11
|
+
geometryNormal,
|
|
12
|
+
geometryViewDir,
|
|
13
|
+
geometryClearcoatNormal,
|
|
14
|
+
material,
|
|
15
|
+
reflectedLight
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// The rest of directional lights.
|
|
19
|
+
#pragma unroll_loop_start
|
|
20
|
+
for (int i = CSM_CASCADE_COUNT; i < NUM_DIR_LIGHTS; i++) {
|
|
21
|
+
directionalLight = directionalLights[i];
|
|
22
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
23
|
+
|
|
24
|
+
RE_Direct(
|
|
25
|
+
directLight,
|
|
26
|
+
geometryPosition,
|
|
27
|
+
geometryNormal,
|
|
28
|
+
geometryViewDir,
|
|
29
|
+
geometryClearcoatNormal,
|
|
30
|
+
material,
|
|
31
|
+
reflectedLight
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
#pragma unroll_loop_end
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Default shader.
|
|
2
|
+
// See: https://github.com/mrdoob/three.js/blob/r169/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js#L136
|
|
3
|
+
|
|
4
|
+
void defaultLights() {
|
|
5
|
+
#pragma unroll_loop_start
|
|
6
|
+
for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
|
|
7
|
+
directionalLight = directionalLights[i];
|
|
8
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
9
|
+
|
|
10
|
+
#if defined(USE_SHADOWMAP) && UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS
|
|
11
|
+
directionalLightShadow = directionalLightShadows[i];
|
|
12
|
+
directLight.color *=
|
|
13
|
+
directLight.visible && receiveShadow
|
|
14
|
+
? getShadow(
|
|
15
|
+
directionalShadowMap[i],
|
|
16
|
+
directionalLightShadow.shadowMapSize,
|
|
17
|
+
directionalLightShadow.shadowIntensity,
|
|
18
|
+
directionalLightShadow.shadowBias,
|
|
19
|
+
directionalLightShadow.shadowRadius,
|
|
20
|
+
vDirectionalShadowCoord[i]
|
|
21
|
+
)
|
|
22
|
+
: 1.0;
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
RE_Direct(
|
|
26
|
+
directLight,
|
|
27
|
+
geometryPosition,
|
|
28
|
+
geometryNormal,
|
|
29
|
+
geometryViewDir,
|
|
30
|
+
geometryClearcoatNormal,
|
|
31
|
+
material,
|
|
32
|
+
reflectedLight
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
#pragma unroll_loop_end
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
void nonShadowCastingLights() {
|
|
2
|
+
#pragma unroll_loop_start
|
|
3
|
+
for (int i = NUM_DIR_LIGHT_SHADOWS; i < NUM_DIR_LIGHTS; i++) {
|
|
4
|
+
directionalLight = directionalLights[i];
|
|
5
|
+
getDirectionalLightInfo(directionalLight, directLight);
|
|
6
|
+
|
|
7
|
+
RE_Direct(
|
|
8
|
+
directLight,
|
|
9
|
+
geometryPosition,
|
|
10
|
+
geometryNormal,
|
|
11
|
+
geometryViewDir,
|
|
12
|
+
geometryClearcoatNormal,
|
|
13
|
+
material,
|
|
14
|
+
reflectedLight
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
#pragma unroll_loop_end
|
|
18
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { lerp } from "three/src/math/MathUtils.js";
|
|
2
|
+
|
|
3
|
+
export type FrustumSplitFunction = (
|
|
4
|
+
length: number,
|
|
5
|
+
near: number,
|
|
6
|
+
far: number,
|
|
7
|
+
lambda?: number,
|
|
8
|
+
result?: number[],
|
|
9
|
+
) => number[];
|
|
10
|
+
|
|
11
|
+
export type FrustumSplitFunctions = {
|
|
12
|
+
uniform: FrustumSplitFunction;
|
|
13
|
+
logarithmic: FrustumSplitFunction;
|
|
14
|
+
practical: FrustumSplitFunction;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type FrustumSplitMode = keyof FrustumSplitFunctions;
|
|
18
|
+
|
|
19
|
+
// See: https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-10-parallel-split-shadow-maps-programmable-gpus
|
|
20
|
+
const modes: FrustumSplitFunctions = {
|
|
21
|
+
uniform: (count, near, far, _, result = []) => {
|
|
22
|
+
for (let i = 0; i < count; ++i) {
|
|
23
|
+
result[i] = (near + ((far - near) * (i + 1)) / count) / far;
|
|
24
|
+
}
|
|
25
|
+
result.length = count;
|
|
26
|
+
return result;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
logarithmic: (count, near, far, _, result = []) => {
|
|
30
|
+
for (let i = 0; i < count; ++i) {
|
|
31
|
+
result[i] = (near * (far / near) ** ((i + 1) / count)) / far;
|
|
32
|
+
}
|
|
33
|
+
result.length = count;
|
|
34
|
+
return result;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
practical: (count, near, far, lambda = 0.5, result = []) => {
|
|
38
|
+
for (let i = 0; i < count; ++i) {
|
|
39
|
+
const uniform = (near + ((far - near) * (i + 1)) / count) / far;
|
|
40
|
+
const logarithmic = (near * (far / near) ** ((i + 1) / count)) / far;
|
|
41
|
+
result[i] = lerp(uniform, logarithmic, lambda);
|
|
42
|
+
}
|
|
43
|
+
result.length = count;
|
|
44
|
+
return result;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const frustumSplitFunctions = modes;
|
|
49
|
+
|
|
50
|
+
export function splitFrustum(
|
|
51
|
+
mode: FrustumSplitMode,
|
|
52
|
+
count: number,
|
|
53
|
+
near: number,
|
|
54
|
+
far: number,
|
|
55
|
+
lambda?: number,
|
|
56
|
+
result: number[] = [],
|
|
57
|
+
): number[] {
|
|
58
|
+
return modes[mode](count, near, far, lambda, result);
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|