Преглед изворни кода

WebGLRenderer: Modernized shadow mapping: native cube depth textures, Vogel disk sampling, and VSM improvements (#32303)

mrdoob пре 1 месец
родитељ
комит
477e43575a
42 измењених фајлова са 642 додато и 346 уклоњено
  1. 0 1
      editor/js/Sidebar.Project.Renderer.js
  2. 0 1
      examples/jsm/materials/MeshGouraudMaterial.js
  3. 2 4
      examples/jsm/shaders/UnpackDepthRGBAShader.js
  4. 24 10
      examples/jsm/utils/ShadowMapViewer.js
  5. 1 1
      examples/webgl_animation_walk.html
  6. 2 4
      examples/webgl_depth_texture.html
  7. 1 1
      examples/webgl_geometry_csg.html
  8. 1 1
      examples/webgl_lights_spotlight.html
  9. 1 1
      examples/webgl_lights_spotlights.html
  10. 1 1
      examples/webgl_loader_3mf_materials.html
  11. 1 1
      examples/webgl_loader_md2_control.html
  12. 1 1
      examples/webgl_shadowmap_csm.html
  13. 8 6
      examples/webgl_shadowmap_pcss.html
  14. 1 1
      examples/webgl_shadowmap_performance.html
  15. 2 2
      examples/webgl_shadowmap_pointlight.html
  16. 1 0
      src/Three.Core.js
  17. 12 4
      src/constants.js
  18. 0 3
      src/lights/LightShadow.js
  19. 8 42
      src/lights/PointLightShadow.js
  20. 25 1
      src/renderers/WebGLRenderer.js
  21. 1 0
      src/renderers/shaders/DFGLUTData.js
  22. 3 3
      src/renderers/shaders/ShaderChunk.js
  23. 1 1
      src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js
  24. 191 190
      src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js
  25. 1 1
      src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js
  26. 3 3
      src/renderers/shaders/ShaderLib.js
  27. 7 0
      src/renderers/shaders/ShaderLib/depth.glsl.js
  28. 1 2
      src/renderers/shaders/ShaderLib/distance.glsl.js
  29. 0 1
      src/renderers/shaders/ShaderLib/meshlambert.glsl.js
  30. 1 2
      src/renderers/shaders/ShaderLib/meshnormal.glsl.js
  31. 0 1
      src/renderers/shaders/ShaderLib/meshphong.glsl.js
  32. 0 1
      src/renderers/shaders/ShaderLib/meshphysical.glsl.js
  33. 0 1
      src/renderers/shaders/ShaderLib/meshtoon.glsl.js
  34. 0 1
      src/renderers/shaders/ShaderLib/shadow.glsl.js
  35. 3 5
      src/renderers/shaders/ShaderLib/vsm.glsl.js
  36. 1 1
      src/renderers/webgl/WebGLLights.js
  37. 1 5
      src/renderers/webgl/WebGLProgram.js
  38. 1 1
      src/renderers/webgl/WebGLPrograms.js
  39. 140 24
      src/renderers/webgl/WebGLShadowMap.js
  40. 79 14
      src/renderers/webgl/WebGLTextures.js
  41. 40 3
      src/renderers/webgl/WebGLUniforms.js
  42. 76 0
      src/textures/CubeDepthTexture.js

+ 0 - 1
editor/js/Sidebar.Project.Renderer.js

@@ -37,7 +37,6 @@ function SidebarProjectRenderer( editor ) {
 	const shadowTypeSelect = new UISelect().setOptions( {
 		0: 'Basic',
 		1: 'PCF',
-		2: 'PCF Soft',
 		3: 'VSM'
 	} ).setWidth( '125px' ).onChange( updateShadows );
 	shadowTypeSelect.setValue( config.getKey( 'project/renderer/shadowType' ) );

+ 0 - 1
examples/jsm/materials/MeshGouraudMaterial.js

@@ -221,7 +221,6 @@ const GouraudShader = {
 		#endif
 
 		#include <common>
-		#include <packing>
 		#include <dithering_pars_fragment>
 		#include <color_pars_fragment>
 		#include <uv_pars_fragment>

+ 2 - 4
examples/jsm/shaders/UnpackDepthRGBAShader.js

@@ -4,7 +4,7 @@
  */
 
 /**
- * Unpack RGBA depth shader that shows RGBA encoded depth as monochrome color.
+ * Depth visualization shader that shows depth values as monochrome color.
  *
  * @constant
  * @type {ShaderMaterial~Shader}
@@ -39,11 +39,9 @@ const UnpackDepthRGBAShader = {
 
 		varying vec2 vUv;
 
-		#include <packing>
-
 		void main() {
 
-			float depth = unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
+			float depth = texture2D( tDiffuse, vUv ).r;
 
 			#ifdef USE_REVERSED_DEPTH_BUFFER
 

+ 24 - 10
examples/jsm/utils/ShadowMapViewer.js

@@ -6,10 +6,8 @@ import {
 	OrthographicCamera,
 	PlaneGeometry,
 	Scene,
-	ShaderMaterial,
-	UniformsUtils
+	ShaderMaterial
 } from 'three';
-import { UnpackDepthRGBAShader } from '../shaders/UnpackDepthRGBAShader.js';
 
 /**
  * This is a helper for visualising a given light's shadow map.
@@ -57,13 +55,29 @@ class ShadowMapViewer {
 		const scene = new Scene();
 
 		//HUD for shadow map
-		const shader = UnpackDepthRGBAShader;
-
-		const uniforms = UniformsUtils.clone( shader.uniforms );
 		const material = new ShaderMaterial( {
-			uniforms: uniforms,
-			vertexShader: shader.vertexShader,
-			fragmentShader: shader.fragmentShader
+			uniforms: {
+				tDiffuse: { value: null },
+				opacity: { value: 1.0 }
+			},
+			vertexShader: /* glsl */`
+				varying vec2 vUv;
+				void main() {
+					vUv = uv;
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+				}`,
+			fragmentShader: /* glsl */`
+				uniform float opacity;
+				uniform sampler2D tDiffuse;
+				varying vec2 vUv;
+				void main() {
+					float depth = texture2D( tDiffuse, vUv ).r;
+					#ifdef USE_REVERSED_DEPTH_BUFFER
+						gl_FragColor = vec4( vec3( depth ), opacity );
+					#else
+						gl_FragColor = vec4( vec3( 1.0 - depth ), opacity );
+					#endif
+				}`
 		} );
 		const plane = new PlaneGeometry( frame.width, frame.height );
 		const mesh = new Mesh( plane, material );
@@ -177,7 +191,7 @@ class ShadowMapViewer {
 				//always end up with the scene's first added shadow casting light's shadowMap
 				//in the shader
 				//See: https://github.com/mrdoob/three.js/issues/5932
-				uniforms.tDiffuse.value = light.shadow.map.texture;
+				material.uniforms.tDiffuse.value = light.shadow.map.texture;
 
 				userAutoClearSetting = renderer.autoClear;
 				renderer.autoClear = false; // To allow render overlay

+ 1 - 1
examples/webgl_animation_walk.html

@@ -110,7 +110,7 @@
 				renderer.toneMapping = THREE.ACESFilmicToneMapping;
 				renderer.toneMappingExposure = 0.5;
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 				container.appendChild( renderer.domElement );
 
 				orbitControls = new OrbitControls( camera, renderer.domElement );

+ 2 - 4
examples/webgl_depth_texture.html

@@ -25,8 +25,6 @@
 			}
 		</script>
 		<script id="post-frag" type="x-shader/x-fragment">
-			#include <packing>
-
 			varying vec2 vUv;
 			uniform sampler2D tDiffuse;
 			uniform sampler2D tDepth;
@@ -36,8 +34,8 @@
 
 			float readDepth( sampler2D depthSampler, vec2 coord ) {
 				float fragCoordZ = texture2D( depthSampler, coord ).x;
-				float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
-				return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
+				float viewZ = ( cameraNear * cameraFar ) / ( ( cameraFar - cameraNear ) * fragCoordZ - cameraFar );
+				return ( viewZ + cameraNear ) / ( cameraNear - cameraFar );
 			}
 
 			void main() {

+ 1 - 1
examples/webgl_geometry_csg.html

@@ -86,7 +86,7 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 				document.body.appendChild( renderer.domElement );
 
 				stats = new Stats();

+ 1 - 1
examples/webgl_lights_spotlight.html

@@ -50,7 +50,7 @@
 				renderer.toneMappingExposure = 1;
 
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				scene = new THREE.Scene();
 

+ 1 - 1
examples/webgl_lights_spotlights.html

@@ -59,7 +59,7 @@
 			function init() {
 
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				camera.position.set( 4.6, 2.2, - 2.1 );
 

+ 1 - 1
examples/webgl_loader_3mf_materials.html

@@ -108,7 +108,7 @@
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 				document.body.appendChild( renderer.domElement );
 
 				//

+ 1 - 1
examples/webgl_loader_md2_control.html

@@ -135,7 +135,7 @@
 				//
 
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				// STATS
 

+ 1 - 1
examples/webgl_shadowmap_csm.html

@@ -84,7 +84,7 @@
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 				renderer.shadowMap.enabled = params.shadows;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls.maxPolarAngle = Math.PI / 2;

+ 8 - 6
examples/webgl_shadowmap_pcss.html

@@ -60,7 +60,7 @@
 					int numBlockers = 0;
 
 					for( int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; i++ ) {
-						float shadowMapDepth = unpackRGBAToDepth(texture2D(shadowMap, uv + poissonDisk[i] * searchRadius));
+						float shadowMapDepth = texture2D(shadowMap, uv + poissonDisk[i] * searchRadius).r;
 						if ( shadowMapDepth < zReceiver ) {
 							blockerDepthSum += shadowMapDepth;
 							numBlockers ++;
@@ -77,13 +77,13 @@
 					float depth;
 					#pragma unroll_loop_start
 					for( int i = 0; i < 17; i ++ ) {
-						depth = unpackRGBAToDepth( texture2D( shadowMap, uv + poissonDisk[ i ] * filterRadius ) );
+						depth = texture2D( shadowMap, uv + poissonDisk[ i ] * filterRadius ).r;
 						if( zReceiver <= depth ) sum += 1.0;
 					}
 					#pragma unroll_loop_end
 					#pragma unroll_loop_start
 					for( int i = 0; i < 17; i ++ ) {
-						depth = unpackRGBAToDepth( texture2D( shadowMap, uv + -poissonDisk[ i ].yx * filterRadius ) );
+						depth = texture2D( shadowMap, uv + -poissonDisk[ i ].yx * filterRadius ).r;
 						if( zReceiver <= depth ) sum += 1.0;
 					}
 					#pragma unroll_loop_end
@@ -234,9 +234,10 @@
 				);
 
 				shader = shader.replace(
-					'#if defined( SHADOWMAP_TYPE_PCF )',
-					document.getElementById( 'PCSSGetShadow' ).textContent +
-					'#if defined( SHADOWMAP_TYPE_PCF )'
+					'\t\t\tif ( frustumTest ) {\n\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;',
+					'\t\t\tif ( frustumTest ) {\n' +
+					document.getElementById( 'PCSSGetShadow' ).textContent + '\n' +
+					'\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;'
 				);
 
 				THREE.ShaderChunk.shadowmap_pars_fragment = shader;
@@ -252,6 +253,7 @@
 				container.appendChild( renderer.domElement );
 
 				renderer.shadowMap.enabled = true;
+				renderer.shadowMap.type = THREE.BasicShadowMap; // PCSS requires reading raw depth values
 
 				// controls
 				const controls = new OrbitControls( camera, renderer.domElement );

+ 1 - 1
examples/webgl_shadowmap_performance.html

@@ -107,7 +107,7 @@
 				//
 
 				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				renderer.shadowMap.type = THREE.PCFShadowMap;
 
 				// CONTROLS
 

+ 2 - 2
examples/webgl_shadowmap_pointlight.html

@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - THREE.PointLight ShadowMap</title>
+		<title>three.js webgl - PointLight ShadowMap</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
 	<body>
 		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - THREE.PointLight ShadowMap by <a href="https://github.com/mkkellogg" target="_blank" rel="noopener">mkkellogg</a>
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - PointLight ShadowMap
 		</div>
 
 		<script type="importmap">

+ 1 - 0
src/Three.Core.js

@@ -35,6 +35,7 @@ export { CompressedCubeTexture } from './textures/CompressedCubeTexture.js';
 export { CubeTexture } from './textures/CubeTexture.js';
 export { CanvasTexture } from './textures/CanvasTexture.js';
 export { DepthTexture } from './textures/DepthTexture.js';
+export { CubeDepthTexture } from './textures/CubeDepthTexture.js';
 export { ExternalTexture } from './textures/ExternalTexture.js';
 export { Texture } from './textures/Texture.js';
 export * from './geometries/Geometries.js';

+ 12 - 4
src/constants.js

@@ -1223,7 +1223,7 @@ export const TriangleStripDrawMode = 1;
 export const TriangleFanDrawMode = 2;
 
 /**
- * Basic depth packing.
+ * The depth value is inverted (1.0 - z) for visualization purposes.
  *
  * @type {number}
  * @constant
@@ -1231,7 +1231,7 @@ export const TriangleFanDrawMode = 2;
 export const BasicDepthPacking = 3200;
 
 /**
- * A depth value is packed into 32 bit RGBA.
+ * The depth value is packed into 32 bit RGBA.
  *
  * @type {number}
  * @constant
@@ -1239,7 +1239,7 @@ export const BasicDepthPacking = 3200;
 export const RGBADepthPacking = 3201;
 
 /**
- * A depth value is packed into 24 bit RGB.
+ * The depth value is packed into 24 bit RGB.
  *
  * @type {number}
  * @constant
@@ -1247,13 +1247,21 @@ export const RGBADepthPacking = 3201;
 export const RGBDepthPacking = 3202;
 
 /**
- * A depth value is packed into 16 bit RG.
+ * The depth value is packed into 16 bit RG.
  *
  * @type {number}
  * @constant
  */
 export const RGDepthPacking = 3203;
 
+/**
+ * The depth value is not packed.
+ *
+ * @type {number}
+ * @constant
+ */
+export const IdentityDepthPacking = 3204;
+
 /**
  * Normal information is relative to the underlying surface.
  *

+ 0 - 3
src/lights/LightShadow.js

@@ -69,9 +69,6 @@ class LightShadow {
 		 * map size will allow for a higher value to be used here before these effects
 		 * become visible.
 		 *
-		 * The property has no effect when the shadow map type is `PCFSoftShadowMap` and
-		 * and it is recommended to increase softness by decreasing the shadow map size instead.
-		 *
 		 * The property has no effect when the shadow map type is `BasicShadowMap`.
 		 *
 		 * @type {number}

+ 8 - 42
src/lights/PointLightShadow.js

@@ -1,9 +1,7 @@
 import { LightShadow } from './LightShadow.js';
 import { PerspectiveCamera } from '../cameras/PerspectiveCamera.js';
 import { Matrix4 } from '../math/Matrix4.js';
-import { Vector2 } from '../math/Vector2.js';
 import { Vector3 } from '../math/Vector3.js';
-import { Vector4 } from '../math/Vector4.js';
 
 const _projScreenMatrix = /*@__PURE__*/ new Matrix4();
 const _lightPositionWorld = /*@__PURE__*/ new Vector3();
@@ -32,46 +30,14 @@ class PointLightShadow extends LightShadow {
 		 */
 		this.isPointLightShadow = true;
 
-		this._frameExtents = new Vector2( 4, 2 );
-
-		this._viewportCount = 6;
-
-		this._viewports = [
-			// These viewports map a cube-map onto a 2D texture with the
-			// following orientation:
-			//
-			//  xzXZ
-			//   y Y
-			//
-			// X - Positive x direction
-			// x - Negative x direction
-			// Y - Positive y direction
-			// y - Negative y direction
-			// Z - Positive z direction
-			// z - Negative z direction
-
-			// positive X
-			new Vector4( 2, 1, 1, 1 ),
-			// negative X
-			new Vector4( 0, 1, 1, 1 ),
-			// positive Z
-			new Vector4( 3, 1, 1, 1 ),
-			// negative Z
-			new Vector4( 1, 1, 1, 1 ),
-			// positive Y
-			new Vector4( 3, 0, 1, 1 ),
-			// negative Y
-			new Vector4( 1, 0, 1, 1 )
-		];
-
 		this._cubeDirections = [
-			new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
-			new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
+			new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 1, 0 ),
+			new Vector3( 0, - 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 )
 		];
 
 		this._cubeUps = [
-			new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
-			new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),	new Vector3( 0, 0, - 1 )
+			new Vector3( 0, - 1, 0 ), new Vector3( 0, - 1, 0 ), new Vector3( 0, 0, 1 ),
+			new Vector3( 0, 0, - 1 ), new Vector3( 0, - 1, 0 ), new Vector3( 0, - 1, 0 )
 		];
 
 	}
@@ -80,9 +46,9 @@ class PointLightShadow extends LightShadow {
 	 * Update the matrices for the camera and shadow, used internally by the renderer.
 	 *
 	 * @param {Light} light - The light for which the shadow is being rendered.
-	 * @param {number} [viewportIndex=0] - The viewport index.
+	 * @param {number} [faceIndex=0] - The cube face index (0-5).
 	 */
-	updateMatrices( light, viewportIndex = 0 ) {
+	updateMatrices( light, faceIndex = 0 ) {
 
 		const camera = this.camera;
 		const shadowMatrix = this.matrix;
@@ -100,8 +66,8 @@ class PointLightShadow extends LightShadow {
 		camera.position.copy( _lightPositionWorld );
 
 		_lookTarget.copy( camera.position );
-		_lookTarget.add( this._cubeDirections[ viewportIndex ] );
-		camera.up.copy( this._cubeUps[ viewportIndex ] );
+		_lookTarget.add( this._cubeDirections[ faceIndex ] );
+		camera.up.copy( this._cubeUps[ faceIndex ] );
 		camera.lookAt( _lookTarget );
 		camera.updateMatrixWorld();
 

+ 25 - 1
src/renderers/WebGLRenderer.js

@@ -2444,6 +2444,30 @@ class WebGLRenderer {
 
 			}
 
+			// Pre-allocate texture units for shadow samplers before setting data textures
+			if ( materialProperties.needsLights ) {
+
+				// Set shadow map uniforms first to ensure they get the first texture units
+				if ( lights.state.directionalShadowMap.length > 0 ) {
+
+					p_uniforms.setValue( _gl, 'directionalShadowMap', lights.state.directionalShadowMap, textures );
+
+				}
+
+				if ( lights.state.spotShadowMap.length > 0 ) {
+
+					p_uniforms.setValue( _gl, 'spotShadowMap', lights.state.spotShadowMap, textures );
+
+				}
+
+				if ( lights.state.pointShadowMap.length > 0 ) {
+
+					p_uniforms.setValue( _gl, 'pointShadowMap', lights.state.pointShadowMap, textures );
+
+				}
+
+			}
+
 			// skinning and morph target uniforms must be set even if material didn't change
 			// auto-setting of texture unit for bone and morph texture must go before other textures
 			// otherwise textures used for skinning and morphing can take over texture units reserved for other material textures
@@ -3508,7 +3532,7 @@ class WebGLRenderer {
  * If you do not require dynamic lighting / shadows, you may set this to `false`.
  * @property {boolean} [needsUpdate=false] - When set to `true`, shadow maps in the scene
  * will be updated in the next `render` call.
- * @property {(BasicShadowMap|PCFShadowMap|PCFSoftShadowMap|VSMShadowMap)} [type=PCFShadowMap] - Defines the shadow map type.
+ * @property {(BasicShadowMap|PCFShadowMap|VSMShadowMap)} [type=PCFShadowMap] - Defines the shadow map type.
  **/
 
 export { WebGLRenderer };

+ 1 - 0
src/renderers/shaders/DFGLUTData.js

@@ -50,6 +50,7 @@ export function getDFGLUT() {
 	if ( lut === null ) {
 
 		lut = new DataTexture( DATA, 32, 32, RGFormat, HalfFloatType );
+		lut.name = 'DFG_LUT';
 		lut.minFilter = LinearFilter;
 		lut.magFilter = LinearFilter;
 		lut.wrapS = ClampToEdgeWrapping;

+ 3 - 3
src/renderers/shaders/ShaderChunk.js

@@ -110,7 +110,7 @@ import * as background from './ShaderLib/background.glsl.js';
 import * as backgroundCube from './ShaderLib/backgroundCube.glsl.js';
 import * as cube from './ShaderLib/cube.glsl.js';
 import * as depth from './ShaderLib/depth.glsl.js';
-import * as distanceRGBA from './ShaderLib/distanceRGBA.glsl.js';
+import * as distance from './ShaderLib/distance.glsl.js';
 import * as equirect from './ShaderLib/equirect.glsl.js';
 import * as linedashed from './ShaderLib/linedashed.glsl.js';
 import * as meshbasic from './ShaderLib/meshbasic.glsl.js';
@@ -241,8 +241,8 @@ export const ShaderChunk = {
 	cube_frag: cube.fragment,
 	depth_vert: depth.vertex,
 	depth_frag: depth.fragment,
-	distanceRGBA_vert: distanceRGBA.vertex,
-	distanceRGBA_frag: distanceRGBA.fragment,
+	distance_vert: distance.vertex,
+	distance_frag: distance.fragment,
 	equirect_vert: equirect.vertex,
 	equirect_frag: equirect.fragment,
 	linedashed_vert: linedashed.vertex,

+ 1 - 1
src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js

@@ -69,7 +69,7 @@ IncidentLight directLight;
 
 		getPointLightInfo( pointLight, geometryPosition, directLight );
 
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
+		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) )
 		pointLightShadow = pointLightShadows[ i ];
 		directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;
 		#endif

+ 191 - 190
src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js

@@ -15,7 +15,16 @@ export default /* glsl */`
 
 	#if NUM_DIR_LIGHT_SHADOWS > 0
 
-		uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
+		#if defined( SHADOWMAP_TYPE_PCF )
+
+			uniform sampler2DShadow directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
+
+		#else
+
+			uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
+
+		#endif
+
 		varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
 
 		struct DirectionalLightShadow {
@@ -32,7 +41,15 @@ export default /* glsl */`
 
 	#if NUM_SPOT_LIGHT_SHADOWS > 0
 
-		uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
+		#if defined( SHADOWMAP_TYPE_PCF )
+
+			uniform sampler2DShadow spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
+
+		#else
+
+			uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
+
+		#endif
 
 		struct SpotLightShadow {
 			float shadowIntensity;
@@ -48,7 +65,16 @@ export default /* glsl */`
 
 	#if NUM_POINT_LIGHT_SHADOWS > 0
 
-		uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
+		#if defined( SHADOWMAP_TYPE_PCF )
+
+			uniform samplerCubeShadow pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
+
+		#elif defined( SHADOWMAP_TYPE_BASIC )
+
+			uniform samplerCube pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
+
+		#endif
+
 		varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];
 
 		struct PointLightShadow {
@@ -65,272 +91,243 @@ export default /* glsl */`
 
 	#endif
 
-	float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {
+	#if defined( SHADOWMAP_TYPE_PCF )
 
-		float depth = unpackRGBAToDepth( texture2D( depths, uv ) );
+		// Interleaved Gradient Noise for randomizing sampling patterns
+		float interleavedGradientNoise( vec2 position ) {
 
-		#ifdef USE_REVERSED_DEPTH_BUFFER
+			return fract( 52.9829189 * fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) );
 
-			return step( depth, compare );
+		}
 
-		#else
+		// Vogel disk sampling for uniform circular distribution
+		vec2 vogelDiskSample( int sampleIndex, int samplesCount, float phi ) {
 
-			return step( compare, depth );
+			const float goldenAngle = 2.399963229728653;
+			float r = sqrt( float( sampleIndex ) + 0.5 ) / sqrt( float( samplesCount ) );
+			float theta = float( sampleIndex ) * goldenAngle + phi;
+			return vec2( cos( theta ), sin( theta ) ) * r;
 
-		#endif
+		}
 
-	}
+	#endif
 
-	vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {
+	#if defined( SHADOWMAP_TYPE_PCF )
 
-		return unpackRGBATo2Half( texture2D( shadow, uv ) );
+		float getShadow( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
 
-	}
+			float shadow = 1.0;
 
-	float VSMShadow( sampler2D shadow, vec2 uv, float compare ) {
+			shadowCoord.xyz /= shadowCoord.w;
+			shadowCoord.z += shadowBias;
 
-		vec2 distribution = texture2DDistribution( shadow, uv );
+			bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
+			bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
 
-		float mean = distribution.x;
-		float variance = distribution.y * distribution.y;
+			if ( frustumTest ) {
 
-		#ifdef USE_REVERSED_DEPTH_BUFFER
+				// Hardware PCF with LinearFilter gives us 4-tap filtering per sample
+				// 5 samples using Vogel disk + IGN = effectively 20 filtered taps with better distribution
+				vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
+				float radius = shadowRadius * texelSize.x;
 
-			float hard_shadow = step( mean, compare );
+				// Use IGN to rotate sampling pattern per pixel
+				float phi = interleavedGradientNoise( gl_FragCoord.xy ) * 6.28318530718; // 2*PI
 
-		#else
+				shadow = (
+					texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 0, 5, phi ) * radius, shadowCoord.z ) ) +
+					texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 1, 5, phi ) * radius, shadowCoord.z ) ) +
+					texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 2, 5, phi ) * radius, shadowCoord.z ) ) +
+					texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 3, 5, phi ) * radius, shadowCoord.z ) ) +
+					texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 4, 5, phi ) * radius, shadowCoord.z ) )
+				) * 0.2;
 
