Kaynağa Gözat

Use source files instead of bundle

vtHawk 5 yıl önce
ebeveyn
işleme
90b50ac1d8

+ 0 - 531
examples/js/libs/three-csm.module.js

@@ -1,531 +0,0 @@
-import { Vector3, ShaderChunk, DirectionalLight, Vector2, LineBasicMaterial, Object3D, Geometry, Line } from '../../../build/three.module.js';
-
-class FrustumVertex {
-	constructor(x, y, z) {
-		this.x = x || 0;
-		this.y = y || 0;
-		this.z = z || 0;
-	}
-
-	fromLerp(v1, v2, amount) {
-		this.x = (1 - amount) * v1.x + amount * v2.x;
-		this.y = (1 - amount) * v1.y + amount * v2.y;
-		this.z = (1 - amount) * v1.z + amount * v2.z;
-
-		return this;
-	}
-}
-
-function toRad(degrees) {
-	return degrees * Math.PI / 180;
-}
-
-class Frustum {
-	constructor(data) {
-		data = data || {};
-
-		this.fov = data.fov || 70;
-		this.near = data.near || 0.1;
-		this.far = data.far || 1000;
-		this.aspect = data.aspect || 1;
-
-		this.vertices = {
-			near: [],
-			far: []
-		};
-	}
-
-	getViewSpaceVertices() {
-		this.nearPlaneY = this.near * Math.tan(toRad(this.fov / 2));
-		this.nearPlaneX = this.aspect * this.nearPlaneY;
-
-		this.farPlaneY = this.far * Math.tan(toRad(this.fov / 2));
-		this.farPlaneX = this.aspect * this.farPlaneY;
-
-		// 3 --- 0  vertices.near/far order
-		// |     |
-		// 2 --- 1
-
-		this.vertices.near.push(
-			new FrustumVertex(this.nearPlaneX, this.nearPlaneY, -this.near),
-			new FrustumVertex(this.nearPlaneX, -this.nearPlaneY, -this.near),
-			new FrustumVertex(-this.nearPlaneX, -this.nearPlaneY, -this.near),
-			new FrustumVertex(-this.nearPlaneX, this.nearPlaneY, -this.near)
-		);
-
-		this.vertices.far.push(
-			new FrustumVertex(this.farPlaneX, this.farPlaneY, -this.far),
-			new FrustumVertex(this.farPlaneX, -this.farPlaneY, -this.far),
-			new FrustumVertex(-this.farPlaneX, -this.farPlaneY, -this.far),
-			new FrustumVertex(-this.farPlaneX, this.farPlaneY, -this.far)
-		);
-
-		return this.vertices;
-	}
-
-	split(breaks) {
-		const result = [];
-
-		for(let i = 0; i < breaks.length; i++) {
-			const cascade = new Frustum();
-
-			if(i === 0) {
-				cascade.vertices.near = this.vertices.near;
-			} else {
-				for(let j = 0; j < 4; j++) {
-					cascade.vertices.near.push(new FrustumVertex().fromLerp(this.vertices.near[j], this.vertices.far[j], breaks[i - 1]));
-				}
-			}
-
-			if(i === breaks - 1) {
-				cascade.vertices.far = this.vertices.far;
-			} else {
-				for(let j = 0; j < 4; j++) {
-					cascade.vertices.far.push(new FrustumVertex().fromLerp(this.vertices.near[j], this.vertices.far[j], breaks[i]));
-				}
-			}
-
-			result.push(cascade);
-		}
-
-		return result;
-	}
-
-	toSpace(cameraMatrix) {
-		const result = new Frustum();
-		const point = new Vector3();
-
-		for(var i = 0; i < 4; i++) {
-			point.set(this.vertices.near[i].x, this.vertices.near[i].y, this.vertices.near[i].z);
-			point.applyMatrix4(cameraMatrix);
-			result.vertices.near.push(new FrustumVertex(point.x, point.y, point.z));
-
-			point.set(this.vertices.far[i].x, this.vertices.far[i].y, this.vertices.far[i].z);
-			point.applyMatrix4(cameraMatrix);
-			result.vertices.far.push(new FrustumVertex(point.x, point.y, point.z));
-		}
-
-		return result;
-	}
-}
-
-class FrustumBoundingBox {
-	constructor() {
-		this.min = {
-			x: 0,
-			y: 0,
-			z: 0
-		};
-		this.max = {
-			x: 0,
-			y: 0,
-			z: 0
-		};
-	}
-
-	fromFrustum(frustum) {
-		const vertices = [];
-
-		for(let i = 0; i < 4; i++) {
-			vertices.push(frustum.vertices.near[i]);
-			vertices.push(frustum.vertices.far[i]);
-		}
-
-		this.min = {
-			x: vertices[0].x,
-			y: vertices[0].y,
-			z: vertices[0].z
-		};
-		this.max = {
-			x: vertices[0].x,
-			y: vertices[0].y,
-			z: vertices[0].z
-		};
-
-		for(let i = 1; i < 8; i++) {
-			this.min.x = Math.min(this.min.x, vertices[i].x);
-			this.min.y = Math.min(this.min.y, vertices[i].y);
-			this.min.z = Math.min(this.min.z, vertices[i].z);
-			this.max.x = Math.max(this.max.x, vertices[i].x);
-			this.max.y = Math.max(this.max.y, vertices[i].y);
-			this.max.z = Math.max(this.max.z, vertices[i].z);
-		}
-
-		return this;
-	}
-
-	getSize() {
-		this.size = {
-			x: this.max.x - this.min.x,
-			y: this.max.y - this.min.y,
-			z: this.max.z - this.min.z
-		};
-
-		return this.size;
-	}
-
-	getCenter(margin) {
-		this.center = {
-			x: (this.max.x + this.min.x) / 2,
-			y: (this.max.y + this.min.y) / 2,
-			z: this.max.z + margin
-		};
-
-		return this.center;
-	}
-}
-
-var Shader = {
-	lights_fragment_begin: `
-GeometricContext geometry;
-geometry.position = - vViewPosition;
-geometry.normal = normal;
-geometry.viewDir = normalize( vViewPosition );
-#ifdef CLEARCOAT
-	geometry.clearcoatNormal = clearcoatNormal;
-#endif
-IncidentLight directLight;
-#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
-	PointLight pointLight;
-	#pragma unroll_loop
-	for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
-		pointLight = pointLights[ i ];
-		getPointDirectLightIrradiance( pointLight, geometry, directLight );
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
-		directLight.color *= all( bvec3( pointLight.shadow, directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;
-		#endif
-		RE_Direct( directLight, geometry, material, reflectedLight );
-	}
-#endif
-#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )
-	SpotLight spotLight;
-	#pragma unroll_loop
-	for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
-		spotLight = spotLights[ i ];
-		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
-		directLight.color *= all( bvec3( spotLight.shadow, directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;
-		#endif
-		RE_Direct( directLight, geometry, material, reflectedLight );
-	}
-#endif
-#if ( NUM_DIR_LIGHTS > 0) && defined( RE_Direct ) && defined( USE_CSM ) && defined( CSM_CASCADES )
-	DirectionalLight directionalLight;
-	float linearDepth = (vViewPosition.z) / (shadowFar - cameraNear);
-
-	#pragma unroll_loop
-	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
-		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-		if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= all( bvec3( directionalLight.shadow, directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
-		#endif
-		if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometry, material, reflectedLight );
-	}
-#endif
-#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && !defined( USE_CSM ) && !defined( CSM_CASCADES )
-	DirectionalLight directionalLight;
-	#pragma unroll_loop
-	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
-		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-		directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
-		#endif
-		RE_Direct( directLight, geometry, material, reflectedLight );
-	}
-#endif
-#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
-	RectAreaLight rectAreaLight;
-	#pragma unroll_loop
-	for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
-		rectAreaLight = rectAreaLights[ i ];
-		RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );
-	}
-#endif
-#if defined( RE_IndirectDiffuse )
-	vec3 iblIrradiance = vec3( 0.0 );
-	vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
-	irradiance += getLightProbeIrradiance( lightProbe, geometry );
-	#if ( NUM_HEMI_LIGHTS > 0 )
-		#pragma unroll_loop
-		for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
-			irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );
-		}
-	#endif
-#endif
-#if defined( RE_IndirectSpecular )
-	vec3 radiance = vec3( 0.0 );
-	vec3 clearcoatRadiance = vec3( 0.0 );
-#endif
-`,
-	lights_pars_begin: `
-#if defined( USE_CSM ) && defined( CSM_CASCADES )
-uniform vec2 CSM_cascades[CSM_CASCADES];
-uniform float cameraNear;
-uniform float shadowFar;
-#endif
-	` + ShaderChunk.lights_pars_begin
-};
-
-class CSM {
-	constructor(data) {
-		data = data || {};
-
-		this.camera = data.camera;
-		this.parent = data.parent;
-		this.fov = data.fov || this.camera.fov;
-		this.near = this.camera.near;
-		this.far = data.far || this.camera.far;
-		this.aspect = data.aspect || this.camera.aspect;
-		this.cascades = data.cascades || 3;
-		this.mode = data.mode || 'practical';
-		this.shadowMapSize = data.shadowMapSize || 2048;
-		this.shadowBias = data.shadowBias || 0.000001;
-		this.lightDirection = data.lightDirection || new Vector3(1, -1, 1).normalize();
-		this.lightIntensity = data.lightIntensity || 1;
-		this.lightNear = data.lightNear || 1;
-		this.lightFar = data.lightFar || 2000;
-		this.lightMargin = data.lightMargin || 200;
-		this.customSplitsCallback = data.customSplitsCallback;
-
-		this.lights = [];
-		this.materials = [];
-		this.createLights();
-
-		this.getBreaks();
-		this.initCascades();
-
-		this.injectInclude();
-	}
-
-	createLights() {
-		for(let i = 0; i < this.cascades; i++) {
-			const light = new DirectionalLight(0xffffff, this.lightIntensity);
-			light.castShadow = true;
-			light.shadow.mapSize.width = this.shadowMapSize;
-			light.shadow.mapSize.height = this.shadowMapSize;
-
-			light.shadow.camera.near = this.lightNear;
-			light.shadow.camera.far = this.lightFar;
-			light.shadow.bias = this.shadowBias;
-
-			this.parent.add(light);
-			this.parent.add(light.target);
-			this.lights.push(light);
-		}
-	}
-
-	initCascades() {
-		this.mainFrustum = new Frustum({
-			fov: this.fov,
-			near: this.near,
-			far: this.far,
-			aspect: this.aspect
-		});
-
-		this.mainFrustum.getViewSpaceVertices();
-
-		this.frustums = this.mainFrustum.split(this.breaks);
-	}
-
-	getBreaks() {
-		this.breaks = [];
-
-		switch (this.mode) {
-			case 'uniform':
-				this.breaks = uniformSplit(this.cascades, this.near, this.far);
-				break;
-			case 'logarithmic':
-				this.breaks = logarithmicSplit(this.cascades, this.near, this.far);
-				break;
-			case 'practical':
-				this.breaks = practicalSplit(this.cascades, this.near, this.far, 0.5);
-				break;
-			case 'custom':
-				if(this.customSplitsCallback === undefined) console.error('CSM: Custom split scheme callback not defined.');
-				this.breaks = this.customSplitsCallback(this.cascades, this.near, this.far);
-				break;
-		}
-
-		function uniformSplit(amount, near, far) {
-			const r = [];
-
-			for(let i = 1; i < amount; i++) {
-				r.push((near + (far - near) * i / amount) / far);
-			}
-
-			r.push(1);
-			return r;
-		}
-
-		function logarithmicSplit(amount, near, far) {
-			const r = [];
-
-			for(let i = 1; i < amount; i++) {
-				r.push((near * (far / near) ** (i / amount)) / far);
-			}
-
-			r.push(1);
-			return r;
-		}
-
-		function practicalSplit(amount, near, far, lambda) {
-			const log = logarithmicSplit(amount, near, far);
-			const uni = uniformSplit(amount, near, far);
-			const r = [];
-
-			for(let i = 1; i < amount; i++) {
-				r.push(lambda * log[i - 1] + (1 - lambda) * uni[i - 1]);
-			}
-
-			r.push(1);
-			return r;
-		}
-	}
-
-	update(cameraMatrix) {
-		for(let i = 0; i < this.frustums.length; i++) {
-			const worldSpaceFrustum = this.frustums[i].toSpace(cameraMatrix);
-			const light = this.lights[i];
-			const lightSpaceFrustum = worldSpaceFrustum.toSpace(light.shadow.camera.matrixWorldInverse);
-
-			light.shadow.camera.updateMatrixWorld(true);
-
-			const bbox = new FrustumBoundingBox().fromFrustum(lightSpaceFrustum);
-			bbox.getSize();
-			bbox.getCenter(this.lightMargin);
-
-			const squaredBBWidth = Math.max(bbox.size.x, bbox.size.y);
-
-			let center = new Vector3(bbox.center.x, bbox.center.y, bbox.center.z);
-			center.applyMatrix4(light.shadow.camera.matrixWorld);
-
-			light.shadow.camera.left = -squaredBBWidth / 2;
-			light.shadow.camera.right = squaredBBWidth / 2;
-			light.shadow.camera.top = squaredBBWidth / 2;
-			light.shadow.camera.bottom = -squaredBBWidth / 2;
-
-			light.position.copy(center);
-			light.target.position.copy(center);
-
-			light.target.position.x += this.lightDirection.x;
-			light.target.position.y += this.lightDirection.y;
-			light.target.position.z += this.lightDirection.z;
-
-			light.shadow.camera.updateProjectionMatrix();
-			light.shadow.camera.updateMatrixWorld();
-		}
-	}
-
-	injectInclude() {
-		ShaderChunk.lights_fragment_begin = Shader.lights_fragment_begin;
-		ShaderChunk.lights_pars_begin = Shader.lights_pars_begin;
-	}
-
-	setupMaterial(material) {
-		material.defines = material.defines || {};
-		material.defines.USE_CSM = 1;
-		material.defines.CSM_CASCADES = this.cascades;
-
-		const breaksVec2 = [];
-
-		for(let i = 0; i < this.cascades; i++) {
-			let amount = this.breaks[i];
-			let prev = this.breaks[i - 1] || 0;
-			breaksVec2.push(new Vector2(prev, amount));
-		}
-
-		const self = this;
-
-		material.onBeforeCompile = function (shader) {
-			shader.uniforms.CSM_cascades = {value: breaksVec2};
-			shader.uniforms.cameraNear = {value: self.camera.near};
-			shader.uniforms.shadowFar = {value: self.far};
-
-			self.materials.push(shader);
-		};
-	}
-
-	updateUniforms() {
-		for(let i = 0; i < this.materials.length; i++) {
-			this.materials[i].uniforms.CSM_cascades.value = this.getExtendedBreaks();
-			this.materials[i].uniforms.cameraNear.value = this.camera.near;
-			this.materials[i].uniforms.shadowFar.value = this.far;
-		}
-	}
-
-	getExtendedBreaks() {
-		let breaksVec2 = [];
-
-		for(let i = 0; i < this.cascades; i++) {
-			let amount = this.breaks[i];
-			let prev = this.breaks[i - 1] || 0;
-			breaksVec2.push(new Vector2(prev, amount));
-		}
-
-		return breaksVec2;
-	}
-
-	setAspect(aspect) {
-		this.aspect = aspect;
-		this.initCascades();
-	}
-
-	updateFrustums() {
-		this.getBreaks();
-		this.initCascades();
-		this.updateUniforms();
-	}
-
-	helper(cameraMatrix) {
-		let frustum;
-		let geometry;
-		const material = new LineBasicMaterial({color: 0xffffff});
-		const object = new Object3D();
-
-		for(let i = 0; i < this.frustums.length; i++) {
-			frustum = this.frustums[i].toSpace(cameraMatrix);
-
-			geometry = new Geometry();
-
-			for(let i = 0; i < 5; i++) {
-				const point = frustum.vertices.near[i === 4 ? 0 : i];
-				geometry.vertices.push(new Vector3(point.x, point.y, point.z));
-			}
-
-			object.add(new Line(geometry, material));
-
-			geometry = new Geometry();
-
-			for(let i = 0; i < 5; i++) {
-				const point = frustum.vertices.far[i === 4 ? 0 : i];
-				geometry.vertices.push(new Vector3(point.x, point.y, point.z));
-			}
-
-			object.add(new Line(geometry, material));
-
-			for(let i = 0; i < 4; i++) {
-				geometry = new Geometry();
-
-				const near = frustum.vertices.near[i];
-				const far = frustum.vertices.far[i];
-
-				geometry.vertices.push(new Vector3(near.x, near.y, near.z));
-				geometry.vertices.push(new Vector3(far.x, far.y, far.z));
-
-				object.add(new Line(geometry, material));
-			}
-		}
-
-		return object;
-	}
-
-	remove() {
-		for(let i = 0; i < this.lights.length; i++) {
-			this.parent.remove(this.lights[i]);
-		}
-	}
-}
-
-export default CSM;

