弱渣从头开始学图形学,大作业基本功能都做完了准备搞事情,想写个基于物理的着色器。只好从头开始学习BRDF。
PBS这套流程最近几年非常流行,主要是为了帮助美术减少材质的错误的。我就是主要读了几篇论文,看了看别人的代码。
Torrance-Sparrow
看的很多PRS的文献都会提到Torrance-Sparrow模型,1967年的一篇文章Theory for Off-Specular Reflection From Roughened Surfaces. 讲的是他们提出了一个粗糙表面的模型来解释材料受到光照时的表现。文中提出。如最上面那张图,Diffuse项之和入射角度
有关,Specular实际上来自于物体表面有一定比率的微表面发现是入射与出射光的平分线
(后来从Blinn-Phong中借鉴了下改叫H)。
当然论文了计算了一大堆,最后得到这样一个公式:
这个公式的Specular部分其实是三项乘起来的,分别叫,
,
。
D是Distribution的简称,用来计算H的分布,也就是有多少部分的微表面贡献了反射?用了一个高斯分布的估计,其中bc是常数。微表面分布似乎在更早一篇Beckmann(1963)的文章中提过用高斯方法估计,也是D这项的经典实现。
F是Fresnel的简称,菲涅尔现象就是入射角大时反射强烈的一种效果。
G是Geometrical Attenuation,就是物体表面粗糙时会发生的自遮挡和阴影。比如下面这个图解释的:
之后文中推导了一个计算G的公式:
文中计算Specular的那个函数就叫做BRDF(Bidirectional Reflection Distribution Function),后来Torrance又有一篇发表于1982的A Reflectance Model for Computer Graphics,和Cook合作实现了自己的Torrance-Sparrow模型,称为Cook-Torrance。这篇文章就成为后来大家改D F G的来源。看D F G这三项怎么能更精确或者计算更快速。以及Diffuse那项大家的实现也有区别。
Disney
第二篇论文来自于Disney,Physically-Based Shading at Disney。是SIGGRAPH2012的PBS课程中的一节。
他们的贡献在于自己写了一个软件比较了一堆的BRDF,提出了他们自己的算法并且考虑了如何给美术使用。
他们用了一套叫做MERL 100 BRDFs的材质库,以和
为坐标轴表示效果。h还是Blinn-Phong里的h,
就是v和h的夹角。
然后就在测试各种模型!附录里列了三十多个历史上的BRDF模型,比如对于Diffuse项有Lambert, Oren-Nayar, Hanrahan-Krueger. 然后他们自己搞了个巨复杂的。
Distribution项有传统的Beckmann, Blinn-Phong, Gaussian,但是高光尾部都表现不好。GGX也有一定缺陷。所以还参考了Low,Bagher和Ngan,最后他们选择了拓展Trowbridge and Reitz(TR等同于GGX),叫做GTR(General-Trowbridge-Reitz)。.
Fresnel就用的Schlick的模型.
Geometrical Attenuation比较了Ward(1992), Kurt(2010), Ashikhmin-Shirley(2001), Walter(2007,即GGX)。最后选择了一个稍微修改版本的GGX,考虑了艺术家的反馈,把roughness从[0,1]映射到[0.5,1].
但是论文里好像没有提Sheen怎么实现的?
Unreal Engine 4
Epic在SIGGRAPH2013的PBS课程中的分享,讲UE4的PBS是怎么设计的。和Disney不一样他们是RealTime,所以实现上hack了不少东西非常有意思。
比如说Diffuse项,他们说搞这么复杂差别不大啊还是效率优先,就。
Distribution项用GGX的效率还不错,直接借鉴的Disney的做法,。其中
。和Disney那个对比一下,注意
,其实是和Disney文章附录B里的推导一致的。
Fresnel项就是用的Schlick模型,但是为了提高计算效率做了个球形高斯分布,参考这篇:Spherical Gaussian approximation for Blinn-Phong, Phong and Fresnel。这个很神奇啊,最后写成
。当然那篇文章里说这个目的在于减少ALU指令数,对现代GPU不一定有用,但对XBOX360和PS3之类还是有效的。跪。
G项仍然用的Schlick,不过使用了,并且和Disney一样做了一个
.
除了标准的这部分,UE4里还提到了他们怎么处理IBL(Image-Based Lighting),也就是环境贴图的。做了个小trick简化计算。精确算的话,对于某点,应该是用环境贴图每个方向做个积分。简化成离散情况,只采样一部分重要方向,就是下面公式的等式左边了。
然后做一个近似,近似之后第一项可以搞成一个pre-filter的环境贴图,第二项可以预计算成一个二维贴图Orz。跪。
当然除了光照模型,文章里还讲了他们怎么近似灯光的。
代码总结
看完了几篇文章,G D F落实到代码里可以是这样的啦。
1 2 3 4 5 6 7 |
float G_schlick(float roughness, float NdV, float NdL) { float k = (roughness + 1) * (roughness + 1) * 0.125; float V = NdV * (1.0 - k) + k; float L = NdL * (1.0 - k) + k; return 0.25 / (V * L); } |
1 2 3 4 5 6 7 |
float D_GGX(float roughness, float NdH) { float m = roughness * roughness; float m2 = m * m; float d = (NdH * m2 - NdH) * NdH + 1.0; return m2 / (PI * d * d); } |
1 2 3 4 5 6 |
vec3 fresnel_factor(vec3 f0, float HdV) { return mix(f0, vec3(1.0), pow(1.01 - HdV, 5.0)); } |
因为G_schlick里面已经算上了分母上的4*NdL*NdV,下面直接乘就可以。
1 2 3 4 |
vec3 specular_light = D_GGX * fresnel_factor * G_schlick * NdL * light_color; vec3 diffuse_light = ( 1 / PI ) * NdL * light_color; |
当然最后还要算上Albeido.
Github上找到一段Shader同样可以参考,戳这里
参考资料:
Theory for Off-Specular Reflection From Roughened Surfaces
Real Shading in Unreal Engine 4
Spherical Gaussian approximation for Blinn-Phong, Phong and Fresnel
Physically-Based Shading at Disney
文刀秋二的专栏——基于物理着色