webgl_shadow_contact.html 8.6 KB
Newer Older
M
Marco Fugaro 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - contact shadows</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				background-color: #fff;
				color: #000;
			}
			a {
				color: #08f;
			}
		</style>
	</head>
	<body>
		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - contact shadows
		</div>
		<script type="module">

			import * as THREE from '../build/three.module.js';
			import { OrbitControls } from './jsm/controls/OrbitControls.js';
			import Stats from './jsm/libs/stats.module.js';
			import { GUI } from './jsm/libs/dat.gui.module.js';
			import { HorizontalBlurShader } from './jsm/shaders/HorizontalBlurShader.js';
			import { VerticalBlurShader } from './jsm/shaders/VerticalBlurShader.js';

			var camera, scene, renderer, stats, gui;

			var meshes = [];

			const PLANE_WIDTH = 2.5;
			const PLANE_HEIGHT = 2.5;
			const CAMERA_HEIGHT = 0.3;

			var state = {
40 41 42 43 44 45 46 47 48
				shadow: {
					blur: 3.5,
					darkness: 1,
					opacity: 1,
				},
				plane: {
					color: '#ffffff',
					opacity: 1,
				},
M
Marco Fugaro 已提交
49 50 51 52 53
				showWireframe: false,
			};

			var shadowGroup, renderTarget, renderTargetBlur, shadowCamera, cameraHelper, depthMaterial, horizontalBlurMaterial, verticalBlurMaterial;

54
			var plane, blurPlane, fillPlane;
M
Marco Fugaro 已提交
55 56 57 58 59 60 61 62 63 64

			init();
			animate();

			function init() {

				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 );
				camera.position.set( 0.5, 1, 2 );

				scene = new THREE.Scene();
M
Marco Fugaro 已提交
65
				scene.background = new THREE.Color( 0xffffff );
M
Marco Fugaro 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

				stats = new Stats();
				document.body.appendChild( stats.dom );

				window.addEventListener( 'resize', onWindowResize );

				// add the example meshes

				var geometries = [
					new THREE.BoxBufferGeometry( 0.4, 0.4, 0.4 ),
					new THREE.IcosahedronBufferGeometry( 0.3 ),
					new THREE.TorusKnotBufferGeometry( 0.4, 0.05, 256, 24, 1, 3 )
				];

				var material = new THREE.MeshNormalMaterial();

				for ( var i = 0, l = geometries.length; i < l; i ++ ) {

					var angle = ( i / l ) * Math.PI * 2;

					var geometry = geometries[ i ];
					var mesh = new THREE.Mesh( geometry, material );
					mesh.position.y = 0.1;
					mesh.position.x = Math.cos( angle ) / 2.0;
					mesh.position.z = Math.sin( angle ) / 2.0;
					scene.add( mesh );
					meshes.push( mesh );

				}



				// the container, if you need to move the plane just move this
				shadowGroup = new THREE.Group();
				shadowGroup.position.y = - 0.3;
				scene.add( shadowGroup );

				// the render target that will show the shadows in the plane texture
				renderTarget = new THREE.WebGLRenderTarget( 512, 512 );
				renderTarget.texture.generateMipmaps = false;

				// the render target that we will use to blur the first render target
				renderTargetBlur = new THREE.WebGLRenderTarget( 512, 512 );
				renderTargetBlur.texture.generateMipmaps = false;


				// make a plane and make it face up
113 114
				var planeGeometry = new THREE.PlaneBufferGeometry( PLANE_WIDTH, PLANE_HEIGHT ).rotateX( Math.PI / 2 );
				var material = new THREE.MeshBasicMaterial( {
M
Marco Fugaro 已提交
115
					map: renderTarget.texture,
116
					opacity: state.shadow.opacity,
M
Marco Fugaro 已提交
117 118
					transparent: true,
				} );
119
				plane = new THREE.Mesh( planeGeometry, material );
M
Marco Fugaro 已提交
120 121 122 123 124 125 126 127 128 129
				shadowGroup.add( plane );

				// the y from the texture is flipped!
				plane.scale.y = - 1;

				// the plane onto which to blur the texture
				blurPlane = new THREE.Mesh( planeGeometry );
				blurPlane.visible = false;
				shadowGroup.add( blurPlane );

130 131 132 133 134 135 136 137 138 139 140
				// the plane with the color of the ground
				var material = new THREE.MeshBasicMaterial( {
					color: state.plane.color,
					opacity: state.plane.opacity,
					transparent: true,
				} );
				fillPlane = new THREE.Mesh( planeGeometry, material );
				fillPlane.rotateX( Math.PI );
				fillPlane.position.y -= 0.00001;
				shadowGroup.add( fillPlane );

M
Marco Fugaro 已提交
141 142 143 144 145 146 147 148
				// the camera to render the depth material from
				shadowCamera = new THREE.OrthographicCamera( - PLANE_WIDTH / 2, PLANE_WIDTH / 2, PLANE_HEIGHT / 2, - PLANE_HEIGHT / 2, 0, CAMERA_HEIGHT );
				shadowCamera.rotation.x = Math.PI / 2; // get the camera to look up
				shadowGroup.add( shadowCamera );

				cameraHelper = new THREE.CameraHelper( shadowCamera );

				// like MeshDepthMaterial, but goes from black to transparent
M
Marco Fugaro 已提交
149
				depthMaterial = new THREE.MeshDepthMaterial();
150
				depthMaterial.userData.darkness = { value: state.shadow.darkness };
