实际开发中项目需要启动阴影设置,使用平行光模拟太阳

1. 开启渲染器阴影支持

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;     // 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;   // 常用软阴影
  • THREE.BasicShadowMap 性能高但效果差

  • THREE.PCFShadowMap 常规软阴影

  • THREE.PCFSoftShadowMap 更柔和(推荐)

  • THREE.VSMShadowMap 体积软阴影(需材质支持)

2. 光源开启阴影

以下光源支持阴影

DirectionalLight, Spotlight,PointLight

其中最常用的是平行光

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(20, 20, 10);
light.castShadow = true;   // 开启阴影投射
scene.add(light);

// 调整 shadow map 质量
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;

// 控制阴影相机视锥体(非常关键)
light.shadow.camera.left = -30;
light.shadow.camera.right = 30;
light.shadow.camera.top = 30;
light.shadow.camera.bottom = -30;
light.shadow.camera.near = 1;
light.shadow.camera.far = 100;

3.物体设置是否支持/接受阴影

mesh.castShadow = true; // 投射阴影
mesh.receiveShadow = true; // 接受阴影

4.地面需要接受阴影

使用Plane模拟平行地面

const plane = new THREE.Mesh(
  new THREE.PlaneGeometry(200, 200),
  new THREE.MeshStandardMaterial({ color: 0x999999 })
);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add(plane);

5.完整代码

<template>
  <div class="container" ref="container"></div>
</template>
<script setup>
import { nextTick, onMounted, useTemplateRef, watchEffect } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";

const gui = new GUI();

const containerDom = useTemplateRef("container");

onMounted(() => {
  nextTick(() => {
    const width = containerDom.value.clientWidth;
    const height = containerDom.value.clientHeight;

    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
    camera.lookAt(0, 0, 0);
    camera.position.set(0, 600, 400);
    scene.add(camera);

    const planeGeometry = new THREE.PlaneGeometry(1000, 10000);
    const planeMaterial = new THREE.MeshLambertMaterial({
      color: new THREE.Color("skyblue"),
    });
    const plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.rotateX(-Math.PI / 2);
    plane.position.y = -50;
    plane.receiveShadow = true;
    scene.add(plane);

    const boxGeometry = new THREE.BoxGeometry(100, 100, 100);
    const boxMaterial = new THREE.MeshLambertMaterial({
      color: new THREE.Color("orange"),
    });
    const box = new THREE.Mesh(boxGeometry, boxMaterial);
    box.castShadow = true;
    scene.add(box);
    const box2 = box.clone();
    box2.position.x = 150;
    scene.add(box2);
    const light = new THREE.DirectionalLight(0xffffff, 1.0);
    light.position.set(100, 450, 300);
    light.lookAt(0, 0, 0);
    light.angle = Math.PI / 6;
    light.castShadow = true;
    scene.add(light);
    light.shadow.camera.left = -500;
    light.shadow.camera.right = 500;
    light.shadow.camera.top = 2000;
    light.shadow.camera.bottom = -1000;
    light.shadow.camera.near = 0.1;
    light.shadow.camera.far = 1000;
    light.shadow.mapSize.width = 1024;
    light.shadow.mapSize.height = 1024;
    const lightHelper = new THREE.DirectionalLightHelper(light, 10);
    const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
    scene.add(cameraHelper);
    // scene.add(lightHelper);

    const f1 = gui.addFolder("平行光");
    f1.add(light.position, "x").min(10).max(1000);
    f1.add(light.position, "y").min(10).max(1000);
    f1.add(light.position, "z").min(10).max(1000);
    f1.add(light, "intensity").min(0).max(10);

    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    const renderer = new THREE.WebGLRenderer({
      antialias: true,
    });
    renderer.setSize(width, height);
    renderer.shadowMap.enabled = true; // 开启阴影
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    containerDom.value.append(renderer.domElement);

    const axesHelper = new THREE.AxesHelper(100);
    scene.add(axesHelper);

    const controls = new OrbitControls(camera, renderer.domElement);
    const clock = new THREE.Clock();

    function render() {
      controls.update(clock.getDelta());
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
    render();
  });
});
</script>
<style lang="scss" scoped>
.container {
  width: 100vw;
  height: 100vh;
  background-color: skyblue;
}
</style>
View Code

 

posted on 2026-01-06 16:52  苏荷酒吧  阅读(3)  评论(0)    收藏  举报