找回密码
 立即注册
首页 业界区 科技 【光照】[漫反射]UnityURP兰伯特有光照衰减吗? ...

【光照】[漫反射]UnityURP兰伯特有光照衰减吗?

忙贬 2025-9-20 08:51:58
【从UnityURP开始探索游戏渲染】专栏-直达
光照衰减的基本原理

在物理正确的光照模型中,衰减需要遵循两个基本定律:

  • 平方反比定律‌:光强与距离平方成反比 (I ∝ 1/r²)
  • 余弦定律‌:表面接收的光强与入射角余弦成正比 (I ∝ cosθ)
经典兰伯特模型的衰减处理

标准兰伯特公式

$漫反射 = 表面颜色 * 表面反照率 * max(0, N·L)$
衰减实现分析


  • 角度衰减‌:

    • ✅ 正确实现余弦定律
    • 通过 N·L 点积计算入射角衰减
    • 符合物理规律:光线入射角越大,光照强度越小

  • 距离衰减‌:

    • ⚠️ ‌完全缺失距离衰减计算
    • 公式中没有包含光源距离(r)相关项
    • 光强不会随距离增加而减弱
    • 导致物理不准确性

Unity URP中的实现方案

距离衰减补偿机制

URP通过额外计算衰减因子来弥补兰伯特的不足:
  1. hlsl
  2. // URP光源获取函数 (Lighting.hlsl)
  3. Light GetMainLight()
  4. {
  5.     Light light;
  6.     light.direction = _MainLightPosition.xyz;
  7.     // 距离衰减计算
  8.     float distance = length(_WorldSpaceCameraPos - positionWS);
  9.     light.distanceAttenuation = 1.0 / max(distance * distance, 0.01);
  10.     light.color = _MainLightColor.rgb;
  11.     return light;
  12. }
  13. // 主光源 漫反射计算
  14. lightingData.mainLightColor += CalculateBlinnPhong(mainLight, inputData, surfaceData);
  15. half3 CalculateBlinnPhong(Light light, InputData inputData, SurfaceData surfaceData)
  16. {
  17. // 这里通过颜色计算了光线衰减
  18.     half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
  19.     half3 lightDiffuseColor = LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS);
  20.     half3 lightSpecularColor = half3(0,0,0);
  21.     #if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
  22.     half smoothness = exp2(10 * surfaceData.smoothness + 1);
  23.     lightSpecularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, half4(surfaceData.specular, 1), smoothness);
  24.     #endif
  25. #if _ALPHAPREMULTIPLY_ON
  26.     return lightDiffuseColor * surfaceData.albedo * surfaceData.alpha + lightSpecularColor;
  27. #else
  28.     return lightDiffuseColor * surfaceData.albedo + lightSpecularColor;
  29. #endif
  30. }
复制代码
URP衰减系统组成

光源类型处理‌:


  • 平行光:无距离衰减 (1.0)
  • 点光源:平方反比衰减 (1/r²)
  • 聚光灯:角度衰减 × 距离衰减
优化策略‌:


  • 使用预计算的衰减纹理
  • 最大距离截断(light.range)
  • 平滑过渡边缘处理
graph TD    A[光源类型] --> B{平行光?}    B -->|是| C[衰减=1.0]    B -->|否| D{点光源?}    D -->|是| E[1/r²计算]    D -->|否| F[聚光灯衰减曲线]    E --> G[距离截断]    F --> G    G --> H[平滑过渡]为什么经典兰伯特缺乏距离衰减


  • 历史设计局限‌:

    • 早期计算机图形学简化模型
    • 源自环境固定的CAD渲染需求
    • 仅考虑局部表面光照

  • 数学简化考量‌:

    • 减少每像素计算量
    • 避免昂贵的距离计算
    • 保持公式简洁性

  • 艺术导向设计‌:

    • 允许美术师手动控制光照范围
    • 避免距离导致的过度变暗
    • 更适合风格化渲染

URP的实用解决方案

衰减校正技术


  • 物理混合方案‌:
    1. hlsl
    2. half3 ApplyAttenuation(Light light, float3 positionWS)
    3. {
    4.     // 基础平方反比衰减
    5.     float dist = distance(light.position, positionWS);
    6.     float atten = 1.0 / (dist * dist);
    7.     // 范围平滑过渡
    8.     float fade = saturate(1.0 - (dist / light.range));
    9.     atten *= fade * fade;
    10.     // 聚光灯角度衰减
    11.     if(light.type == SPOT)
    12.     {
    13.         float3 toLight = normalize(light.position - positionWS);
    14.         float spotFactor = dot(toLight, light.direction);
    15.         atten *= smoothstep(light.outerAngle, light.innerAngle, spotFactor);
    16.     }
    17.     return saturate(atten * light.intensity);
    18. }
    复制代码
  • 移动端优化版‌:
    1. hlsl
    2. half3 SimpleAttenuation(Light light, float3 positionWS)
    3. {
    4.     // 使用预计算的衰减纹理
    5.     float dist = distance(light.position, positionWS);
    6.     float t = saturate(dist / light.range);
    7.     half atten = SAMPLE_TEXTURE2D(_LightAttenuationTex, sampler_LinearClamp, float2(t, 0.5)).r;
    8.     return atten * light.intensity;
    9. }
    复制代码
Unity编辑器中配置
  1. csharp
  2. // 光源组件属性设置
  3. Light light = gameObject.AddComponent<Light>();
  4. light.type = LightType.Point;
  5. light.range = 10.0f;// 控制衰减范围
  6. light.intensity = 1.0f;// 控制最大强度
  7. light.color = Color.white;
复制代码
结论与建议

核心结论


  • 经典兰伯特模型自身不包含距离衰减‌,仅有角度衰减
  • URP通过外部衰减系统提供完整衰减支持‌,使经验模型实用化
  • 现代实现已接近物理正确‌,但仍有可控的艺术化调整空间
开发实践建议


  • 性能敏感场景‌:
    1. hls
    2. // 使用简化距离衰减
    3. float atten = saturate(1.0 - distance/range);
    复制代码
  • 高品质渲染‌:
    1. hlsl
    2. // 物理精确衰减
    3. float atten = 1.0 / (distance * distance + 1e-5);
    复制代码
  • 风格化渲染‌:
    1. hlsl
    2. // 自定义衰减曲线
    3. float atten = exp(-_Falloff * distance);
    复制代码
在URP中,虽然经典兰伯特模型本身不具备完整的物理衰减特性,但通过引擎层的光照系统补偿,开发者可以轻松实现物理正确的衰减效果,同时保留艺术控制自由度。这种分层设计正是现代渲染管线的实用智慧体现。
【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

昨天 01:36

举报

懂技术并乐意极积无私分享的人越来越少。珍惜
您需要登录后才可以回帖 登录 | 立即注册