+ 264 - 0
examples/jsm/csm/CSM.js

@@ -0,0 +1,264 @@
+import * as THREE from '../../../build/three.module.js';
+import Frustum from './Frustum.js';
+import FrustumBoundingBox from './FrustumBoundingBox.js';
+import Shader from './Shader.js';
+
+export default class CSM {
+	constructor(data) {
+		data = data || {};
+
+		this.camera = data.camera;
+		this.parent = data.parent;
+		this.fov = data.fov || this.camera.fov;
+		this.near = this.camera.near;
+		this.far = data.far || this.camera.far;
+		this.aspect = data.aspect || this.camera.aspect;
+		this.cascades = data.cascades || 3;
+		this.mode = data.mode || 'practical';
+		this.shadowMapSize = data.shadowMapSize || 2048;
+		this.shadowBias = data.shadowBias || 0.000001;
+		this.lightDirection = data.lightDirection || new THREE.Vector3(1, -1, 1).normalize();
+		this.lightIntensity = data.lightIntensity || 1;
+		this.lightNear = data.lightNear || 1;
+		this.lightFar = data.lightFar || 2000;
+		this.lightMargin = data.lightMargin || 200;
+		this.customSplitsCallback = data.customSplitsCallback;
+
+		this.lights = [];
+		this.materials = [];
+		this.createLights();
+
+		this.getBreaks();
+		this.initCascades();
+
+		this.injectInclude();
+	}
+
+	createLights() {
+		for(let i = 0; i < this.cascades; i++) {
+			const light = new THREE.DirectionalLight(0xffffff, this.lightIntensity);
+			light.castShadow = true;
+			light.shadow.mapSize.width = this.shadowMapSize;
+			light.shadow.mapSize.height = this.shadowMapSize;
+
+			light.shadow.camera.near = this.lightNear;
+			light.shadow.camera.far = this.lightFar;
+			light.shadow.bias = this.shadowBias;
+
+			this.parent.add(light);
+			this.parent.add(light.target);
+			this.lights.push(light);
+		}
+	}
+
+	initCascades() {
+		this.mainFrustum = new Frustum({
+			fov: this.fov,
+			near: this.near,
+			far: this.far,
+			aspect: this.aspect
+		});
+
+		this.mainFrustum.getViewSpaceVertices();
+
+		this.frustums = this.mainFrustum.split(this.breaks);
+	}
+
+	getBreaks() {
+		this.breaks = [];
+
+		switch (this.mode) {
+			case 'uniform':
+				this.breaks = uniformSplit(this.cascades, this.near, this.far);
+				break;
+			case 'logarithmic':
+				this.breaks = logarithmicSplit(this.cascades, this.near, this.far);
+				break;
+			case 'practical':
+				this.breaks = practicalSplit(this.cascades, this.near, this.far, 0.5);
+				break;
+			case 'custom':
+				if(this.customSplitsCallback === undefined) console.error('CSM: Custom split scheme callback not defined.');
+				this.breaks = this.customSplitsCallback(this.cascades, this.near, this.far);
+				break;
+		}
+
+		function uniformSplit(amount, near, far) {
+			const r = [];
+
+			for(let i = 1; i < amount; i++) {
+				r.push((near + (far - near) * i / amount) / far);
+			}
+
+			r.push(1);
+			return r;
+		}
+
+		function logarithmicSplit(amount, near, far) {
+			const r = [];
+
+			for(let i = 1; i < amount; i++) {
+				r.push((near * (far / near) ** (i / amount)) / far);
+			}
+
+			r.push(1);
+			return r;
+		}
+
+		function practicalSplit(amount, near, far, lambda) {
+			const log = logarithmicSplit(amount, near, far);
+			const uni = uniformSplit(amount, near, far);
+			const r = [];
+
+			for(let i = 1; i < amount; i++) {
+				r.push(lambda * log[i - 1] + (1 - lambda) * uni[i - 1]);
+			}
+
+			r.push(1);
+			return r;
+		}
+	}
+
+	update(cameraMatrix) {
+		for(let i = 0; i < this.frustums.length; i++) {
+			const worldSpaceFrustum = this.frustums[i].toSpace(cameraMatrix);
+			const light = this.lights[i];
+			const lightSpaceFrustum = worldSpaceFrustum.toSpace(light.shadow.camera.matrixWorldInverse);
+
+			light.shadow.camera.updateMatrixWorld(true);
+
+			const bbox = new FrustumBoundingBox().fromFrustum(lightSpaceFrustum);
+			bbox.getSize();
+			bbox.getCenter(this.lightMargin);
+
+			const squaredBBWidth = Math.max(bbox.size.x, bbox.size.y);
+
+			let center = new THREE.Vector3(bbox.center.x, bbox.center.y, bbox.center.z);
+			center.applyMatrix4(light.shadow.camera.matrixWorld);
+
+			light.shadow.camera.left = -squaredBBWidth / 2;
+			light.shadow.camera.right = squaredBBWidth / 2;
+			light.shadow.camera.top = squaredBBWidth / 2;
+			light.shadow.camera.bottom = -squaredBBWidth / 2;
+
+			light.position.copy(center);
+			light.target.position.copy(center);
+
+			light.target.position.x += this.lightDirection.x;
+			light.target.position.y += this.lightDirection.y;
+			light.target.position.z += this.lightDirection.z;
+
+			light.shadow.camera.updateProjectionMatrix();
+			light.shadow.camera.updateMatrixWorld();
+		}
+	}
+
+	injectInclude() {
+		THREE.ShaderChunk.lights_fragment_begin = Shader.lights_fragment_begin;
+		THREE.ShaderChunk.lights_pars_begin = Shader.lights_pars_begin;
+	}
+
+	setupMaterial(material) {
+		material.defines = material.defines || {};
+		material.defines.USE_CSM = 1;
+		material.defines.CSM_CASCADES = this.cascades;
+
+		const breaksVec2 = [];
+
+		for(let i = 0; i < this.cascades; i++) {
+			let amount = this.breaks[i];
+			let prev = this.breaks[i - 1] || 0;
+			breaksVec2.push(new THREE.Vector2(prev, amount));
+		}
+
+		const self = this;
+
+		material.onBeforeCompile = function (shader) {
+			shader.uniforms.CSM_cascades = {value: breaksVec2};
+			shader.uniforms.cameraNear = {value: self.camera.near};
+			shader.uniforms.shadowFar = {value: self.far};
+
+			self.materials.push(shader);
+		};
+	}
+
+	updateUniforms() {
+		for(let i = 0; i < this.materials.length; i++) {
+			this.materials[i].uniforms.CSM_cascades.value = this.getExtendedBreaks();
+			this.materials[i].uniforms.cameraNear.value = this.camera.near;
+			this.materials[i].uniforms.shadowFar.value = this.far;
+		}
+	}
+
+	getExtendedBreaks() {
+		let breaksVec2 = [];
+
+		for(let i = 0; i < this.cascades; i++) {
+			let amount = this.breaks[i];
+			let prev = this.breaks[i - 1] || 0;
+			breaksVec2.push(new THREE.Vector2(prev, amount));
+		}
+
+		return breaksVec2;
+	}
+
+	setAspect(aspect) {
+		this.aspect = aspect;
+		this.initCascades();
+	}
+
+	updateFrustums() {
+		this.getBreaks();
+		this.initCascades();
+		this.updateUniforms();
+	}
+
+	helper(cameraMatrix) {
+		let frustum;
+		let geometry;
+		const material = new THREE.LineBasicMaterial({color: 0xffffff});
+		const object = new THREE.Object3D();
+
+		for(let i = 0; i < this.frustums.length; i++) {
+			frustum = this.frustums[i].toSpace(cameraMatrix);
+
+			geometry = new THREE.Geometry();
+
+			for(let i = 0; i < 5; i++) {
+				const point = frustum.vertices.near[i === 4 ? 0 : i];
+				geometry.vertices.push(new THREE.Vector3(point.x, point.y, point.z));
+			}
+
+			object.add(new THREE.Line(geometry, material));
+
+			geometry = new THREE.Geometry();
+
+			for(let i = 0; i < 5; i++) {
+				const point = frustum.vertices.far[i === 4 ? 0 : i];
+				geometry.vertices.push(new THREE.Vector3(point.x, point.y, point.z));
+			}
+
+			object.add(new THREE.Line(geometry, material));
+
+			for(let i = 0; i < 4; i++) {
+				geometry = new THREE.Geometry();
+
+				const near = frustum.vertices.near[i];
+				const far = frustum.vertices.far[i];
+
+				geometry.vertices.push(new THREE.Vector3(near.x, near.y, near.z));
+				geometry.vertices.push(new THREE.Vector3(far.x, far.y, far.z));
+
+				object.add(new THREE.Line(geometry, material));
+			}
+		}
+
+		return object;
+	}
+
+	remove() {
+		for(let i = 0; i < this.lights.length; i++) {
+			this.parent.remove(this.lights[i]);
+		}
+	}
+}

