Three.js中使用HDR环境纹理时无法生成/接收阴影问题
问题:Three.js中3D模型无法投射阴影的解决方法
我是Three.js新手,正在开发一个项目,需要让下载的3D模型投射阴影,同时使用下载的HDR文件。已设置scene.environment = texture、模型网格的castShadow和receiveShadow为true、平面开启receiveShadow,也启用了渲染器的阴影映射,但阴影仍无法生成。使用版本:three-v0.162.0,node-v20.11.1,vite-v5.2.0。以下是我的index.js代码:
import * as THREE from "three"; import "./style.css"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"; // Scene const scene = new THREE.Scene(); // Sizes const sizes = { width: 800, height: 500, }; const gltfLoader = new GLTFLoader(); const rgbeloader = new RGBELoader(); let car3; rgbeloader.load("./WhiteNeons_NAD.hdr", (texture) => { texture.mapping = THREE.EquirectangularReflectionMapping; scene.environment = texture; scene.castShadow = true; // 此处无效,属于错误设置 gltfLoader.load("./models/car3/scene.gltf", (gltf) => { const model = gltf.scene; model.position.set(0, 0, 0); model.traverse((node) => { node.castShadow = node.isMesh; node.receiveShadow = node.isMesh; }); scene.add(model); car3 = model; }, undefined, (error) => { console.error(error); }); }, undefined, (err) => { console.error("HDR:: ", err); }); // Plane const planeGeometry = new THREE.PlaneGeometry(10, 10); const planeMat = new THREE.MeshBasicMaterial({ color: 0x5a5a5a }); // 此材质无法接收阴影 const plane = new THREE.Mesh(planeGeometry, planeMat); plane.receiveShadow = true; plane.rotation.x = -0.5 * Math.PI; scene.add(plane); const gridHelper = new THREE.GridHelper(); gridHelper.receiveShadow = true; scene.add(gridHelper); // Camera const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height, 0.1, 1000); camera.position.set(5, 2, 0); scene.add(camera); // Renderer const canvas = document.querySelector(".webgl"); const renderer = new THREE.WebGLRenderer({ canvas }); renderer.setClearColor(0xa3a3a3); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.outputColorSpace = THREE.SRGBColorSpace; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 6; renderer.setSize(sizes.width, sizes.height); renderer.setPixelRatio(window.devicePixelRatio); renderer.render(scene, camera); // Controls const controls = new OrbitControls(camera, canvas); window.addEventListener("resize", () => { camera.updateProjectionMatrix(); }); function animate(time) { if (car3) { car3.rotation.y = -time / 1000; } controls.update(); requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
修复方案
1. 添加可投射阴影的光源
HDR环境贴图仅负责环境反射和全局照明,Three.js的阴影必须由明确的光源(如平行光、点光)生成,且需开启光源的castShadow属性:
// 添加平行光(示例) const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(5, 10, 5); directionalLight.castShadow = true; // 开启光源投射阴影 // 调整阴影相机参数,确保覆盖模型和平面范围 directionalLight.shadow.camera.left = -10; directionalLight.shadow.camera.right = 10; directionalLight.shadow.camera.top = 10; directionalLight.shadow.camera.bottom = -10; directionalLight.shadow.mapSize.set(2048, 2048); // 提升阴影分辨率 scene.add(directionalLight);
2. 替换平面材质
MeshBasicMaterial不受光照和阴影影响,需替换为支持光照的材质:
const planeMat = new THREE.MeshStandardMaterial({ color: 0x5a5a5a });
3. 删除无效的场景阴影设置
scene.castShadow是无效属性,仅网格和光源拥有该属性,需删除此代码。
4. 调整模型位置(可选)
确保模型在平面上方,避免重叠导致阴影被遮挡:
model.position.set(0, 0.5, 0); // 根据模型实际尺寸调整Y轴高度
修复后的完整代码
import * as THREE from "three"; import "./style.css"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"; // Scene const scene = new THREE.Scene(); // Sizes const sizes = { width: window.innerWidth, height: window.innerHeight, }; // 添加平行光(负责生成阴影) const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(5, 10, 5); directionalLight.castShadow = true; directionalLight.shadow.camera.left = -10; directionalLight.shadow.camera.right = 10; directionalLight.shadow.camera.top = 10; directionalLight.shadow.camera.bottom = -10; directionalLight.shadow.mapSize.set(2048, 2048); scene.add(directionalLight); const gltfLoader = new GLTFLoader(); const rgbeloader = new RGBELoader(); let car3; rgbeloader.load("./WhiteNeons_NAD.hdr", (texture) => { texture.mapping = THREE.EquirectangularReflectionMapping; scene.environment = texture; gltfLoader.load("./models/car3/scene.gltf", (gltf) => { const model = gltf.scene; model.position.set(0, 0.5, 0); // 调整模型高度避免与平面重叠 model.traverse((node) => { if (node.isMesh) { node.castShadow = true; node.receiveShadow = true; } }); scene.add(model); car3 = model; }, undefined, (error) => { console.error(error); }); }, undefined, (err) => { console.error("HDR:: ", err); }); // Plane const planeGeometry = new THREE.PlaneGeometry(10, 10); const planeMat = new THREE.MeshStandardMaterial({ color: 0x5a5a5a }); // 替换为可接收阴影的材质 const plane = new THREE.Mesh(planeGeometry, planeMat); plane.receiveShadow = true; plane.rotation.x = -0.5 * Math.PI; scene.add(plane); const gridHelper = new THREE.GridHelper(); scene.add(gridHelper); // Camera const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height, 0.1, 1000); camera.position.set(5, 2, 0); scene.add(camera); // Renderer const canvas = document.querySelector(".webgl"); const renderer = new THREE.WebGLRenderer({ canvas }); renderer.setClearColor(0xa3a3a3); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.outputColorSpace = THREE.SRGBColorSpace; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 6; renderer.setSize(sizes.width, sizes.height); renderer.setPixelRatio(window.devicePixelRatio); // Controls const controls = new OrbitControls(camera, canvas); controls.enableDamping = true; // 添加阻尼使交互更平滑 window.addEventListener("resize", () => { sizes.width = window.innerWidth; sizes.height = window.innerHeight; camera.aspect = sizes.width / sizes.height; camera.updateProjectionMatrix(); renderer.setSize(sizes.width, sizes.height); }); function animate(time) { if (car3) { car3.rotation.y = -time / 1000; } controls.update(); renderer.render(scene, camera); requestAnimationFrame(animate); } animate();
内容的提问来源于stack exchange,提问作者Deep Patel
相关产品推荐
相关产品推荐