M
Marco Fugaro 已提交
151 152 153 154
				depthMaterial.onBeforeCompile = function ( shader ) {

					shader.uniforms.darkness = depthMaterial.userData.darkness;
					shader.fragmentShader = `
M
Marco Fugaro 已提交
155
						uniform float darkness;
M
Marco Fugaro 已提交
156
						${shader.fragmentShader.replace(
M
Marco Fugaro 已提交
157 158 159
					'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );',
					'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );'
				)}
M
Marco Fugaro 已提交
160 161 162
					`;

				};
M
Marco Fugaro 已提交
163 164 165 166 167 168 169 170 171 172 173 174
				depthMaterial.depthTest = false;
				depthMaterial.depthWrite = false;

				horizontalBlurMaterial = new THREE.ShaderMaterial( HorizontalBlurShader );
				horizontalBlurMaterial.depthTest = false;

				verticalBlurMaterial = new THREE.ShaderMaterial( VerticalBlurShader );
				verticalBlurMaterial.depthTest = false;

				//

				gui = new GUI();
175 176 177 178 179 180 181 182
				var shadowFolder = gui.addFolder( 'shadow' );
				shadowFolder.open();
				var planeFolder = gui.addFolder( 'plane' );
				planeFolder.open();


				shadowFolder.add( state.shadow, 'blur', 0, 15, 0.1 );
				shadowFolder.add( state.shadow, 'darkness', 1, 5, 0.1 ).onChange( function () {
M
Marco Fugaro 已提交
183

184
					depthMaterial.userData.darkness.value = state.shadow.darkness;
M
Marco Fugaro 已提交
185

186 187
				} );
				shadowFolder.add( state.shadow, 'opacity', 0, 1, 0.01 ).onChange( function () {
M
Marco Fugaro 已提交
188

189
					plane.material.opacity = state.shadow.opacity;
M
Marco Fugaro 已提交
190 191

				} );
192
				planeFolder.addColor( state.plane, 'color' ).onChange( function () {
M
Marco Fugaro 已提交
193

194
					fillPlane.material.color = new THREE.Color( state.plane.color );
M
Marco Fugaro 已提交
195 196

				} );
197 198 199 200 201 202
				planeFolder.add( state.plane, 'opacity', 0, 1, 0.01 ).onChange( function () {

					fillPlane.material.opacity = state.plane.opacity;

				} );

M
Marco Fugaro 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
				gui.add( state, 'showWireframe', true ).onChange( function () {

					if ( state.showWireframe ) {

						scene.add( cameraHelper );

					} else {

						scene.remove( cameraHelper );

					}

				} );

				//

M
Marco Fugaro 已提交
219
				renderer = new THREE.WebGLRenderer( { antialias: true } );
M
Marco Fugaro 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( renderer.domElement );

				//

				new OrbitControls( camera, renderer.domElement );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

M
Marco Fugaro 已提交
239
			// renderTarget --> blurPlane (horizontalBlur) --> renderTargetBlur --> blurPlane (verticalBlur) --> renderTarget
M
Marco Fugaro 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
			function blurShadow( amount ) {

				blurPlane.visible = true;

				// blur horizontally and draw in the renderTargetBlur
				blurPlane.material = horizontalBlurMaterial;
				blurPlane.material.uniforms.tDiffuse.value = renderTarget.texture;
				horizontalBlurMaterial.uniforms.h.value = amount * 1 / 256;

				renderer.setRenderTarget( renderTargetBlur );
				renderer.render( blurPlane, shadowCamera );

				// blur vertically and draw in the main renderTarget
				blurPlane.material = verticalBlurMaterial;
				blurPlane.material.uniforms.tDiffuse.value = renderTargetBlur.texture;
				verticalBlurMaterial.uniforms.v.value = amount * 1 / 256;

				renderer.setRenderTarget( renderTarget );
				renderer.render( blurPlane, shadowCamera );

				blurPlane.visible = false;

			}

M
Marco Fugaro 已提交
264
			function animate( ) {
M
Marco Fugaro 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278

				requestAnimationFrame( animate );

				//

				meshes.forEach( mesh => {

					mesh.rotation.x += 0.01;
					mesh.rotation.y += 0.02;

				} );

				//

279
				// remove the background
M
Marco Fugaro 已提交
280 281 282
				var initialBackground = scene.background;
				scene.background = null;

283
				// force the depthMaterial to everything
M
Marco Fugaro 已提交
284 285 286 287 288 289 290 291 292 293 294
				cameraHelper.visible = false;
				scene.overrideMaterial = depthMaterial;

				// render to the render target to get the depths
				renderer.setRenderTarget( renderTarget );
				renderer.render( scene, shadowCamera );

				// and reset the override material
				scene.overrideMaterial = null;
				cameraHelper.visible = true;

295
				blurShadow( state.shadow.blur );
M
Marco Fugaro 已提交
296 297 298

				// a second pass to reduce the artifacts
				// (0.4 is the minimum blur amout so that the artifacts are gone)
299
				blurShadow( state.shadow.blur * 0.4 );
M
Marco Fugaro 已提交
300 301 302 303 304 305

				// reset and render the normal scene
				renderer.setRenderTarget( null );
				scene.background = initialBackground;

				renderer.render( scene, camera );
M
Marco Fugaro 已提交
306
				stats.update();
M
Marco Fugaro 已提交
307 308 309 310 311 312

			}

		</script>
	</body>
</html>