-			float hard_shadow = step( compare, mean );
+			}
 
-		#endif
+			return mix( 1.0, shadow, shadowIntensity );
 
-		// Early return if fully lit
-		if ( hard_shadow == 1.0 ) return 1.0;
+		}
 
-		// Variance must be non-zero to avoid division by zero
-		variance = max( variance, 0.0000001 );
+	#elif defined( SHADOWMAP_TYPE_VSM )
 
-		// Distance from mean
-		float d = compare - mean;
+		float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
 
-		// Chebyshev's inequality for upper bound on probability
-		float p_max = variance / ( variance + d * d );
+			float shadow = 1.0;
 
-		// Reduce light bleeding by remapping [amount, 1] to [0, 1]
-		p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );
+			shadowCoord.xyz /= shadowCoord.w;
+			shadowCoord.z += shadowBias;
 
-		return max( hard_shadow, p_max );
+			bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
+			bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
 
-	}
+			if ( frustumTest ) {
 
-	float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
+				vec2 distribution = texture2D( shadowMap, shadowCoord.xy ).rg;
 
-		float shadow = 1.0;
+				float mean = distribution.x;
+				float variance = distribution.y * distribution.y;
 
-		shadowCoord.xyz /= shadowCoord.w;
-		shadowCoord.z += shadowBias;
+				#ifdef USE_REVERSED_DEPTH_BUFFER
 
-		bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
-		bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
+					float hard_shadow = step( mean, shadowCoord.z );
 
-		if ( frustumTest ) {
+				#else
 
-		#if defined( SHADOWMAP_TYPE_PCF )
+					float hard_shadow = step( shadowCoord.z, mean );
 
-			vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
+				#endif
 
-			float dx0 = - texelSize.x * shadowRadius;
-			float dy0 = - texelSize.y * shadowRadius;
-			float dx1 = + texelSize.x * shadowRadius;
-			float dy1 = + texelSize.y * shadowRadius;
-			float dx2 = dx0 / 2.0;
-			float dy2 = dy0 / 2.0;
-			float dx3 = dx1 / 2.0;
-			float dy3 = dy1 / 2.0;
+				// Early return if fully lit
+				if ( hard_shadow == 1.0 ) {
 
-			shadow = (
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )
-			) * ( 1.0 / 17.0 );
-
-		#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
-
-			vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
-			float dx = texelSize.x;
-			float dy = texelSize.y;
-
-			vec2 uv = shadowCoord.xy;
-			vec2 f = fract( uv * shadowMapSize + 0.5 );
-			uv -= f * texelSize;
+					shadow = 1.0;
 
-			shadow = (
-				texture2DCompare( shadowMap, uv, shadowCoord.z ) +
-				texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +
-				texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +
-				mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),
-					 texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),
-					 f.x ) +
-				mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),
-					 texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),
-					 f.x ) +
-				mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),
-					 texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),
-					 f.y ) +
-				mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),
-					 texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),
-					 f.y ) +
-				mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),
-						  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),
-						  f.x ),
-					 mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),
-						  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),
-						  f.x ),
-					 f.y )
-			) * ( 1.0 / 9.0 );
-
-		#elif defined( SHADOWMAP_TYPE_VSM )
-
-			shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );
-
-		#else // no percentage-closer filtering:
-
-			shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );
+				} else {
 
-		#endif
+					// Variance must be non-zero to avoid division by zero
+					variance = max( variance, 0.0000001 );
 
-		}
+					// Distance from mean
+					float d = shadowCoord.z - mean;
 
