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
相关产品推荐
相关产品推荐

