【技术调研】三维(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.静态的三个小球,使用的是鼠标+相机方向进行光线投射,鼠标点击时触发。即鼠标点击会变黄色。
<!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 = -2const 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 = -2sphere4.position.z = -5const sphere5 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32),new THREE.MeshBasicMaterial({color: 0xff0000}))sphere5.position.z = -5const sphere6 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32),new THREE.MeshBasicMaterial({color: 0xff0000}))sphere6.position.x = 2;sphere6.position.z = -5scene.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.5sphere2.position.y = Math.sin(elapsedTime * 0.7) * 1.5sphere3.position.y = Math.sin(elapsedTime * 1.4) * 1.5const 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>