Three.js阴影实现与注意事项(基础版)

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>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值