菲涅尔与边缘光:物理来源与可控写法

Unity 知识 · Shader 区域 · 常见效果 · 第 2 篇 更新于 2026-10-15

边缘光(rim light)是风格化渲染的招牌:角色轮廓发亮、全息投影边缘发光。它几乎总是菲涅尔的变体——但「物理的菲涅尔」和「好看的边缘光」是两回事,这篇把两者都写清楚。

目标

在表面朝向与视线接近垂直的区域(轮廓附近)叠加一层可控的亮边。

原理:边缘为什么天然更亮

菲涅尔效应(微表面 F 项,见 /notes/unity/lighting-2/):视线越贴近表面(掠射角),反射比例越高。数学核心就一项:

u = dot(N, V)   // 正对表面 = 1,掠射/边缘 = 0
F = F0 + (1 - F0)(1 - u)^5   // Schlick 近似

1 - dot(N, V) 在轮廓处趋近 1——这就是所有边缘光的种子。物理 PBR 里它已经包含在 F 项里,不需要也不应该再加一层 rim(会双重菲涅尔,边缘过曝)。风格化 rim 是故意偏离物理的「设计性打光」。

关键代码:可控 rim light

// 片元程序核心
float3 N = normalize(i.normalWS);
float3 V = normalize(_WorldSpaceCameraPos - i.positionWS);

// 1) 基础菲涅尔项
float fres = 1.0 - saturate(dot(N, V));
fres = pow(fres, _RimPower);          // 2~6 常见;越大边越细

// 2) 背光抑制:只保留「背对主光」的边缘(可选)
float ndl = dot(N, _WorldSpaceLightPos0.xyz);
float backMask = saturate(-ndl * 0.5 + 0.6);

// 3) 叠加
float3 rim = _RimColor.rgb * fres * _RimIntensity * backMask;
col.rgb += rim * albedo;              // 乘 albedo:暗部材质不吃 rim

参数直觉:_RimPower 控制边宽(物理 Schlick 的 5 次幂偏宽,风格化常用 2–4);_RimIntensity 配线性空间值;backMask 决定 rim 出现在背光侧(轮廓光感)还是整圈(全息感)。

物理版一行版(PBR 材质微调用):

float fresnel = F_Schlick(dot(N, V), 0.04); // 见 /notes/unity/lighting-3/

变体配方

  • 全息/能量罩:One One 加法混合(见 /notes/unity/structure-3/)+ 宽 rim(power 1.5–2)+ 扫描线 UV 叠加(见 /notes/unity/effects-4/)。
  • 描边感 rim:极窄 rim(power 6–10)+ 高亮色——比真描边 Pass 便宜得多,但只在朝向过渡处出现,硬剪影轮廓不如倒模描边,对比见 /notes/unity/effects-3/。
  • 卡通三带:fres > 0.9 ? rimColor : base 硬分段,卡通渲染常用。

常见坑

症状原因
法线贴图一开,rim 变成碎星星rim 用了扰动后的 N——细节法线让 N·V 逐像素乱跳。风格化 rim 常改用几何法线(插值未扰动的 N)
摄像机一动 rim 就游动菲涅尔本来就依赖 V,这是物理正确;要「钉在模型上」的描边请走几何描边,见 /notes/unity/effects-3/
rim 在暗场景里也刺眼没乘光照/遮罩;加 backMask 或乘 ndl 相关项
边缘颜色发灰不纯线性空间里 rim 与 albedo 相乘的顺序/强度问题,见 /notes/unity/performance-3/ 的色彩空间一节
双面模型(Cull Off)rim 内外都有背面 N 反向导致 dot(N,V) 为负被 saturate 成 1——双面时先 faceforward/按正面翻转 N

自检三问

  • PBR 材质上再加一层 rim,物理上重复在哪?
  • 为什么 rim 对法线贴图如此敏感,而漫反射不敏感?
  • 想要「摄像机移动也不游动」的轮廓线,菲涅尔能做到吗?为什么?