-		return mix( 1.0, shadow, shadowIntensity );
+					// Chebyshev's inequality for upper bound on probability
+					float p_max = variance / ( variance + d * d );
 
-	}
+					// Reduce light bleeding by remapping [amount, 1] to [0, 1]
+					p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );
+
+					shadow = max( hard_shadow, p_max );
 
-	// cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D
-	// vector suitable for 2D texture mapping. This code uses the following layout for the
-	// 2D texture:
-	//
-	// xzXZ
-	//  y Y
-	//
-	// Y - Positive y direction
-	// y - Negative y direction
-	// X - Positive x direction
-	// x - Negative x direction
-	// Z - Positive z direction
-	// z - Negative z direction
-	//
-	// Source and test bed:
-	// https://gist.github.com/tschw/da10c43c467ce8afd0c4
+				}
 
-	vec2 cubeToUV( vec3 v, float texelSizeY ) {
+			}
 
-		// Number of texels to avoid at the edge of each square
+			return mix( 1.0, shadow, shadowIntensity );
 
-		vec3 absV = abs( v );
+		}
 
-		// Intersect unit cube
+	#else // SHADOWMAP_TYPE_BASIC
 
-		float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );
-		absV *= scaleToCube;
+		float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
 
-		// Apply scale to avoid seams
+			float shadow = 1.0;
 
