前置知识:Lambert 漫反射、Phong 高光、反射定律。用于头发/毛发/拉丝这类纤维状表面的经验着色模型,见 各向异性各向异性前置知识:反射定律、微表面 BRDF(NDF、粗糙度、半程向量),见 [[Physically Based Rendering]]。
各向异性的原理
保持灯光和相机不动,只旋转物体:
- 普通金属不管转到哪个方向,高光形状都差不多,这是各向同性;
- 拉丝金属转动后,高光会跟着拉丝方向变化,这是各向异性。
区别不在表面是否粗糙,而在微观结构有没有统一方向。
同一个表面有两个粗糙度
微表面理论中的粗糙度,描述的是微表面法线的分散程度。法线越集中,反射光越汇聚,高光越锐利;法线越发散,反射光越散开,高光越模糊。
随机凹凸表面的微表面法线在各个方位上均匀分布。物体绕法线旋转后,法线分布没有变化,所以只需要一个粗糙度描述——这就是各向同性。
拉丝金属、缎面和头发的微观结构带有方向。沿沟槽或纤维方向,表面变化较小;垂直该方向,表面起伏更明显。于是切平面中的两个方向具有不同的法线分布,也就需要两个粗糙度描述——这就是各向异性。
image.png
高光为什么垂直于沟槽拉长
把一条沟槽看成细圆柱。平行光照到圆柱后,每个命中点的反射方向由该点法线决定。
- 沿圆柱轴线取一排点,。
问题:纤维没有唯一法线
Blinn–Phong 高光以表面法线 n 为核心:靠半程向量与法线的对齐 n⋅h 来定强度。
但一根头发是一条无限细的圆柱纤维,它没有唯一法线——横截面是个圆,圆周上每一点的法线都朝不同方向,构成一整圈垂直于切线的法线。
圆柱表面上,任意一点的切平面里其实有两个方向的切线:
- 沿轴切线——顺着纤维走向(发根到发梢)的那条;
- 周向切线——绕着横截面圆走的那条(沿圆周)。
Kajiya–Kay 的核心思路:用切线 t(沿轴切线)替代法线 n 来做着色。 下面推它的高光项——Unity 里用 Kajiya–Kay 主要就是用这一项,漫反射走标准 Lambert / PBR。

约定:t 为单位切线,l 指向光源,v 指向眼睛,都是单位向量。记 θl 为 t 与 l 的夹角,θv 为 t 与 v 的夹角。

高光项:从 Blinn–Phong 出发
先回顾 Blinn–Phong 高光。它以半程向量 h=l+v 为核心:
Ψspec=ks(n⋅h)p,h=l+v
下面分三步,把它从「平面」搬到「发丝」。
第一步:Blinn–Phong 的规则——h 和法线 n 同向时最亮。
镜面反射定律:
光要想正好反射进你眼睛,入射光和视线必须关于法线对称。而"l 和 v 关于法线对称",恰好就等于"它俩的正中间 h 和法线 n 同向"。
所以:h 和 n 同向 → 光正好反射进眼睛 → 高光最亮。 这就是 Blinn–Phong 的核心。h 偏离 n 越多,高光越暗。

第二步:发丝的法线全都垂直于发丝方向 t。
发丝是个圆柱,表面任意一点的法线都从中轴沿半径朝外戳出,而半径方向和中轴方向(发丝方向 t)天然成直角。所以发丝一圈的法线,每一根都垂直于 t,合起来就是「垂直于 t 的一整圈方向」——像一把圆柱刷子,刷毛全朝侧面戳,没有一根顺着发丝。
第三步:两步合起来——h 垂直于 t 时最亮。
第一步要「h 和某条法线同向」才最亮;第二步说发丝的法线全都垂直于 t。所以 h 要和其中某条法线同向,它自己也必须垂直于 t。只要 h 垂直于 t,这一圈里就一定有一条法线正好和它同向 → 完美镜面 → 最亮;只要 h 有一点沿着 t 的分量(不垂直),就对不上任何法线 → 变暗。
发丝的法线只朝侧面(垂直 t),所以负责对准反射的 h 也只有垂直 t 时,才能对上一根、把光反进眼睛。

写成公式。
从 Blinn–Phong 公式: Ψspec=ks(n⋅h)p
- 因为:n⋅h=cos(n,h)
- 所以: Ψspec=kscosp(n,h)
换到发丝(把 cos 换成 sin):
- 因为:发丝的法线全都垂直于 t,高光最亮的条件是 h⊥t;
- 因为:cos(t,h) 在 h∥t 时 =1、h⊥t 时 =0,方向正好反了;而 sin(t,h) 在 h⊥t 时 =1,正合适;
- 所以:把对齐度量 cos(n,h) 换成 sin(t,h),得 Ψspec=kssinp(t,h)。
换成点积(shader 好算):
- 因为:单位向量下 sin2+cos2=1,即 sin(t,h)=1−cos2(t,h),又 cos(t,h)=t⋅h;
- 所以:sin(t,h)=1−(t⋅h)2。
结论:
Ψspecular=kssinp(t,h)=ks(1−(t⋅h)2)p
float TdotH = dot(T, H);
float sinTH = sqrt(1.0 - TdotH * TdotH);
float spec = pow(sinTH, exponent);
几何意义:偏移高光。 把峰值条件 h⊥t 展开:
h⊥t⟺(l+v)⋅t=0⟺cosθl+cosθv=0⟺θl+θv=π
高光最亮的位置不在镜像对称处,而是随光照沿纤维滑动——这正是头发那条标志性高光带的来源。
小结与用法
高光只依赖光照/视线方向对切线 t 的夹角,与绕纤维的方位无关——这是它对圆柱的核心简化(忽略方位依赖),也是便宜好用的原因。
Unity 里用 Kajiya–Kay 就用这一项做各向异性高光,漫反射另走标准 Lambert / PBR(头发上再叠 wrapped diffuse 模拟半透)。
关于符号的一个坑:不少资料把高光写成 (cosθlcosθv+sinθlsinθv)p=cosp(θl−θv)(加号)。那是把切线直接当法线套进 Phong 反射公式 r=2(t⋅l)t−l 得到的结果,峰值在 θl=θv,与上面半程向量写法的偏移高光(峰值在 θl+θv=π)并不一致。看到 ± 号打架时,先确认视线向量的朝向约定。上面用的 sinp(t,h) 没有这个歧义,推荐用它。
局限
- 纯经验模型,不含能量守恒、无 Fresnel、不分辨发丝内部的透射与多次散射。想要真实头发(尤其浅色发的通透感、次级高光带 R/TT/TRT),要上 Marschner 模型。
- 需要沿发丝走向提供切线 t;发根到发梢的切线要平滑,否则高光会断。
- 常配合「切线偏移」(shift tangent)做出主/次两条高光带,见 Scheuermann 的实时头发方案。