+ 92 - 0
examples/jsm/csm/Frustum.js

@@ -0,0 +1,92 @@
+import * as THREE from '../../../build/three.module.js';
+import FrustumVertex from './FrustumVertex.js';
+import {toRad} from './Utils.js';
+
+export default class Frustum {
+	constructor(data) {
+		data = data || {};
+
+		this.fov = data.fov || 70;
+		this.near = data.near || 0.1;
+		this.far = data.far || 1000;
+		this.aspect = data.aspect || 1;
+
+		this.vertices = {
+			near: [],
+			far: []
+		};
+	}
+
+	getViewSpaceVertices() {
+		this.nearPlaneY = this.near * Math.tan(toRad(this.fov / 2));
+		this.nearPlaneX = this.aspect * this.nearPlaneY;
+
+		this.farPlaneY = this.far * Math.tan(toRad(this.fov / 2));
+		this.farPlaneX = this.aspect * this.farPlaneY;
+
+		// 3 --- 0  vertices.near/far order
+		// |     |
+		// 2 --- 1
+
+		this.vertices.near.push(
+			new FrustumVertex(this.nearPlaneX, this.nearPlaneY, -this.near),
+			new FrustumVertex(this.nearPlaneX, -this.nearPlaneY, -this.near),
+			new FrustumVertex(-this.nearPlaneX, -this.nearPlaneY, -this.near),
+			new FrustumVertex(-this.nearPlaneX, this.nearPlaneY, -this.near)
+		);
+
+		this.vertices.far.push(
+			new FrustumVertex(this.farPlaneX, this.farPlaneY, -this.far),
+			new FrustumVertex(this.farPlaneX, -this.farPlaneY, -this.far),
+			new FrustumVertex(-this.farPlaneX, -this.farPlaneY, -this.far),
+			new FrustumVertex(-this.farPlaneX, this.farPlaneY, -this.far)
+		);
+
+		return this.vertices;
+	}
+
+	split(breaks) {
+		const result = [];
+
+		for(let i = 0; i < breaks.length; i++) {
+			const cascade = new Frustum();
+
+			if(i === 0) {
+				cascade.vertices.near = this.vertices.near;
+			} else {
+				for(let j = 0; j < 4; j++) {
+					cascade.vertices.near.push(new FrustumVertex().fromLerp(this.vertices.near[j], this.vertices.far[j], breaks[i - 1]));
+				}
+			}
+
+			if(i === breaks - 1) {
+				cascade.vertices.far = this.vertices.far;
+			} else {
+				for(let j = 0; j < 4; j++) {
+					cascade.vertices.far.push(new FrustumVertex().fromLerp(this.vertices.near[j], this.vertices.far[j], breaks[i]))
+				}
+			}
+
+			result.push(cascade);
+		}
+
+		return result;
+	}
+
+	toSpace(cameraMatrix) {
+		const result = new Frustum();
+		const point = new THREE.Vector3();
+
+		for(var i = 0; i < 4; i++) {
+			point.set(this.vertices.near[i].x, this.vertices.near[i].y, this.vertices.near[i].z);
+			point.applyMatrix4(cameraMatrix);
+			result.vertices.near.push(new FrustumVertex(point.x, point.y, point.z));
+
+			point.set(this.vertices.far[i].x, this.vertices.far[i].y, this.vertices.far[i].z);
+			point.applyMatrix4(cameraMatrix);
+			result.vertices.far.push(new FrustumVertex(point.x, point.y, point.z));
+		}
+
+		return result;
+	}
+}