-		// two texels less per square (one texel will do for NEAREST)
-		v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );
+			shadowCoord.xyz /= shadowCoord.w;
+			shadowCoord.z += shadowBias;
 
-		// Unwrap
+			bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
+			bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
 
-		// space: -1 ... 1 range for each square
-		//
-		// #X##		dim    := ( 4 , 2 )
-		//  # #		center := ( 1 , 1 )
+			if ( frustumTest ) {
 
-		vec2 planar = v.xy;
+				float depth = texture2D( shadowMap, shadowCoord.xy ).r;
 
-		float almostATexel = 1.5 * texelSizeY;
-		float almostOne = 1.0 - almostATexel;
+				#ifdef USE_REVERSED_DEPTH_BUFFER
 
-		if ( absV.z >= almostOne ) {
+					shadow = step( depth, shadowCoord.z );
 
-			if ( v.z > 0.0 )
-				planar.x = 4.0 - v.x;
+				#else
 
-		} else if ( absV.x >= almostOne ) {
+					shadow = step( shadowCoord.z, depth );
 
-			float signX = sign( v.x );
-			planar.x = v.z * signX + 2.0 * signX;
+				#endif
 
-		} else if ( absV.y >= almostOne ) {
+			}
 
-			float signY = sign( v.y );
-			planar.x = v.x + 2.0 * signY + 2.0;
-			planar.y = v.z * signY - 2.0;
+			return mix( 1.0, shadow, shadowIntensity );
 
 		}
 
