You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:17:17