文章摘要
各向异性反光现象源于物体表面微观结构的定向排列(如头发的发丝、拉丝金属的划痕、光盘的同心圆槽等)。这种定向结构导致光线反射被引导至特定方向,形成动态高光条带或彩虹色带(如丝绸的柔光闪动、CD的彩虹效应)。其本质是表面微法线集中在某一方向,使反射光随观察角度变化而移动。简言之,各向异性反光是表面纹路对光线的定向"引流"结果。
1. 微观结构决定各向异性
这些物体(比如头发、缎面、拉丝金属、光盘、镲片、黑胶唱片)之所以会有各向异性的光照效果,都是因为它们表面的微观结构具有“定向性”,而不是像普通平滑物体那样杂乱无章。
举例说明:
- 头发:由成千上万根细小的发丝平行排列,每根发丝的表面其实像一根小圆柱,局部的镜面反射全都朝着发丝走向排列。
- 缎面/丝绸:纤维长丝都沿同一个方向编织,表面形成定向的纤维沟槽。
- 拉丝金属:表面处理时,机械工具反复沿一个方向抛光,留下大量微小平行划痕。
- CD、黑胶唱片、镲片:表面有大量同心圆环形的凹槽和细线,这些凹槽/线条都按照固定方向排列。
- 镲片:鼓镲上反复手工或机械拉出的圆形线纹;
- 黑胶唱片:刻录时刻出的沟槽沿着圆周展开。
2. 光的反射方式受定向影响
当光线照射到这些有方向性的微观结构时,光线的反射结果就不再是简单的漫反射或者均匀镜面反射,而是受定向沟槽/线条/表面微面引导,出现特殊的反光效果。
具体表现
- 发亮条带 & 色彩变幻:比如拉丝金属板、头发在阳光下会有明显的高光带,而且只在某个方向看得到,就是因为大量小划痕都把光反射到同一“主向”;换个角度,那条亮带就跟着移动。
- 彩虹光效(CD、黑胶唱片等):定向的凹槽还会干涉折射出彩虹效应。
- 缎面/丝绸的柔光闪动:纤维的定向排列让光线被沿一个方向“集中”反射,远远看去会有光带在移动。
3. 各向异性反射的本质——定向分布的微表面法线
用物理和计算机图形学术语来说,这些特殊现象本质上都来自于:
- 这些材料表面的微观法线分布不是各方向均匀的,而是集中在某一个方向的平面上(比如,都垂直于发丝,或垂直于沟槽走向)。
- 因此,光的反射“高光”不是面状(如普通塑料),而是被定向拉伸成条带、斑点或者特殊色带。
- 你的眼睛和光源在和物体表面的特定角度时,微表面结构就会把大部分反射光聚到某一个方向,这也是为什么你转动头发、金属或光盘时,亮带跟着动、色彩发生变化。
小结:「各向异性现象」的本质原因
- 微观结构定向分布(沟槽/线条/纤维):表面不是随机分布的微面,而是聚集在某个“主方向”。
- 决定光线反射特点:光线被引导,导致反光随观察方向或灯光方向发生变化。
- 视觉上的动态高光条带/色带/斑点:因定向微结构的选择性反射。
一句话总结:
这些物体的奇妙反光,是因为它们的表面“纹路”把光像排水沟那样定向引导了出来,形成随角度移动的各向异性亮斑或色带。
下面用通俗易懂的方式为你详细解释,计算机图形学里是怎么“模拟”这些现实中的各向异性光照现象的。
1. 标准各向同性光照模型回顾
大多数游戏和图形软件的默认光照模型(比如 Phong、Blinn-Phong),假设物体表面无特定方向,微表面法线均匀分布。渲染出来的高光总是圆斑状,不会随着表面划痕或纤维走向改变。
但现实中的头发、CD、拉丝金属等关键在于“主方向”,这些靠普通模型就做不出那种随角度变化的动态光带。
2. 各向异性光照模拟思路
A. 给顶点或像素增加“主方向”信息
- 多数各向异性模型会在每个顶点(或像素)除了表面法线以外,再记录一个“切线”或“主方向向量”——通常叫"Tangent"(走向)、“Bitangent”(副走向)。
- 这样,渲染时不仅知道表面“正面朝哪”,还知道“纹理/丝线”朝哪。
例如渲染头发时,每根头发纤维自身就有一个主方向;渲染金属拉丝时,表面每点都标记划痕方向。
B. 改写高光和漫反射公式
普通材质的高光只考虑法线和光向:
- Ispecular=(V⋅R)sI_{specular} = (V \cdot R)^sIspecular=(V⋅R)s
各向异性模型,比如 Kajiya-Kay、Ashikhmin-Shirley、Heidrich-Seidel 等,会改用带主方向参数的公式,或直接查表(如你文章里提到的查二维纹理):
- 利用切线 T⃗\vec{T}T 修正高光的拉伸方向。
- 模型计算时,引入 (L⋅T)(L \cdot T)(L⋅T)、(V⋅T)(V \cdot T)(V⋅T) 等参数,让高光沿主方向变宽或拉伸成条带。
C. 用二维查找表(Lookup Table)或贴图加速
- 复杂的各向异性公式往往不直接在着色器里一条条算,而是提前做成“二维光照查找表”。
- 顶点(或像素)用它自己的切线等信息查表拿高光值。比如,“s”坐标代表光线和切线夹角,“t”坐标代表视线和切线夹角,查二维贴图得结果。
- 这样能高效、快速地在GPU上做出高质量的各向异性效果。
D. Shader代码实现简述
在GLSL/HLSL等着色器中,实际流程大致如下:
- 顶点/像素采样得到:法线 N⃗\vec{N}N,切线 T⃗\vec{T}T,光线方向 L⃗\vec{L}L,视线方向 V⃗\vec{V}V。
- 计算 L⋅TL \cdot TL⋅T、V⋅TV \cdot TV⋅T 等点积。
- 应用公式(如 Heidrich-Seidel、Kajiya-Kay 或查表法)得到N⋅LN \cdot LN⋅L(漫反射系数)、V⋅RV \cdot RV⋅R(高光系数)。
- 生成纹理坐标用于查找表贴图(如代码里:tex2D(lightLUT,texcoord))。
- 最后混合材质颜色和灯光,输出片元颜色。
代码片段(例)
vec3 T = Tangent; // 切线
float s = dot(L, T);
float t = dot(V, T);
float NdL = sqrt(1.0 - s*s);
float VdR = sqrt(1.0 - s*s) * sqrt(1.0 - t*t) - (s*t);
float diffuse = NdL;
float specular = pow(VdR, shininess);
// 结合查表方式替代公式也可以
3. 图形学中的实际应用案例
- 头发Shader(Kajiya-Kay模型):适合细长物体如头发、羊毛,能做出真实的长型高光。
- 拉丝金属Shader(Heidrich-Seidel模型):适合金属表面,能做出随着观察角度变化的拉丝光带。
- CD/黑胶Shader(周期性切线+光栅效果):利用同心圆方向和干涉公式,做出动态彩虹高光。
- 丝绸Shader(Ashikhmin-Shirley模型):高光、柔光会变成带状但更分散。
4. 其他纹理方法补充
- 可以用法线贴图(Normal Map)存储和微调主方向信息,让高光条带在表面复杂变化。
- 也可用切线空间贴图,增加物体表面定向纹理效果。
5. 总结
模拟各向异性光照的核心就是在渲染过程中,不仅仅计算法线反射,还把表面主方向的信息也算进去,让高光和反光能够根据纹路/切线的真实走向动态变化。
现实模拟的效果,就是你在游戏、美术或渲染工具里能看到头发、金属、丝绸表面的光带随着你的视角和灯光的变化而“流动”和“拉伸”,尽可能逼真还原了生活中的美丽视觉!
这里以拉丝金属/丝绸这种典型的各向异性材质为例,给你详细讲解常用各向异性高光Shader的实现思路和代码:
1. 各向异性高光(Heidrich-Seidel模型)理论
最常见的方式之一是Heidrich-Seidel各向异性高光模型,它的实际高光公式是:
Specular∝[(H⃗⋅T⃗)2]k\text{Specular} \propto \left[ (\vec{H} \cdot \vec{T})^2 \right]^kSpecular∝[(H⋅T)2]k
- 这里,H⃗\vec{H}H 是半程向量(光方向与视线方向的归一化和)。
- T⃗\vec{T}T 是切线方向(拉丝/纤维方向)。
- kkk 用于控制高光的锐度(越大越尖锐)。
也可以用H⃗\vec{H}H和表面法线N⃗\vec{N}N的组合来进一步精细调整。
2. 简单各向异性高光Shader代码实现(GLSL/HLSL通用思想)
假设你在片段着色器中已有以下输入:
vec3 normal
表面法线(世界空间或切线空间)vec3 tangent
切线方向(主方向,已归一化)vec3 lightDir
入射光单位向量(已归一化,从表面指向光源)vec3 viewDir
视线方向单位向量(已归一化,从表面指向相机)
代码示例(GLSL,供主流引擎稍适配即可)
// 必须保证所有向量归一化!!!
vec3 N = normalize(normal);
vec3 T = normalize(tangent);
// 计算环绕主方向、法线的正交基(可选)
vec3 B = normalize(cross(N, T));
vec3 L = normalize(lightDir);
vec3 V = normalize(viewDir);
vec3 H = normalize(L + V); // 半程向量
// 各向异性高光分量
float tDotH = dot(T, H);
float shine = 40.0; // 高光锐度可调(小更宽,大更尖锐)
float spec = pow(tDotH * tDotH, shine); // 取平方让它更集中在某一方向
// ---- 漫反射一般处理,不变
float NdotL = max(dot(N, L), 0.0);
vec3 diffuse = baseColor * NdotL;
// ---- 最终颜色叠加
vec3 finalColor = diffuse + spec * vec3(1.0, 1.0, 1.0) * specularColor; // 高光白色
gl_FragColor = vec4(finalColor, 1.0);
说明与拓展
- 你可动态从贴图或顶点获得
tangent
方向。 shine
参数越高,高光带越细锐;可调节模拟不同金属。- 高光色(specularColor)可变(比如金色、银色等)。
- 可以再加上各向异性散射/漫反射项做出丝绸更柔和的亮条。
3. 进阶:查找表实现(更真实、可艺术定制)
可以把不同光向和主方向的高光亮度提前做成一张二维纹理贴图(Lookup Table,LUT),在shader里用(L⋅N)(L\cdot N)(L⋅N)和(H⋅T)(H\cdot T)(H⋅T)查表,得到更复杂、艺术化的高光分布。例如:
float coordS = dot(T, H); // 或 dot(T, L) 视需求
float coordT = dot(N, L); // 漫反射分布
vec2 lutCoord = vec2(coordS * 0.5 + 0.5, coordT * 0.5 + 0.5);
float anisotropicSpec = texture2D(lightLUT, lutCoord).r; // 查表获取亮度
...
vec3 finalColor = diffuse + anisotropicSpec * specularColor;
这样美术可以直接画出你想要的光斑形状,查表就能表现复杂材质变化!
4. 典型场景推荐参数
材质 | shine(锐度) | 高光颜色 | 说明 |
---|---|---|---|
拉丝金属 | 40~80 | 灰色/银白 | 明显条带 |
金属车漆 | 120~300 | 彩色/强白 | 高光窄且尖锐 |
丝绸/绸带 | 5~15 | 漫反射主导,略带高光白色 | 柔亮带,高光模糊 |
头发 | 10~30 | 白色+主色mixin | 柔性亮条条 |
5. 参考资料与进一步阅读
- SIGGRAPH经典论文:“Anisotropic Highlights Using A Tangent Vector Texture Map”, Heidrich & Seidel, 1998
- Unity3D/Unreal官方文档搜索“Anisotropic Hair/Metal Shader”
- glTF 2.0 、Unreal等现代引擎都支持各向异性参数