-		// Transform to UV space
+	#endif
 
-		// scale := 0.5 / dim
-		// translate := ( center + 0.5 ) / dim
-		return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );
+	#if NUM_POINT_LIGHT_SHADOWS > 0
 
-	}
+	#if defined( SHADOWMAP_TYPE_PCF )
 
-	float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
+	float getPointShadow( samplerCubeShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
 
 		float shadow = 1.0;
 
 		// for point lights, the uniform @vShadowCoord is re-purposed to hold
 		// the vector from the light to the world-space position of the fragment.
 		vec3 lightToPosition = shadowCoord.xyz;
-		
-		float lightToPositionLength = length( lightToPosition );
 
-		if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) {
+		// Direction from light to fragment
+		vec3 bd3D = normalize( lightToPosition );
+
+		// For cube shadow maps, depth is stored as distance along each face's view axis, not radial distance
+		// The view-space depth is the maximum component of the direction vector (which face is sampled)
+		vec3 absVec = abs( lightToPosition );
+		float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );
 
-			// dp = normalized distance from light to fragment position
-			float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); // need to clamp?
+		if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {
+
+			// Calculate perspective depth for cube shadow map
+			// Standard perspective depth formula: depth = (far * (z - near)) / (z * (far - near))
+			float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );
 			dp += shadowBias;
 
-			// bd3D = base direction 3D
-			vec3 bd3D = normalize( lightToPosition );
+			// Hardware PCF with LinearFilter gives us 4-tap filtering per sample
+			// Use Vogel disk + IGN sampling for better quality
+			float texelSize = shadowRadius / shadowMapSize.x;
 
-			vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );
+			// Build a tangent-space coordinate system for applying offsets
+			vec3 absDir = abs( bd3D );
+			vec3 tangent = absDir.x > absDir.z ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 );
+			tangent = normalize( cross( bd3D, tangent ) );
+			vec3 bitangent = cross( bd3D, tangent );
 
-			#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )
+			// Use IGN to rotate sampling pattern per pixel
+			float phi = interleavedGradientNoise( gl_FragCoord.xy ) * 6.28318530718;
 
-				vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;
+			shadow = (
+				texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 0, 5, phi ).x + bitangent * vogelDiskSample( 0, 5, phi ).y ) * texelSize, dp ) ) +
+				texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 1, 5, phi ).x + bitangent * vogelDiskSample( 1, 5, phi ).y ) * texelSize, dp ) ) +
+				texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 2, 5, phi ).x + bitangent * vogelDiskSample( 2, 5, phi ).y ) * texelSize, dp ) ) +
+				texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 3, 5, phi ).x + bitangent * vogelDiskSample( 3, 5, phi ).y ) * texelSize, dp ) ) +
+				texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 4, 5, phi ).x + bitangent * vogelDiskSample( 4, 5, phi ).y ) * texelSize, dp ) )
+			) * 0.2;
 
-				shadow = (
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +
-					texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )
-				) * ( 1.0 / 9.0 );
+		}
+
+		return mix( 1.0, shadow, shadowIntensity );
+
+	}
+
+	#elif defined( SHADOWMAP_TYPE_BASIC )
 
-			#else // no percentage-closer filtering
+	float getPointShadow( samplerCube shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
 
-				shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );
+		float shadow = 1.0;
+
+		// for point lights, the uniform @vShadowCoord is re-purposed to hold
+		// the vector from the light to the world-space position of the fragment.
+		vec3 lightToPosition = shadowCoord.xyz;
+
+		// Direction from light to fragment
+		vec3 bd3D = normalize( lightToPosition );
+
+		// For cube shadow maps, depth is stored as distance along each face's view axis, not radial distance
+		// The view-space depth is the maximum component of the direction vector (which face is sampled)
+		vec3 absVec = abs( lightToPosition );
+		float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );
+
+		if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {
+
+			// Calculate perspective depth for cube shadow map
+			// Standard perspective depth formula: depth = (far * (z - near)) / (z * (far - near))
+			float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );
+			dp += shadowBias;
+
+			float depth = textureCube( shadowMap, bd3D ).r;
+
+			#ifdef USE_REVERSED_DEPTH_BUFFER
+
+				shadow = step( depth, dp );
+
+			#else
+
+				shadow = step( dp, depth );
 
 			#endif
 
@@ -340,5 +337,9 @@ export default /* glsl */`
 
 	}
 
+	#endif
+
+	#endif
+
 #endif
 `;

+ 1 - 1
src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js

