【技术调研】三维(4)-ThreeJs阴影投射、光线投射及案例

阴影投射

阴影是灯光经过物体后产生的,几个关键的设置:

  • 灯光属性设置:.castShadow : Boolean 。此属性设置为 true 灯光将投射阴影。注意:这样做的代价比较高,需要通过调整让阴影看起来正确。 查看 DirectionalLightShadow 了解详细信息。 默认值为 false
  • 物体属性设置:
    • .castShadow : Boolean 对象是否被渲染到阴影贴图中。默认值为false
    • .receiveShadow : Boolean 材质是否接收阴影。默认值为false
  • 渲染器设置:.shadowMap : enabled: 如果设置开启,允许在场景中使用阴影贴图。默认是 false

阴影投射动画案例

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<title>My first three.js app</title>
	<style>
		body {
			margin: 0;
		}
	</style>
</head>

<body>
	<script type="module">
		import * as THREE from "three";
		import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

		const scene = new THREE.Scene();
		const camera = new THREE.PerspectiveCamera(
			75,
			window.innerWidth / window.innerHeight,
			0.1,
			1000
		);
		camera.position.set(0, 50, 100);
		camera.lookAt(0, 0, 0);

		const renderer = new THREE.WebGLRenderer({
			antialias: true
		})
		renderer.setSize(window.innerWidth, window.innerHeight);
		renderer.shadowMap.enabled = true;
		renderer.shadowMapType = THREE.PCFSoftShadowMap;
		document.body.appendChild(renderer.domElement);

		//创建坐标格辅助对象
		// const gridHelper = new THREE.GridHelper(100,20,0xffffff );
		// scene.add( gridHelper );






		/**
		 *  创建地面
		 */
		const geometry2 = new THREE.PlaneGeometry(100, 100);
		const material2 = new THREE.MeshStandardMaterial({ color: 0xffffff });
		const plane = new THREE.Mesh(geometry2, material2);
		plane.rotation.x = -Math.PI / 2;
		plane.receiveShadow = true;
		scene.add(plane);
		//旋转


		/**
		 *  创建一个立方体
		 */
		const geometry = new THREE.BoxGeometry(10, 10, 10);
		const textureLoader = new THREE.TextureLoader();
		const texture = textureLoader.load('./img/Banner.png');
		const material = new THREE.MeshStandardMaterial({ map: texture });
		const cube = new THREE.Mesh(geometry,material);
		cube.position.set(0, 5, 0);
		cube.castShadow = true;
		scene.add(cube);


		/**
		 * 灯光
		 */
		//环境光
		const ambientLight = new THREE.AmbientLight(0x404040); // 柔和的白光
		scene.add(ambientLight);
		// 平行光
		const directionLight = new THREE.DirectionalLight(0xffffff, 1);
		directionLight.castShadow = true;
		//让阴影更清晰
		directionLight.shadow.mapSize.width = 2048;
		directionLight.shadow.mapSize.height = 2048;
		//不设置以下内容看不见
		directionLight.shadow.camera.left = -100;
		directionLight.shadow.camera.right = 100;
		directionLight.shadow.camera.top = 100;
		directionLight.shadow.camera.bottom = -100;
		directionLight.position.set(150, 20, 0);
		const directionalLightHelper = new THREE.DirectionalLightHelper(directionLight, 5);
		scene.add(directionLight, directionalLightHelper);
		// 点光源
		const pointLight = new THREE.PointLight( 0xffffff, 500 );
		const pointLightHelper = new THREE.PointLightHelper(pointLight,1);
		pointLight.castShadow = true;
		pointLight.position.set( 10, 10, 0 );
		scene.add( pointLight,pointLightHelper );
		/**
		 * 用于查看投射相机
		 */
		// const cam = directionLight.shadow.camera;
		// const cameraHelper = new THREE.CameraHelper(cam);
		// scene.add(cameraHelper);
		// cameraHelper.visible = true;




		const controls = new OrbitControls(camera, renderer.domElement);
		controls.enableDamping = true;
		let angle = 0;
		const animete = () => {
			angle += 0.01;
			pointLight.position.set(10*Math.cos(angle),pointLight.position.y,10*Math.sin(angle));
			requestAnimationFrame(animete);
			renderer.render(scene, camera);
		};
		animete();

	</script>
</body>

</html>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

光线投射

RayCaster可以向特定方向投射光线,并测试哪些对象与其相交。光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。

应用场景:

  1. 测试相机前方是否有一堵墙(障碍)
  2. 光线是否击中目标
  3. 当鼠标移动时测试是否有物体位于光标下方,以此模拟鼠标事件
  4. 当物体朝向特定某处时提示信息