+ 65 - 0
examples/jsm/csm/FrustumBoundingBox.js

@@ -0,0 +1,65 @@
+export default class FrustumBoundingBox {
+	constructor() {
+		this.min = {
+			x: 0,
+			y: 0,
+			z: 0
+		};
+		this.max = {
+			x: 0,
+			y: 0,
+			z: 0
+		};
+	}
+
+	fromFrustum(frustum) {
+		const vertices = [];
+
+		for(let i = 0; i < 4; i++) {
+			vertices.push(frustum.vertices.near[i]);
+			vertices.push(frustum.vertices.far[i]);
+		}
+
+		this.min = {
+			x: vertices[0].x,
+			y: vertices[0].y,
+			z: vertices[0].z
+		};
+		this.max = {
+			x: vertices[0].x,
+			y: vertices[0].y,
+			z: vertices[0].z
+		};
+
+		for(let i = 1; i < 8; i++) {
+			this.min.x = Math.min(this.min.x, vertices[i].x);
+			this.min.y = Math.min(this.min.y, vertices[i].y);
+			this.min.z = Math.min(this.min.z, vertices[i].z);
+			this.max.x = Math.max(this.max.x, vertices[i].x);
+			this.max.y = Math.max(this.max.y, vertices[i].y);
+			this.max.z = Math.max(this.max.z, vertices[i].z);
+		}
+		
+		return this;
+	}
+
+	getSize() {
+		this.size = {
+			x: this.max.x - this.min.x,
+			y: this.max.y - this.min.y,
+			z: this.max.z - this.min.z
+		};
+
+		return this.size;
+	}
+
+	getCenter(margin) {
+		this.center = {
+			x: (this.max.x + this.min.x) / 2,
+			y: (this.max.y + this.min.y) / 2,
+			z: this.max.z + margin
+		};
+
+		return this.center;
+	}
+}

