【从UnityURP开始探索游戏渲染】专栏-直达
光照衰减的基本原理
在物理正确的光照模型中,衰减需要遵循两个基本定律:
- 平方反比定律:光强与距离平方成反比 (I ∝ 1/r²)
- 余弦定律:表面接收的光强与入射角余弦成正比 (I ∝ cosθ)
经典兰伯特模型的衰减处理
标准兰伯特公式
KaTeX parse error: Expected 'EOF', got '漫' at position 1: 漫̲反射 = 表面颜色 * 表面反…
衰减实现分析
- 角度衰减:
- ✅ 正确实现余弦定律
- 通过 N·L 点积计算入射角衰减
- 符合物理规律:光线入射角越大,光照强度越小
- 距离衰减:
- ⚠️ 完全缺失距离衰减计算
- 公式中没有包含光源距离®相关项
- 光强不会随距离增加而减弱
- 导致物理不准确性
Unity URP中的实现方案
距离衰减补偿机制
URP通过额外计算衰减因子来弥补兰伯特的不足:
hlsl
// URP光源获取函数 (Lighting.hlsl)
Light GetMainLight()
{
Light light;
light.direction = _MainLightPosition.xyz;
// 距离衰减计算
float distance = length(_WorldSpaceCameraPos - positionWS);
light.distanceAttenuation = 1.0 / max(distance * distance, 0.01);
light.color = _MainLightColor.rgb;
return light;
}
// 主光源 漫反射计算
lightingData.mainLightColor += CalculateBlinnPhong(mainLight, inputData, surfaceData);
half3 CalculateBlinnPhong(Light light, InputData inputData, SurfaceData surfaceData)
{
// 这里通过颜色计算了光线衰减
half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
half3 lightDiffuseColor = LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS);
half3 lightSpecularColor = half3(0,0,0);
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
half smoothness = exp2(10 * surfaceData.smoothness + 1);
lightSpecularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, half4(surfaceData.specular, 1), smoothness);
#endif
#if _ALPHAPREMULTIPLY_ON
return lightDiffuseColor * surfaceData.albedo * surfaceData.alpha + lightSpecularColor;
#else
return lightDiffuseColor * surfaceData.albedo + lightSpecularColor;
#endif
}
URP衰减系统组成
光源类型处理:
- 平行光:无距离衰减 (1.0)
- 点光源:平方反比衰减 (1/r²)
- 聚光灯:角度衰减 × 距离衰减
优化策略:
- 使用预计算的衰减纹理
- 最大距离截断(light.range)
- 平滑过渡边缘处理
光源类型
为什么经典兰伯特缺乏距离衰减
- 历史设计局限:
- 早期计算机图形学简化模型
- 源自环境固定的CAD渲染需求
- 仅考虑局部表面光照
- 数学简化考量:
- 减少每像素计算量
- 避免昂贵的距离计算
- 保持公式简洁性
- 艺术导向设计:
- 允许美术师手动控制光照范围
- 避免距离导致的过度变暗
- 更适合风格化渲染
URP的实用解决方案
衰减校正技术
-
物理混合方案:
hlsl half3 ApplyAttenuation(Light light, float3 positionWS) { // 基础平方反比衰减 float dist = distance(light.position, positionWS); float atten = 1.0 / (dist * dist); // 范围平滑过渡 float fade = saturate(1.0 - (dist / light.range)); atten *= fade * fade; // 聚光灯角度衰减 if(light.type == SPOT) { float3 toLight = normalize(light.position - positionWS); float spotFactor = dot(toLight, light.direction); atten *= smoothstep(light.outerAngle, light.innerAngle, spotFactor); } return saturate(atten * light.intensity); }
-
移动端优化版:
hlsl half3 SimpleAttenuation(Light light, float3 positionWS) { // 使用预计算的衰减纹理 float dist = distance(light.position, positionWS); float t = saturate(dist / light.range); half atten = SAMPLE_TEXTURE2D(_LightAttenuationTex, sampler_LinearClamp, float2(t, 0.5)).r; return atten * light.intensity; }
Unity编辑器中配置
csharp
// 光源组件属性设置
Light light = gameObject.AddComponent<Light>();
light.type = LightType.Point;
light.range = 10.0f;// 控制衰减范围
light.intensity = 1.0f;// 控制最大强度
light.color = Color.white;
结论与建议
核心结论
- 经典兰伯特模型自身不包含距离衰减,仅有角度衰减
- URP通过外部衰减系统提供完整衰减支持,使经验模型实用化
- 现代实现已接近物理正确,但仍有可控的艺术化调整空间
开发实践建议
-
性能敏感场景:
hls // 使用简化距离衰减 float atten = saturate(1.0 - distance/range);
-
高品质渲染:
hlsl // 物理精确衰减 float atten = 1.0 / (distance * distance + 1e-5);
-
风格化渲染:
hlsl // 自定义衰减曲线 float atten = exp(-_Falloff * distance);
在URP中,虽然经典兰伯特模型本身不具备完整的物理衰减特性,但通过引擎层的光照系统补偿,开发者可以轻松实现物理正确的衰减效果,同时保留艺术控制自由度。这种分层设计正是现代渲染管线的实用智慧体现。
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)
點(diǎn)擊查看更多內(nèi)容
為 TA 點(diǎn)贊
評(píng)論
評(píng)論
共同學(xué)習(xí),寫(xiě)下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章
正在加載中
感謝您的支持,我會(huì)繼續(xù)努力的~
掃碼打賞,你說(shuō)多少就多少
贊賞金額會(huì)直接到老師賬戶(hù)
支付方式
打開(kāi)微信掃一掃,即可進(jìn)行掃碼打賞哦
今天注冊(cè)有機(jī)會(huì)得
100積分直接送
付費(fèi)專(zhuān)欄免費(fèi)學(xué)
大額優(yōu)惠券免費(fèi)領(lǐng)