上一节的汇率表只管"同侧半球"的周转,本节把边界拆掉:光跨过界面时怎么分成、钻进介质后怎么衰减、以及在浅层物质里进出一个"不是同一个点"的出口时,表面模型为什么不够用。这三件事对应三个名字:斯涅尔定律、菲涅耳方程、次表面散射。它们解释的视觉现象你天天见——玻璃杯壁的亮线、玻璃边缘的绿、皮肤下透出的红——本节让你能逐个算出来。
折射的方向规则一条就够:入射角正弦与折射角正弦之比,等于两侧折射率之比的倒数。写成代码要小心方向翻折与内外的判别——这里给出经得起实现考验的完整版本:
function refract(d, n, eta): # eta = n_in / n_out,n 朝向入射一侧 cos_i = -dot(d, n) k = 1 - eta·eta·(1 - cos_i·cos_i) if k < 0: return 无解 # 全内反射:只能反射,没有折射方向 return eta·d + (eta·cos_i - sqrt(k))·n
k 小于零的分支值得单独驻足:光从光密介质射向光疏介质(水中射向空气)且入射角超过临界角时,折射方向在数学上不存在——能量全部转给反射,这就是全内反射。手算一个数:水的折射率 1.33、空气 1.0,临界角正弦等于 1.0 除以 1.33,临界角约 48.8 度。潜水员在水面以下回望水面,超过这个角度的水面像一面银镜,就是这个原理。玻璃到空气的临界角约 41.8 度,所以玻璃工艺品内部总有几条"亮到发白"的界面——光在里面多次全反射才溜出来。第 3 章命中记录里的面向标志在这里正式上岗:它决定折射率比值的分子分母怎么放、法线朝哪边翻。
方向定了,能量怎么在反射与透射之间分账?答案是菲涅耳方程。完整形式对偏振敏感,渲染里常用近似替代——介电质的施利克近似只依赖 F0(正入射反射率):
F(cosθ) = F0 + (1 − F0) · (1 − cosθ)^5 水 F0 = 0.02 玻璃 F0 = 0.04 钻石 F0 = 0.17 金属 F0 = 0.5~0.9
这条五次方曲线的形状要背下来:正入射附近几乎平贴 F0(水面正看只有百分之二的反射),掠射角处冲向 1(水面远看如镜)。它一举解释了几个日常视觉:为什么雨后的路面在低角度看去泛着天空的亮膜;为什么球体边缘总有一圈亮边(边缘处视线与法线夹角大,菲涅耳推高反射);为什么玻璃杯的轮廓比正面清楚——你看到的"杯壁"主要是掠射角的高菲涅耳反射。金属不走施利克近似,其 F0 本身带色(金的黄、铜的橙红来自复折射率的实部与虚部共同作用),反射率整体高且随角度变化平缓——这就是 5.1 说"金属高光带本色"的能量学出处。
光穿进透明介质,每走一段就被吸收一份。比尔-朗伯定律说透射能量按距离指数衰减,吸收系数按波长取不同值——玻璃对红光的吸收略强于绿光,穿得越厚绿色相对越多,这就是厚玻璃边缘发绿的出处:边缘处光路长,吸收差异被放大。实现上只需在折射路径里按传播距离乘一个指数衰减因子,零成本换真实感。
真正的难关是次表面散射:皮肤、蜡、大理石、牛奶这类材质,光进入表面后在内部散射多次,再从旁边的另一个点穿出。它有两个致命特征让表面 BSDF 失效:其一,入射点与出射点分离——能量越过了几何位置,渲染方程"同一点进出"的前提塌了;其二,出射分布与入射方向几乎无关(信息被打散),BRDF 的双方向结构装不下。扩展方案叫 BSSRDF:入射方向、出射方向之外,再加出入射点、出射点两个自由度,散射 profile 描述"从入射点进入的光有多少、以什么分布从旁边各距离的出射点冒出来"。渲染时沿表面按 profile 采样一组出射点分别求值——这是离线皮肤渲染的标准做法。实时管线则普遍降级:屏幕空间模糊或预积分查找表,用"扩散近似"骗过人眼。

把本节三件工具组装成一个完整案例:渲染一杯清水。第一段,水面:入射角 30 度的视线,施利克近似给出反射约 0.025——水面主体靠折射见底,反射只贡献微弱的天光膜。第二段,水中路径:折射角按 1.0 比 1.33 折算约 22 度,光在杯内走 15 厘米,按清水的吸收系数衰减后能量所剩无几且染色轻微——所以清水几乎无色。第三段,杯底与侧壁:侧壁处光从玻璃内侧射向空气,入射角超过 41.8 度临界角的区域发生全内反射,杯壁局部出现亮银色条带——这也是玻璃杯轮廓感的最大来源。三段账合起来,杯子的"通透感"不用任何特技就成立了。变式一:把水换成折射率 1.47 的甘油,临界角推到约 42.9 度、折射弯折更狠,杯后景物扭曲加剧。变式二:把清水换成掺奶的水(吸收系数暴涨加散射 profile 出现),第三段升级成次表面散射——牛奶的白正是千万次内部散射把所有方向打匀的结果。两次变式只动参数不动结构,这正是把物理写进材质系统的回报。
把本节公式变成工作台上的一页小抄,先从折射率开始:
| 材质 | 折射率 | F0 约值 | 临界角(内射向空气) |
|---|---|---|---|
| 水 | 1.33 | 0.02 | 48.8 度 |
| 玻璃 | 1.5 | 0.04 | 41.8 度 |
| 蓝宝石 | 1.77 | 0.08 | 34.4 度 |
| 钻石 | 2.42 | 0.17 | 24.4 度 |
钻石火彩的物理出处就在表末行:临界角小,全内反射在宝石内部反复发生的次数多,色散把白光拆成彩带的机会随之暴涨——不需要任何特效,一个折射率自己就会表演。
另一页小抄是薄壁与实体的取舍。叶子、布料、泡泡这类"两层表面夹一层空气"的几何,完整实体折射要求建模出厚度,双面各算一次折射还常常穿透失败;工程惯例是薄壁模式:单层表面、透射不改道直接穿过去,能量只用菲涅耳分账。代价是背景透过薄壁不产生弯折。判断这条近似什么时候穿帮,盯着观感敏感度就行:观众对水面弯折极其敏感、对叶片透射几乎无感,所以水面永远是实体折射、叶子永远可以薄壁。把近似用在对观感不敏感的地方,是材质工程与材质物理的分界线。
小抄之外存一条排错经验:玻璃渲染发灰发黑的多数根源不在折射而在能量记账——每次界面穿越按菲涅耳分成、吸收按厚度指数扣账,两层玻璃四次界面之后能量只剩零点几;若实现再把全内反射的能量误丢(而不是全额转给反射股),玻璃会黑得像墨镜。排障口诀:先查全内反射分支有没有把能量转干净,再查吸收系数是不是拿普通介质填成了烟雾浓度。能量账每一步都要平——本章开头立过铁律,玻璃是它最高频的审计现场。变式练习:给"玻璃发灰"的三个嫌疑人(全内反射丢能量、吸收系数过大、分成漏算)各设一个开关单独排查,三条症状一一对应之后,这条排障链就长在你身上了。
方向与能量的机理都齐了,还差"材质处处不同"这一层真实感——下一节给 BSDF 的参数喂上纹理与过程化生成。