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

Three.js中如何将方向光的作用范围限制在玩家周边的有限区域?

Three.js 限制方向光作用范围的实现方案

方案1:自定义着色器修改方向光衰减(效果最优)

直接修改材质的着色器逻辑,保留方向光的平行光照特性和阴影效果,同时精准控制作用范围,适配所有几何体类型,不会出现面数不足导致的光斑异常问题:

  • 给所有需要接收该方向光的材质添加onBeforeCompile钩子,在片元着色器的光照计算环节插入距离判断逻辑
  • 椭圆形范围只需将点到玩家的欧式距离替换为椭球距离公式即可,示例代码如下:
// 先在uniform里传入玩家位置和范围参数
material.onBeforeCompile = (shader) => {
  shader.uniforms.playerPos = { value: playerMesh.position };
  shader.uniforms.lightRange = { value: 20 }; // 光照范围半径
  shader.uniforms.ellipseRatio = { value: 1.5 }; // 椭圆X轴和Z轴的长度比例
  shader.fragmentShader = `
    uniform vec3 playerPos;
    uniform float lightRange;
    uniform float ellipseRatio;
  ` + shader.fragmentShader;
  // 插入范围衰减逻辑到光照计算前
  shader.fragmentShader = shader.fragmentShader.replace(
    '#include <lights_fragment_begin>',
    `
      #include <lights_fragment_begin>
      // 计算当前片元到玩家的平面椭球距离(俯视视角无需计算Y轴)
      float dx = abs(vWorldPosition.x - playerPos.x) * ellipseRatio;
      float dz = abs(vWorldPosition.z - playerPos.z);
      float dist = sqrt(dx * dx + dz * dz);
      // 超出范围的方向光贡献置0,加渐变过渡避免边缘割裂
      float rangeAtten = 1.0 - smoothstep(lightRange * 0.8, lightRange, dist);
      directionalLightColor *= rangeAtten;
    `
  );
};

方案2:阴影视锥体裁剪+范围遮罩(改造成本最低)

如果不想修改着色器,可以用原生Three.js接口实现,性能开销更低:

  • 保持方向光的配置不变,开启阴影后每帧同步更新方向光的阴影相机视锥体,让视锥体刚好框住玩家周围需要光照的范围,超出视锥体的物体不会接收该方向光的照射和阴影
  • 要实现椭圆形光照范围,只需要在地面材质上叠加一张随玩家移动的椭圆形渐变alpha纹理,或者用全屏后期处理遮罩,把范围外的光照效果抹去即可

方案3:聚光灯优化适配(性能最优)

针对你之前使用聚光灯遇到的问题,做少量优化即可达到需求:

  • 给聚光灯的map参数赋值一张椭圆形的渐变透明度纹理,这样不管接收面的几何体面数多少,都能输出均匀平滑的椭圆光斑,不会出现面数不足导致的光斑异常
  • 调高聚光灯的阴影分辨率、调整阴影偏置参数,就能拿到接近方向光的高质量阴影效果,整体性能开销比前两个方案更低

注意事项

  • 俯视视角下无需计算Y轴的距离,仅判断X、Z轴的平面距离就能满足需求,进一步降低性能开销
  • 边缘使用smoothstep做渐变过渡,可以避免光照范围边缘出现明显的锯齿和割裂感
  • 如果项目使用了后期处理管线,可以直接在光照合成阶段统一做范围裁剪,不需要逐个修改材质的着色器,适配性更高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 20:18:01