实际开发中项目需要启动阴影设置,使用平行光模拟太阳
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>
浙公网安备 33010602011771号