【技术美术】各向异性光照
【技术美术】各项异性光照
各项同性与各项异性
一颗光滑的球被灯光照到,默认高光函数会形成一个圆斑,因为他用于计算高光的属性从任意表面向四周都是均匀相同的变化率(光滑的球面法线)。而在各向异性的表面上,则有可能形成条纹,因为其用于计算光照的属性,在条纹的垂直方向和水平方向上变化率是不同的(例如切线)。简要来说:
- 各项同性:从物体表面一点往四周出发,其属性变化率都是一致的。
- 各项异性:从物体表面一点往四周出发,其属性变化率根据具体方向会有明显差异。
所以实现各项异性光照的关键点在于,使用各方向变化率不同的属性计算。要获取各项异性的属性,方法有两种:
- 从贴图上下手,绘制表面属性时就故意使其各方向变化率不一。
- 从算法上下手,改用其他天生就各向异性的属性并设计专门的算法实现效果。
第一点对美术要求高,而且因为贴图分辨率的限制,效果不一定好,例如一根一根的头发丝非常多,不可能每根都在大小有限的贴图上表现的非常清楚。所以一般会采用第二种方法实现各向异性,也因此我们需要学习新的光照算法。
Kajiya-Kay 光照模型
Kajiya 是一种模拟头发高光的光照模型(是模型意味着它很全面,不仅是函数计算那么简单),其由 Blinn-Phong 改造而来,特点是不使用法线,而是使用天然就各项异性的切线代为计算光照数据。
高光计算

例如上图这个圆柱的高光(其实模拟的就是发丝),如果使用正常的 Blinn-Phong,其一定会形成一道顺着柱子的条纹高光,因为其上下的法线变化更缓,左右则更急,所以上下将会显示很多的高光。但我们希望实现的高光效果,则与其相反,希望其能沿着柱子显示,而不是上下。
如何实现?寻找一下圆柱周围一圈的相同点就会发现,其副切线都是一致的。不过副切线恰巧与法线垂直,所以基于法线的光照计算方法 \(\cos(\theta)\) (\(\theta\) 是 normal 和 h 的夹角)就不能用了,应改为 \(\sin(\theta')\) (\(\theta'\) 是 bitangent 和 h 的夹角),不过 \(\sin\) 不利于计算,但可利用三角函数关系式转换为 \(\sqrt{1 - \cos^2(\theta')}\),这样就可以用点乘实现,即上图中的公式结果。
将其替换到 Blinn-Phong(PBR 版) 的基数部分后,我们便可以写出kk镜射光的具体代码了:
float blinnCardinal = saturate(sqrt(1 - pow(dot(h, b), 2)));
float kkSpecularTerm = pow(blinnCardinal, max(2 / roughness2 - 2, 0.0001)) * (1 / roughness2);
- b:副切线。
高光扰动
上述的 kk 实现后可以在球面上形成天使环的效果,但如果我们希望球面能表现头发效果的话,就必须实现一丝一丝的效果。方法很简单,同样是基于各向异性,让代表每根头发处的表面表现出不同的效果,从而营造出由密集的不同物体构成的感觉,即我们要打破天使环光滑的光斑效果。
此处我们就必须利用贴图来实现各向异性了,因为顶点无法承载如此高频的信息。我们可以搞一张带有密集条纹的噪波图来扰动用于计算的切线,扰动方式也很简单,直接用与其一起计算的法线即可,本质上就是调整了他们的光照角度。
float anisotropy = tex2D(_AnisotropyMap, uv).r;
float disturbance = (anisotropy - 0.5) * _DisturbanceIntensity + _DisturbanceOffset;
float3 b = normalize(bitangent + normal * disturbance);
- _AnisotropyMap:各向异性噪波图,可用做高光扰动基数。
- _DisturbanceIntensity:扰动的强度。
- _DisturbanceOffset:统一向一个方向扰动,可实现高光偏移效果。
双重高光

这里还有一个注意点,就是双重高光,就是计算两次参数不同的高光,一个做前景一个做背景,叠起来,这样会更符合现实的头发效果。当然这种双重光照的渲染技巧,不仅是在这适用,很多其他场合也是可用的。
金属拉丝
金属拉丝的效果类似碟片、平底锅的反光,一个盘状物体上可以明显看到对称的三角高光。这些东西的共同点是,上面都有一圈一圈的小纹路,这也正是这些物体形成各项异性高光的原因。本质上这和一根一根头发丝组成的各项异性高光是一样的。
所以需要考虑有什么属性,在这些一圈一圈的纹路上水平和垂直方向的变化率不同。法线肯定是不行的,其在全方向都一致,故只能实现各向同性高光。但切线可以(不过也依赖圆盘的做法),如果是一些uv成扇形展开的圆盘,其切线会顺着圆圈纹路的方向,所以从纹路方向看,他们是缓慢变化角度的,而从垂直角度来看,则是始终不变的。因此对于这类圆盘,我们只需要利用切线计算光照就行。
那具体如何计算?还是用 kk 模型,虽然 kk 模型默认是针对头发丝的,但得力于 Blinn 的超强兼容性,我们依然可以通过变换基数的方式,将其改造为金属拉丝高光。
float blinnCardinal = saturate(sqrt(1 - pow(dot(h, t), 2)));
这和 kk 基本一样,仅仅是把副切线(b)替换成了切线(t)而已。
各向异性反照率
上面仅讲到了各向异性表面高光的实现,但实际上其漫射等其他光照也会受影响。不过处理方法非常简单,我们可以直接简单的扰动物体的反照率即可。
albedo = albedo * lerp(1, anisotropy, _AlbedoDisturbance);
anisotropy:上文扰动高光时用过的各向异性系数。_AlbedoDisturbance:反照率扰动强度。
我觉得这实际上也非常科学,模拟的就是头发丝时有时无的状态嘛。

浙公网安备 33010602011771号