@@ -35,7 +35,7 @@ float getShadowMask() {
 
 	#endif
 
-	#if NUM_POINT_LIGHT_SHADOWS > 0
+	#if NUM_POINT_LIGHT_SHADOWS > 0 && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) )
 
 	PointLightShadow pointLight;
 

+ 3 - 3
src/renderers/shaders/ShaderLib.js

@@ -265,7 +265,7 @@ const ShaderLib = {
 
 	},
 
-	distanceRGBA: {
+	distance: {
 
 		uniforms: /*@__PURE__*/ mergeUniforms( [
 			UniformsLib.common,
@@ -277,8 +277,8 @@ const ShaderLib = {
 			}
 		] ),
 
-		vertexShader: ShaderChunk.distanceRGBA_vert,
-		fragmentShader: ShaderChunk.distanceRGBA_frag
+		vertexShader: ShaderChunk.distance_vert,
+		fragmentShader: ShaderChunk.distance_frag
 
 	},
 

+ 7 - 0
src/renderers/shaders/ShaderLib/depth.glsl.js

@@ -98,16 +98,23 @@ void main() {
 
 	#elif DEPTH_PACKING == 3201
 
+		// TODO Deprecate
 		gl_FragColor = packDepthToRGBA( fragCoordZ );
 
 	#elif DEPTH_PACKING == 3202
 
+		// TODO Deprecate
 		gl_FragColor = vec4( packDepthToRGB( fragCoordZ ), 1.0 );
 
 	#elif DEPTH_PACKING == 3203
 
+		// TODO Deprecate
 		gl_FragColor = vec4( packDepthToRG( fragCoordZ ), 0.0, 1.0 );
 
+	#elif DEPTH_PACKING == 3204
+
+		gl_FragColor = vec4( vec3( fragCoordZ ), 1.0 );
+
 	#endif
 
 }

+ 1 - 2
src/renderers/shaders/ShaderLib/distanceRGBA.glsl.js → src/renderers/shaders/ShaderLib/distance.glsl.js

@@ -50,7 +50,6 @@ uniform float farDistance;
 varying vec3 vWorldPosition;
 
 #include <common>
-#include <packing>
 #include <uv_pars_fragment>
 #include <map_pars_fragment>
 #include <alphamap_pars_fragment>
@@ -72,7 +71,7 @@ void main () {
 	dist = ( dist - nearDistance ) / ( farDistance - nearDistance );
 	dist = saturate( dist ); // clamp to [ 0, 1 ]
 
-	gl_FragColor = packDepthToRGBA( dist );
+	gl_FragColor = vec4( dist, 0.0, 0.0, 1.0 );
 
 }
 `;

+ 0 - 1
src/renderers/shaders/ShaderLib/meshlambert.glsl.js

@@ -58,7 +58,6 @@ uniform vec3 emissive;
 uniform float opacity;
 
 #include <common>
-#include <packing>
 #include <dithering_pars_fragment>
 #include <color_pars_fragment>
 #include <uv_pars_fragment>

+ 1 - 2
src/renderers/shaders/ShaderLib/meshnormal.glsl.js

@@ -58,7 +58,6 @@ uniform float opacity;
 
 #endif
 
-#include <packing>
 #include <uv_pars_fragment>
 #include <normal_pars_fragment>
 #include <bumpmap_pars_fragment>
@@ -75,7 +74,7 @@ void main() {
 	#include <normal_fragment_begin>
 	#include <normal_fragment_maps>
 
-	gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a );
+	gl_FragColor = vec4( normalize( normal ) * 0.5 + 0.5, diffuseColor.a );
 
 	#ifdef OPAQUE
 

+ 0 - 1
src/renderers/shaders/ShaderLib/meshphong.glsl.js

@@ -60,7 +60,6 @@ uniform float shininess;
 uniform float opacity;
 
 #include <common>
-#include <packing>
 #include <dithering_pars_fragment>
 #include <color_pars_fragment>
 #include <uv_pars_fragment>

+ 0 - 1
src/renderers/shaders/ShaderLib/meshphysical.glsl.js

@@ -130,7 +130,6 @@ uniform float opacity;
 varying vec3 vViewPosition;
 
 #include <common>
-#include <packing>
 #include <dithering_pars_fragment>
 #include <color_pars_fragment>
 #include <uv_pars_fragment>

+ 0 - 1
src/renderers/shaders/ShaderLib/meshtoon.glsl.js

@@ -56,7 +56,6 @@ uniform vec3 emissive;
 uniform float opacity;
 
 #include <common>
-#include <packing>
 #include <dithering_pars_fragment>
 #include <color_pars_fragment>
 #include <uv_pars_fragment>

+ 0 - 1
src/renderers/shaders/ShaderLib/shadow.glsl.js

@@ -36,7 +36,6 @@ uniform vec3 color;
 uniform float opacity;
 
 #include <common>
-#include <packing>
 #include <fog_pars_fragment>
 #include <bsdfs>
 #include <lights_pars_begin>

+ 3 - 5
src/renderers/shaders/ShaderLib/vsm.glsl.js

@@ -11,8 +11,6 @@ uniform sampler2D shadow_pass;
 uniform vec2 resolution;
 uniform float radius;
 
-#include <packing>
-
 void main() {
 
 	const float samples = float( VSM_SAMPLES );
@@ -28,13 +26,13 @@ void main() {
 
 		#ifdef HORIZONTAL_PASS
 
-			vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );
+			vec2 distribution = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ).rg;
 			mean += distribution.x;
 			squared_mean += distribution.y * distribution.y + distribution.x * distribution.x;
 
 		#else
 
-			float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );
+			float depth = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ).r;
 			mean += depth;
 			squared_mean += depth * depth;
 
@@ -47,7 +45,7 @@ void main() {
 
 	float std_dev = sqrt( squared_mean - mean * mean );
 
-	gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );
+	gl_FragColor = vec4( mean, std_dev, 0.0, 1.0 );
 
 }
 `;

+ 1 - 1
src/renderers/webgl/WebGLLights.js

@@ -239,7 +239,7 @@ function WebGLLights( extensions ) {
 			const intensity = light.intensity;
 			const distance = light.distance;
 
-			const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;
+			const shadowMap = ( light.shadow && light.shadow.map ) ? ( light.shadow.map.depthTexture || light.shadow.map.texture ) : null;
 
 			if ( light.isAmbientLight ) {
 

+ 1 - 5
src/renderers/webgl/WebGLProgram.js

@@ -1,7 +1,7 @@
 import { WebGLUniforms } from './WebGLUniforms.js';
 import { WebGLShader } from './WebGLShader.js';
 import { ShaderChunk } from '../shaders/ShaderChunk.js';
-import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, NeutralToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearTransfer, SRGBTransfer } from '../../constants.js';
+import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, NeutralToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearTransfer, SRGBTransfer } from '../../constants.js';
 import { ColorManagement } from '../../math/ColorManagement.js';
 import { Vector3 } from '../../math/Vector3.js';
 import { Matrix3 } from '../../math/Matrix3.js';
@@ -369,10 +369,6 @@ function generateShadowMapTypeDefine( parameters ) {
 
 		shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
 
-	} else if ( parameters.shadowMapType === PCFSoftShadowMap ) {
-
-		shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
-
 	} else if ( parameters.shadowMapType === VSMShadowMap ) {
 
 		shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM';

+ 1 - 1
src/renderers/webgl/WebGLPrograms.js

@@ -21,7 +21,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 
 	const shaderIDs = {
 		MeshDepthMaterial: 'depth',
-		MeshDistanceMaterial: 'distanceRGBA',
+		MeshDistanceMaterial: 'distance',
 		MeshNormalMaterial: 'normal',
 		MeshBasicMaterial: 'basic',
 		MeshLambertMaterial: 'lambert',

+ 140 - 24
src/renderers/webgl/WebGLShadowMap.js

@@ -1,5 +1,6 @@
-import { FrontSide, BackSide, DoubleSide, NearestFilter, PCFShadowMap, VSMShadowMap, RGBADepthPacking, NoBlending } from '../../constants.js';
+import { FrontSide, BackSide, DoubleSide, NearestFilter, LinearFilter, PCFShadowMap, VSMShadowMap, NoBlending, LessEqualCompare, GreaterEqualCompare, DepthFormat, UnsignedIntType, RGFormat, HalfFloatType, PCFSoftShadowMap, IdentityDepthPacking } from '../../constants.js';
 import { WebGLRenderTarget } from '../WebGLRenderTarget.js';
+import { WebGLCubeRenderTarget } from '../WebGLCubeRenderTarget.js';
 import { MeshDepthMaterial } from '../../materials/MeshDepthMaterial.js';
 import { MeshDistanceMaterial } from '../../materials/MeshDistanceMaterial.js';
 import { ShaderMaterial } from '../../materials/ShaderMaterial.js';
@@ -9,6 +10,8 @@ import { Mesh } from '../../objects/Mesh.js';
 import { Vector4 } from '../../math/Vector4.js';
 import { Vector2 } from '../../math/Vector2.js';
 import { Frustum } from '../../math/Frustum.js';
+import { DepthTexture } from '../../textures/DepthTexture.js';
+import { CubeDepthTexture } from '../../textures/CubeDepthTexture.js';
 
 import * as vsm from '../shaders/ShaderLib/vsm.glsl.js';
 import { warn } from '../../utils.js';
@@ -22,7 +25,8 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 		_viewport = new Vector4(),
 
-		_depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ),
+		_depthMaterial = new MeshDepthMaterial(),
+		_depthMaterialVSM = new MeshDepthMaterial( { depthPacking: IdentityDepthPacking } ),
 		_distanceMaterial = new MeshDistanceMaterial(),
 
 		_materialCache = {},
@@ -77,6 +81,13 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 		if ( lights.length === 0 ) return;
 
+		if ( lights.type === PCFSoftShadowMap ) {
+
+			warn( 'WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead.' );
+			lights.type = PCFShadowMap;
+
+		}
+
 		const currentRenderTarget = renderer.getRenderTarget();
 		const activeCubeFace = renderer.getActiveCubeFace();
 		const activeMipmapLevel = renderer.getActiveMipmapLevel();
@@ -101,8 +112,31 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 		// check for shadow map type changes
 
-		const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap );
-		const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap );
+		const typeChanged = _previousType !== this.type;
+
+		// When shadow map type changes, materials need recompilation because sampler types change
+		// (sampler2DShadow for PCF vs sampler2D for Basic)
+		if ( typeChanged ) {
+
+			scene.traverse( function ( object ) {
+
+				if ( object.material ) {
+
+					if ( Array.isArray( object.material ) ) {
+
+						object.material.forEach( mat => mat.needsUpdate = true );
+
+					} else {
+
+						object.material.needsUpdate = true;
+
+					}
+
+				}
+
+			} );
+
+		}
 
 		// render depth map
 
@@ -148,42 +182,113 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 			}
 