光线投射动画

1.动态的三个小球使用了一个固定方向的光线投射,被光线穿透会变绿色。

2.静态的三个小球,使用的是鼠标+相机方向进行光线投射,鼠标点击时触发。即鼠标点击会变黄色。

<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body {
				margin: 0;
			}
		</style>
	</head>

	<body>
		<script type="module">
			import * as THREE from "three";
			import {
				OrbitControls
			} from 'three/addons/controls/OrbitControls.js';

			const scene = new THREE.Scene();
			const camera = new THREE.PerspectiveCamera(
				75,
				window.innerWidth / window.innerHeight,
				0.1,
				1000
			);
			camera.position.set(0, 5, 5);
			camera.lookAt(0, 0, 0);

			const renderer = new THREE.WebGLRenderer({
				antialias: true
			})
			renderer.setSize(window.innerWidth, window.innerHeight);
			renderer.shadowMap.enabled = true;
			renderer.shadowMapType = THREE.PCFSoftShadowMap;
			document.body.appendChild(renderer.domElement);




			/**
			 *  创建地面
			 */
			const geometry2 = new THREE.PlaneGeometry(100, 100);
			const material2 = new THREE.MeshStandardMaterial({
				color: 0xffffff
			});
			const plane = new THREE.Mesh(geometry2, material2);
			//旋转
			plane.rotation.x = -Math.PI / 2;
			plane.receiveShadow = true;
			plane.position.y = -5;
			scene.add(plane);


			/**
			 * 球1
			 */
			const sphere1 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)
			sphere1.position.x = -2
			const sphere2 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)

			const sphere3 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)
			sphere3.position.x = 2;
			scene.add(sphere1, sphere2, sphere3)
			const sphere4 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)
			sphere4.position.x = -2
			sphere4.position.z = -5
			const sphere5 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)
			sphere5.position.z = -5
			const sphere6 = new THREE.Mesh(
				new THREE.SphereGeometry(0.5, 32, 32),
				new THREE.MeshBasicMaterial({
					color: 0xff0000
				})
			)
			sphere6.position.x = 2;
			sphere6.position.z = -5
			scene.add(sphere4, sphere5, sphere6)


			/**
			 * 灯光
			 */
			//环境光
			const ambientLight = new THREE.AmbientLight(0x404040); // 柔和的白光
			scene.add(ambientLight);

			/**
			 * 创建光线投射
			 */
			const raycaster = new THREE.Raycaster()
			//射线原点
			const rayOrigin = new THREE.Vector3(-3, 0, 0)
			//射线方向
			const rayDirection = new THREE.Vector3(10, 0, 0)
			//将该向量的方向设置为和原向量相同,但是其长度
			rayDirection.normalize()
			raycaster.set(rayOrigin, rayDirection)
			// 检测和射线相交的物体。
			const intersect = raycaster.intersectObject(sphere1)
			console.log(intersect)
			// 检测和射线相交的一组物体。
			const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3])
			console.log(intersects)
			console.log(raycaster)


			/**
			 * 创建用于鼠标控制的光线投射
			 */
			const raycaster2 = new THREE.Raycaster();
			const objectsToTests = [sphere4, sphere5, sphere6];
			/**
			 * 获取鼠标位置,转换为x,y形成射线原点
			 */
			const mouse = new THREE.Vector2();
			window.addEventListener("mousedown", (event) => {

				mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
				mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
				//设置后会根据鼠标和相机 进行光线投射
				console.log(camera.position);
				raycaster2.setFromCamera(mouse, camera);
				console.log(raycaster2);
				console.log(mouse);
				const intersectObjects = raycaster2.intersectObjects(objectsToTests);
				console.log(intersectObjects);
				for (const object of objectsToTests) {
					object.material.color.set(0xff0000);
				}
				for (const intersect of intersectObjects) {
					intersect.object.material.color.set(0xFFFF00)
				}

			})


			const controls = new OrbitControls(camera, renderer.domElement);
			controls.enableDamping = true;
			const clock = new THREE.Clock()
			const animete = () => {

				const elapsedTime = clock.getElapsedTime();

				sphere1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
				sphere2.position.y = Math.sin(elapsedTime * 0.7) * 1.5
				sphere3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
				const objectsToTests = [sphere1, sphere2, sphere3]
				const intersectObjects = raycaster.intersectObjects(objectsToTests)

				for (const object of objectsToTests) {
					object.material.color.set(0xff0000)
				}
				for (const intersect of intersectObjects) {
					intersect.object.material.color.set(0x008000)
				}
				
				controls.update()
				requestAnimationFrame(animete);
				renderer.render(scene, camera);
			};
			animete();
		</script>
	</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值