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

Three.js中地面纹理波纹与阴影参数的非试错配置方法

解决Three.js DirectionalLight阴影参数精准配置与纹理波纹问题

核心逻辑梳理

DirectionalLight的阴影依赖正交相机实现,只有当相机视锥体完整包裹「所有投阴影的物体+它们在接收面上的投影范围」,且相机方向与灯光方向完全一致时,阴影才能正常显示。参数配置的关键是精准计算视锥体范围,而非试错。

无需试错的参数配置方法

1. 明确阴影覆盖的目标区域

先定义场景中需要投射/接收阴影的范围:比如地面是x[-25,25]、z[-25,25]、y=0的平面,球体活动范围是y[0,20]。

2. 计算阴影相机视锥体范围

针对非垂直灯光(如位置(-20,35,-20)看向原点),需将目标区域的所有顶点投影到阴影相机的局部坐标系,以此确定left/right/top/bottom:

// 定义目标区域的所有关键顶点(地面四角+球体活动范围的四角)
const targetPoints = [
  new THREE.Vector3(-25, 0, -25), new THREE.Vector3(25, 0, -25),
  new THREE.Vector3(25, 0, 25), new THREE.Vector3(-25, 0, 25),
  new THREE.Vector3(-25, 20, -25), new THREE.Vector3(25, 20, -25),
  new THREE.Vector3(25, 20, 25), new THREE.Vector3(-25, 20, 25)
];

const shadowCam = directionalLight.shadow.camera;
// 更新相机世界矩阵并反转,用于将世界坐标转为相机局部坐标
shadowCam.updateMatrixWorld();
const camInverse = new THREE.Matrix4().getInverse(shadowCam.matrixWorld);

let minX = Infinity, maxX = -Infinity;
let minY = Infinity, maxY = -Infinity;
let maxZ = -Infinity;

// 遍历顶点计算局部空间的极值
targetPoints.forEach(point => {
  const localPoint = point.clone().applyMatrix4(camInverse);
  minX = Math.min(minX, localPoint.x);
  maxX = Math.max(maxX, localPoint.x);
  minY = Math.min(minY, localPoint.y);
  maxY = Math.max(maxY, localPoint.y);
  maxZ = Math.max(maxZ, localPoint.z);
});

// 添加5%余量避免边缘阴影被裁剪
const padding = 0.05;
const xRange = maxX - minX;
const yRange = maxY - minY;
shadowCam.left = minX - xRange * padding;
shadowCam.right = maxX + xRange * padding;
shadowCam.bottom = minY - yRange * padding;
shadowCam.top = maxY + yRange * padding;

// 设置near/far:near避免z-fighting,far覆盖相机到目标区域的最远距离
shadowCam.near = 0.1;
shadowCam.far = maxZ + 10;
shadowCam.updateProjectionMatrix();

3. 解决纹理波纹问题

波纹源于阴影贴图分辨率不足或视锥体范围过大导致贴图拉伸,解决方法:

  • 提高阴影贴图分辨率:directionalLight.shadow.mapSize.set(2048, 2048)(可根据性能调整到4096x4096)
  • 启用软阴影优化质量:renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  • 严格控制视锥体范围,避免过度覆盖无关区域

针对指定灯光参数的配置示例

先确保灯光方向正确:

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(-20, 35, -20);
directionalLight.lookAt(0, 0, 0);
directionalLight.castShadow = true;

再套用上面的参数计算代码,即可精准覆盖地面和球体的阴影范围,同时避免纹理波纹。

额外注意事项

  • 地面需设置receiveShadow = true,球体需设置castShadow = true
  • 轻微抬高地面位置(如y=0.01)或调整shadowCam.near,避免z-fighting
  • 若场景中有动态物体,可在物体活动范围变化时重新计算阴影参数,无需每一帧更新

内容的提问来源于stack exchange,提问作者user4657635

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 20:55:18