-			if ( shadow.map === null || toVSM === true || fromVSM === true ) {
-
-				const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {};
+			if ( shadow.map === null || typeChanged === true ) {
 
 				if ( shadow.map !== null ) {
 
+					if ( shadow.map.depthTexture !== null ) {
+
+						shadow.map.depthTexture.dispose();
+						shadow.map.depthTexture = null;
+
+					}
+
 					shadow.map.dispose();
 
 				}
 
-				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + '.shadowMap';
+				if ( this.type === VSMShadowMap ) {
+
+					if ( light.isPointLight ) {
+
+						warn( 'WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.' );
+						continue;
+
+					}
+
+					shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, {
+						format: RGFormat,
+						type: HalfFloatType,
+						minFilter: LinearFilter,
+						magFilter: LinearFilter,
+						generateMipmaps: false
+					} );
+					shadow.map.texture.name = light.name + '.shadowMap';
+
+				} else {
+
+					if ( light.isPointLight ) {
+
+						shadow.map = new WebGLCubeRenderTarget( _shadowMapSize.x );
+						shadow.map.depthTexture = new CubeDepthTexture( _shadowMapSize.x, UnsignedIntType );
+
+					} else {
+
+						shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y );
+						shadow.map.depthTexture = new DepthTexture( _shadowMapSize.x, _shadowMapSize.y, UnsignedIntType );
+
+					}
+
+					shadow.map.depthTexture.name = light.name + '.shadowMap';
+					shadow.map.depthTexture.format = DepthFormat;
+
+					const reversedDepthBuffer = renderer.state.buffers.depth.getReversed();
+
+					if ( this.type === PCFShadowMap ) {
+
+						shadow.map.depthTexture.compareFunction = reversedDepthBuffer ? GreaterEqualCompare : LessEqualCompare;
+						shadow.map.depthTexture.minFilter = LinearFilter;
+						shadow.map.depthTexture.magFilter = LinearFilter;
+
+					} else {
+
+						shadow.map.depthTexture.compareFunction = null;
+						shadow.map.depthTexture.minFilter = NearestFilter;
+						shadow.map.depthTexture.magFilter = NearestFilter;
+
+					}
+
+				}
 
 				shadow.camera.updateProjectionMatrix();
 
 			}
 
-			renderer.setRenderTarget( shadow.map );
-			renderer.clear();
+			// For cube render targets (PointLights), render all 6 faces. Otherwise, render once.
+			const faceCount = shadow.map.isWebGLCubeRenderTarget ? 6 : 1;
+
+			for ( let face = 0; face < faceCount; face ++ ) {
+
+				// For cube render targets, render to each face separately
+				if ( shadow.map.isWebGLCubeRenderTarget ) {
+
+					renderer.setRenderTarget( shadow.map, face );
+					renderer.clear();
+
+				} else {
+
+					// For 2D render targets, use viewports
+					if ( face === 0 ) {
+
+						renderer.setRenderTarget( shadow.map );
+						renderer.clear();
 
-			const viewportCount = shadow.getViewportCount();
+					}
 
-			for ( let vp = 0; vp < viewportCount; vp ++ ) {
+					const viewport = shadow.getViewport( face );
 
-				const viewport = shadow.getViewport( vp );
+					_viewport.set(
+						_viewportSize.x * viewport.x,
+						_viewportSize.y * viewport.y,
+						_viewportSize.x * viewport.z,
+						_viewportSize.y * viewport.w
+					);
 
-				_viewport.set(
-					_viewportSize.x * viewport.x,
-					_viewportSize.y * viewport.y,
-					_viewportSize.x * viewport.z,
-					_viewportSize.y * viewport.w
-				);
+					_state.viewport( _viewport );
 
-				_state.viewport( _viewport );
+				}
 
-				shadow.updateMatrices( light, vp );
+				shadow.updateMatrices( light, face );
 
 				_frustum = shadow.getFrustum();
 
@@ -227,7 +332,10 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 		if ( shadow.mapPass === null ) {
 
-			shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y );
+			shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, {
+				format: RGFormat,
+				type: HalfFloatType
+			} );
 
 		}
 
@@ -263,7 +371,15 @@ function WebGLShadowMap( renderer, objects, capabilities ) {
 
 		} else {
 
-			result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial;
+			if ( type === VSMShadowMap ) {
+
+				result = _depthMaterialVSM;
+
+			} else {
+
+				result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial;
+
+			}
 
 			if ( ( renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) ||
 				( material.displacementMap && material.displacementScale !== 0 ) ||

+ 79 - 14
src/renderers/webgl/WebGLTextures.js

@@ -583,7 +583,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 		const textureProperties = properties.get( texture );
 
-		if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+		if ( texture.isCubeDepthTexture !== true && texture.version > 0 && textureProperties.__version !== texture.version ) {
 
 			uploadCubeTexture( textureProperties, texture, slot );
 			return;
@@ -1642,10 +1642,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 	}
 
 	// Setup resources for a Depth Texture for a FBO (needs an extension)
-	function setupDepthTexture( framebuffer, renderTarget ) {
+	function setupDepthTexture( framebuffer, renderTarget, cubeFace ) {
 
-		const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget );
-		if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );
+		const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
 
 		state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
 
@@ -1669,20 +1668,69 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 		}
 
-		setTexture2D( renderTarget.depthTexture, 0 );
+		if ( isCube ) {
+
+			// For cube depth textures, initialize and bind without uploading image data
+			if ( textureProperties.__webglInit === undefined ) {
+
+				textureProperties.__webglInit = true;
+				renderTarget.depthTexture.addEventListener( 'dispose', onTextureDispose );
+
+			}
+
+			// Only create and allocate storage once
+			if ( textureProperties.__webglTexture === undefined ) {
+
+				textureProperties.__webglTexture = _gl.createTexture();
+
+				state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
+				setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.depthTexture );
+
+				// Allocate storage for all 6 faces with correct depth texture format
+				const glFormat = utils.convert( renderTarget.depthTexture.format );
+				const glType = utils.convert( renderTarget.depthTexture.type );
+
+				// Use proper internal format for depth textures
+				let glInternalFormat;
+				if ( renderTarget.depthTexture.format === DepthFormat ) {
+
+					glInternalFormat = _gl.DEPTH_COMPONENT24;
+
+				} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {
+
+					glInternalFormat = _gl.DEPTH24_STENCIL8;
+
+				}
+
+				for ( let i = 0; i < 6; i ++ ) {
+
+					_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+				}
+
+			}
+
+		} else {
+
+			setTexture2D( renderTarget.depthTexture, 0 );
+
+		}
 
 		const webglDepthTexture = textureProperties.__webglTexture;
 		const samples = getRenderTargetSamples( renderTarget );
 
