阴影投射
阴影是灯光经过物体后产生的,几个关键的设置:
- 灯光属性设置:.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 = -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>