ShadowMapViewer.js 5.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * @author arya-s / https://github.com/arya-s
 *
 * This is a helper for visualising a given light's shadow map.
 * It works for shadow casting lights: THREE.DirectionalLight and THREE.SpotLight.
 * It renders out the shadow map and displays it on a HUD.
 *
 * Example usage:
 *	1) Include <script src='examples/js/utils/ShadowMapViewer.js'><script> in your html file
 *
 *	2) Create a shadow casting light and name it optionally:
 *		var light = new THREE.DirectionalLight( 0xffffff, 1 );
 *		light.castShadow = true;
 *		light.name = 'Sun';
 *
 *	3) Create a shadow map viewer for that light and set its size and position optionally:
M
Mr.doob 已提交
17
 *		var shadowMapViewer = new THREE.ShadowMapViewer( light );
18 19 20 21 22 23 24 25
 *		shadowMapViewer.size.set( 128, 128 );	//width, height  default: 256, 256
 *		shadowMapViewer.position.set( 10, 10 );	//x, y in pixel	 default: 0, 0 (top left corner)
 *
 *	4) Render the shadow map viewer in your render loop:
 *		shadowMapViewer.render( renderer );
 *
 *	5) Optionally: Update the shadow map viewer on window resize:
 *		shadowMapViewer.updateForWindowResize();
A
arya-s 已提交
26 27
 *
 *	6) If you set the position or size members directly, you need to call shadowMapViewer.update();
28 29 30 31 32 33
 */

THREE.ShadowMapViewer = function ( light ) {

	//- Internals
	var scope = this;
A
arya-s 已提交
34 35
	var doRenderLabel = ( light.name !== undefined && light.name !== '' );
	var userAutoClearSetting;
36 37

	//Holds the initial position and dimension of the HUD
M
Mr.doob 已提交
38
	var frame = {
39 40 41 42 43 44
		x: 10,
		y: 10,
		width: 256,
		height: 256,
	};

A
arya-s 已提交
45
	var camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 10 );
46
	camera.position.set( 0, 0, 2 );
47 48 49 50 51 52 53
	var scene = new THREE.Scene();

	//HUD for shadow map
	var shader = THREE.UnpackDepthRGBAShader;

	var uniforms = new THREE.UniformsUtils.clone( shader.uniforms );
	var material = new THREE.ShaderMaterial( {
54 55 56
		uniforms: uniforms,
		vertexShader: shader.vertexShader,
		fragmentShader: shader.fragmentShader
57 58 59 60 61 62 63 64
	} );
	var plane = new THREE.PlaneBufferGeometry( frame.width, frame.height );
	var mesh = new THREE.Mesh( plane, material );

	scene.add( mesh );


	//Label for light's name
A
arya-s 已提交
65 66
	var labelCanvas, labelMesh;

67
	if ( doRenderLabel ) {
68

A
arya-s 已提交
69
		labelCanvas = document.createElement( 'canvas' );
70

A
arya-s 已提交
71 72
		var context = labelCanvas.getContext( '2d' );
		context.font = 'Bold 20px Arial';
73

A
arya-s 已提交
74 75 76
		var labelWidth = context.measureText( light.name ).width;
		labelCanvas.width = labelWidth;
		labelCanvas.height = 25;	//25 to account for g, p, etc.
77

A
arya-s 已提交
78 79 80
		context.font = 'Bold 20px Arial';
		context.fillStyle = 'rgba( 255, 0, 0, 1 )';
		context.fillText( light.name, 0, 20 );
81

A
arya-s 已提交
82 83 84 85
		var labelTexture = new THREE.Texture( labelCanvas );
		labelTexture.magFilter = THREE.LinearFilter;
		labelTexture.minFilter = THREE.LinearFilter;
		labelTexture.needsUpdate = true;
86

A
arya-s 已提交
87 88
		var labelMaterial = new THREE.MeshBasicMaterial( { map: labelTexture, side: THREE.DoubleSide } );
		labelMaterial.transparent = true;
89

A
arya-s 已提交
90 91 92
		var labelPlane = new THREE.PlaneBufferGeometry( labelCanvas.width, labelCanvas.height );
		labelMesh = new THREE.Mesh( labelPlane, labelMaterial );

93
		scene.add( labelMesh );
A
arya-s 已提交
94 95

	}
96 97 98 99


	function resetPosition () {

100
		scope.position.set( scope.position.x, scope.position.y );
101 102 103 104 105

	}

	//- API
	// Set to false to disable displaying this shadow map
M
Mr.doob 已提交
106
	this.enabled = true;
107 108 109

	// Set the size of the displayed shadow map on the HUD
	this.size = {
A
arya-s 已提交
110 111 112
		width: frame.width,
		height: frame.height,
		set: function ( width, height ) {
113

A
arya-s 已提交
114 115
			this.width = width;
			this.height = height;
116

A
arya-s 已提交
117
			mesh.scale.set( this.width / frame.width, this.height / frame.height, 1 );
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

			//Reset the position as it is off when we scale stuff
			resetPosition();

		}
	};

	// Set the position of the displayed shadow map on the HUD
	this.position = {
		x: frame.x,
		y: frame.y,
		set: function ( x, y ) {

			this.x = x;
			this.y = y;

A
arya-s 已提交
134 135
			var width = scope.size.width;
			var height = scope.size.height;
136

G
gero3 已提交
137
			mesh.position.set( - window.innerWidth / 2 + width / 2 + this.x, window.innerHeight / 2 - height / 2 - this.y, 0 );
A
arya-s 已提交
138

139
			if ( doRenderLabel ) labelMesh.position.set( mesh.position.x, mesh.position.y - scope.size.height / 2 + labelCanvas.height / 2, 0 );
140 141 142 143 144 145 146 147 148 149 150 151 152

		}
	};

	this.render = function ( renderer ) {

		if ( this.enabled ) {

			//Because a light's .shadowMap is only initialised after the first render pass
			//we have to make sure the correct map is sent into the shader, otherwise we
			//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
153
			uniforms.tDiffuse.value = light.shadow.map.texture;
154

A
arya-s 已提交
155
			userAutoClearSetting = renderer.autoClear;
156
			renderer.autoClear = false; // To allow render overlay
B
brason 已提交
157
			renderer.clearDepth();
158
			renderer.render( scene, camera );
A
arya-s 已提交
159
			renderer.autoClear = userAutoClearSetting;	//Restore user's setting
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

		}

	};

	this.updateForWindowResize = function () {

		if ( this.enabled ) {

			 camera.left = window.innerWidth / - 2;
			 camera.right = window.innerWidth / 2;
			 camera.top = window.innerHeight / 2;
			 camera.bottom = window.innerHeight / - 2;

		}

	};

A
arya-s 已提交
178 179 180 181 182 183 184 185 186
	this.update = function () {

		this.position.set( this.position.x, this.position.y );
		this.size.set( this.size.width, this.size.height );

	};

	//Force an update to set position/size
	this.update();
187

B
brason 已提交
188
};
189

190
THREE.ShadowMapViewer.prototype.constructor = THREE.ShadowMapViewer;