+		const glTextureType = isCube ? _gl.TEXTURE_CUBE_MAP_POSITIVE_X + cubeFace : _gl.TEXTURE_2D;
+		const glAttachmentType = renderTarget.depthTexture.format === DepthStencilFormat ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;
+
 		if ( renderTarget.depthTexture.format === DepthFormat ) {
 
 			if ( useMultisampledRTT( renderTarget ) ) {
 
-				multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+				multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0, samples );
 
 			} else {
 
-				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0 );
 
 			}
 
@@ -1690,11 +1738,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			if ( useMultisampledRTT( renderTarget ) ) {
 
-				multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+				multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0, samples );
 
 			} else {
 
-				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0 );
 
 			}
 
@@ -1745,17 +1793,28 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 		if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) {
 
-			if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );
+			if ( isCube ) {
 
-			const mipmaps = renderTarget.texture.mipmaps;
+				// For cube render targets with depth texture, setup each face
+				for ( let i = 0; i < 6; i ++ ) {
 
-			if ( mipmaps && mipmaps.length > 0 ) {
+					setupDepthTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, i );
 
-				setupDepthTexture( renderTargetProperties.__webglFramebuffer[ 0 ], renderTarget );
+				}
 
 			} else {
 
-				setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );
+				const mipmaps = renderTarget.texture.mipmaps;
+
+				if ( mipmaps && mipmaps.length > 0 ) {
+
+					setupDepthTexture( renderTargetProperties.__webglFramebuffer[ 0 ], renderTarget, 0 );
+
+				} else {
+
+					setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 0 );
+
+				}
 
 			}
 
@@ -2337,6 +2396,12 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 	this.setupFrameBufferTexture = setupFrameBufferTexture;
 	this.useMultisampledRTT = useMultisampledRTT;
 
+	this.isReversedDepthBuffer = function () {
+
+		return state.buffers.depth.getReversed();
+
+	};
+
 }
 
 export { WebGLTextures };

+ 40 - 3
src/renderers/webgl/WebGLUniforms.js

@@ -46,7 +46,7 @@ import { Texture } from '../../textures/Texture.js';
 import { DataArrayTexture } from '../../textures/DataArrayTexture.js';
 import { Data3DTexture } from '../../textures/Data3DTexture.js';
 import { DepthTexture } from '../../textures/DepthTexture.js';
-import { LessEqualCompare } from '../../constants.js';
+import { LessEqualCompare, GreaterEqualCompare } from '../../constants.js';
 
 const emptyTexture = /*@__PURE__*/ new Texture();
 
@@ -572,7 +572,7 @@ function setValueT1( gl, v, textures ) {
 
 	if ( this.type === gl.SAMPLER_2D_SHADOW ) {
 
-		emptyShadowTexture.compareFunction = LessEqualCompare; // #28670
+		emptyShadowTexture.compareFunction = textures.isReversedDepthBuffer() ? GreaterEqualCompare : LessEqualCompare;
 		emptyTexture2D = emptyShadowTexture;
 
 	} else {
@@ -822,9 +822,21 @@ function setValueT1Array( gl, v, textures ) {
 
 	}
 
+	let emptyTexture2D;
+
+	if ( this.type === gl.SAMPLER_2D_SHADOW ) {
+
+		emptyTexture2D = emptyShadowTexture;
+
+	} else {
+
+		emptyTexture2D = emptyTexture;
+
+	}
+
 	for ( let i = 0; i !== n; ++ i ) {
 
-		textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
+		textures.setTexture2D( v[ i ] || emptyTexture2D, units[ i ] );
 
 	}
 
@@ -1110,6 +1122,31 @@ class WebGLUniforms {
 
 		}
 
+		// Sort uniforms to prioritize shadow samplers first (for optimal texture unit allocation)
+
+		const shadowSamplers = [];
+		const otherUniforms = [];
+
+		for ( const u of this.seq ) {
+
+			if ( u.type === gl.SAMPLER_2D_SHADOW || u.type === gl.SAMPLER_CUBE_SHADOW || u.type === gl.SAMPLER_2D_ARRAY_SHADOW ) {
+
+				shadowSamplers.push( u );
+
+			} else {
+
+				otherUniforms.push( u );
+
+			}
+
+		}
+
+		if ( shadowSamplers.length > 0 ) {
+
+			this.seq = shadowSamplers.concat( otherUniforms );
+
+		}
+
 	}
 
 	setValue( gl, name, value, textures ) {

+ 76 - 0
src/textures/CubeDepthTexture.js

@@ -0,0 +1,76 @@
+import { DepthTexture } from './DepthTexture.js';
+import { CubeReflectionMapping, NearestFilter, UnsignedIntType, DepthFormat } from '../constants.js';
+
+/**
+ * This class can be used to automatically save the depth information of a
+ * cube rendering into a cube texture with depth format. Used for PointLight shadows.
+ *
+ * @augments DepthTexture
+ */
+class CubeDepthTexture extends DepthTexture {
+
+	/**
+	 * Constructs a new cube depth texture.
+	 *
+	 * @param {number} size - The size (width and height) of each cube face.
+	 * @param {number} [type=UnsignedIntType] - The texture type.
+	 * @param {number} [mapping=CubeReflectionMapping] - The texture mapping.
+	 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
+	 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
+	 * @param {number} [magFilter=NearestFilter] - The mag filter value.
+	 * @param {number} [minFilter=NearestFilter] - The min filter value.
+	 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value.
+	 * @param {number} [format=DepthFormat] - The texture format.
+	 */
+	constructor( size, type = UnsignedIntType, mapping = CubeReflectionMapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, format = DepthFormat ) {
+
+		// Create 6 identical image descriptors for the cube faces
+		const image = { width: size, height: size, depth: 1 };
+		const images = [ image, image, image, image, image, image ];
+
+		// Call DepthTexture constructor with width, height
+		super( size, size, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format );
+
+		// Replace the single image with the array of 6 images
+		this.image = images;
+
+		/**
+		 * This flag can be used for type testing.
+		 *
+		 * @type {boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isCubeDepthTexture = true;
+
+		/**
+		 * Set to true for cube texture handling in WebGLTextures.
+		 *
+		 * @type {boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isCubeTexture = true;
+
+	}
+
+	/**
+	 * Alias for {@link CubeDepthTexture#image}.
+	 *
+	 * @type {Array<Image>}
+	 */
+	get images() {
+
+		return this.image;
+
+	}
+
+	set images( value ) {
+
+		this.image = value;
+
+	}
+
+}
+
+export { CubeDepthTexture };

粤ICP备19079148号