+ 15 - 0
examples/jsm/csm/FrustumVertex.js

@@ -0,0 +1,15 @@
+export default class FrustumVertex {
+	constructor(x, y, z) {
+		this.x = x || 0;
+		this.y = y || 0;
+		this.z = z || 0;
+	}
+
+	fromLerp(v1, v2, amount) {
+		this.x = (1 - amount) * v1.x + amount * v2.x;
+		this.y = (1 - amount) * v1.y + amount * v2.y;
+		this.z = (1 - amount) * v1.z + amount * v2.z;
+
+		return this;
+	}
+}

+ 94 - 0
examples/jsm/csm/Shader.js

@@ -0,0 +1,94 @@
+import * as THREE from '../../../build/three.module.js';
+
+export default {
+	lights_fragment_begin: `
+GeometricContext geometry;
+geometry.position = - vViewPosition;
+geometry.normal = normal;
+geometry.viewDir = normalize( vViewPosition );
+#ifdef CLEARCOAT
+	geometry.clearcoatNormal = clearcoatNormal;
+#endif
+IncidentLight directLight;
+#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
+	PointLight pointLight;
+	#pragma unroll_loop
+	for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
+		pointLight = pointLights[ i ];
+		getPointDirectLightIrradiance( pointLight, geometry, directLight );
+		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
+		directLight.color *= all( bvec3( pointLight.shadow, directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;
+		#endif
+		RE_Direct( directLight, geometry, material, reflectedLight );
+	}
+#endif
+#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )
+	SpotLight spotLight;
+	#pragma unroll_loop
+	for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
+		spotLight = spotLights[ i ];
+		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
+		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
+		directLight.color *= all( bvec3( spotLight.shadow, directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;
+		#endif
+		RE_Direct( directLight, geometry, material, reflectedLight );
+	}
+#endif
+#if ( NUM_DIR_LIGHTS > 0) && defined( RE_Direct ) && defined( USE_CSM ) && defined( CSM_CASCADES )
+	DirectionalLight directionalLight;
+	float linearDepth = (vViewPosition.z) / (shadowFar - cameraNear);
+
+	#pragma unroll_loop
+	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
+		directionalLight = directionalLights[ i ];
+		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
+		if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= all( bvec3( directionalLight.shadow, directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
+		#endif
+		if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometry, material, reflectedLight );
+	}
+#endif
+#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && !defined( USE_CSM ) && !defined( CSM_CASCADES )
+	DirectionalLight directionalLight;
+	#pragma unroll_loop
+	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
+		directionalLight = directionalLights[ i ];
+		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
+		directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
+		#endif
+		RE_Direct( directLight, geometry, material, reflectedLight );
+	}
+#endif
+#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
+	RectAreaLight rectAreaLight;
+	#pragma unroll_loop
+	for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
+		rectAreaLight = rectAreaLights[ i ];
+		RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );
+	}
+#endif
+#if defined( RE_IndirectDiffuse )
+	vec3 iblIrradiance = vec3( 0.0 );
+	vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
+	irradiance += getLightProbeIrradiance( lightProbe, geometry );
+	#if ( NUM_HEMI_LIGHTS > 0 )
+		#pragma unroll_loop
+		for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
+			irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );
+		}
+	#endif
+#endif
+#if defined( RE_IndirectSpecular )
+	vec3 radiance = vec3( 0.0 );
+	vec3 clearcoatRadiance = vec3( 0.0 );
+#endif
+`,
+	lights_pars_begin: `
+#if defined( USE_CSM ) && defined( CSM_CASCADES )
+uniform vec2 CSM_cascades[CSM_CASCADES];
+uniform float cameraNear;
+uniform float shadowFar;
+#endif
+	` + THREE.ShaderChunk.lights_pars_begin
+}

+ 3 - 0
examples/jsm/csm/Utils.js

@@ -0,0 +1,3 @@
+export function toRad(degrees) {
+	return degrees * Math.PI / 180;
+}

+ 1 - 1
examples/webgl_cascadedshadowmaps.html

@@ -20,7 +20,7 @@
 
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { GUI } from './jsm/libs/dat.gui.module.js';
-			import * as CSM from './js/libs/three-csm.module.js';
+			import * as CSM from './jsm/csm/CSM.js';
 
 			var renderer, scene, camera, controls, csm;
 

+ 0 - 2
utils/modularize.js

@@ -58,8 +58,6 @@ var files = [
 	{ path: 'interactive/SelectionBox.js', dependencies: [], ignoreList: [] },
 	{ path: 'interactive/SelectionHelper.js', dependencies: [], ignoreList: [] },
 
-	{ path: 'libs/three-csm.module.js', dependencies: [], ignoreList: [] },
-
 	{ path: 'lights/LightProbeGenerator.js', dependencies: [], ignoreList: [] },
 	{ path: 'lights/RectAreaLightUniformsLib.js', dependencies: [], ignoreList: [] },