1. 启用阴影映射
首先需要在渲染器上启用阴影映射:
renderer.shadowMap.enabled = true;
2. 配置光源投射阴影
只有三种光源可以投射阴影:
- DirectionalLight(方向光)
模拟太阳光,无限远平行光线(例:开放世界游戏的全局光照)。 - PointLight(点光源)
从单点向四周均匀发光(例:FPS游戏中的爆炸特效光源)。 - SpotLight(聚光灯)
锥形照射范围,可调节角度与衰减(例:竞速游戏的车前灯渲染)。
不受光照影响的材质(不能产生阴影):
-
MeshBasicMaterial
MeshNormalMaterial (虽然不受光照,但会根据法线显示颜色)
-
MeshDepthMaterial (显示深度)
MeshMatcapMaterial (使用预计算的光照,但不受场景中灯光影响)
受光照影响的材质(可以产生阴影):
-
MeshLambertMaterial (朗伯材质,对光照反应明显,性能较好)
-
MeshPhongMaterial (冯氏材质,有高光效果)
-
MeshStandardMaterial (基于物理的渲染PBR材质,金属度和粗糙度)
-
MeshPhysicalMaterial (扩展的PBR材质,增加了透明涂层、光泽度等)
设置光源投射阴影:
directionalLight.castShadow = true;
3. 配置物体属性
- 需要投射阴影的物体:object.castShadow = true;
- 需要接收阴影的物体:object.receiveShadow = true;
注意事项:必须同时满足三个条件才能显示阴影:1.渲染器启用阴影 2.光源启用投射 3.物体设置接收/投射属性
4. 优化阴影质量
通过提高阴影映射分辨率来改善阴影质量:
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
补充说明:“分辨率越高性能消耗越大,建议根据设备性能调整”
代码如下:
<template>
<div class="container" ref="containerRef"></div>
</template>
<script setup>
import * as THREE from "three";
import { onMounted, ref } from "vue";
// Controls鼠标交互
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import * as dat from "dat.gui";
const containerRef = ref(null);
const stat = new Stats();
// 场景、相机、渲染器
const scene = new THREE.Scene();
// 环境光
scene.add(new THREE.AmbientLight(0xffffff, 0.2));
//方向光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true; // 开启阴影投射
//阴影更光滑优化
directionalLight.shadow.mapSize.set(2048, 2048);
scene.add(directionalLight);
// gui
const gui = new dat.GUI();
gui.add(directionalLight.position, "x", -5, 5, 0.01).name("X灯光的位置");
gui.add(directionalLight.position, "y", -5, 5, 0.01).name("Y灯光的位置");
gui.add(directionalLight.position, "z", -5, 5, 0.01).name("z灯光的位置");
// 只有三种光能产生阴影 DirectionalLight PointLight SpotLight
// Plane
const planeG = new THREE.PlaneGeometry(4, 4);
const planeM = new THREE.MeshStandardMaterial({
color: 0xcccccc,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeG, planeM);
plane.receiveShadow = true; // 接收阴影
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
// 添加一个球体作为示例
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0xffff00,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(0, 0.5, 0);
// sphere.position.y = 1; // 将球体稍微抬高,避免与平面重合
sphere.castShadow = true; // 投射阴影
scene.add(sphere);
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(4, 2, 5);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true; // 启用阴影映射
// 坐标轴
const axes = new THREE.AxesHelper(50);
scene.add(axes);
// 控制器和动画
let orbitControls;
const clock = new THREE.Clock();
function animate() {
const time = clock.getElapsedTime();
sphere.position.y = Math.abs(Math.sin(time)) + 0.5;
// 更新控制器
if (orbitControls) {
orbitControls.update();
}
// 渲染
renderer.render(scene, camera);
stat.update();
requestAnimationFrame(animate);
}
// 挂载
onMounted(() => {
// 初始化轨道控制器
orbitControls = new OrbitControls(camera, containerRef.value);
orbitControls.enableDamping = true;
// 添加到DOM
containerRef.value.appendChild(renderer.domElement);
containerRef.value.appendChild(stat.domElement);
// 开始动画
animate();
});
// 窗口大小调整
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
<style>
.container {
width: 100%;
height: 100vh;
overflow: hidden;
}
</style>
2420

被折叠的 条评论
为什么被折叠?



