本文主要是把个人学习的PBR经验做个小总结,便于理清思路与复习,如果能帮助到您的话那也是荣幸之至,如果发现有不对的地方,请大佬们加以指正。
所涉及到的PBR知识主要是BXDF中最简单也是最为常用的双向反射分布函数(Bidrectional Beflectance Distribution Function,BRDF)类型,BRDF搞明白之后,其它的如BTDF、BSDF、BSSRDF也就有了基础。
本文有关各种原理不做详细的赘述,可以自行找各位大佬们的精彩文章或视频去深入,在此只做简要的阐述,请见谅。
一、 PBR知识体系架构图
认识PBR前,还是一定要多熟悉这个架构图,对于理清各分支细节有很大的好处,特别是刚接触容易迷惑之时。
上图是自己绘制的比较简版的架构图,详细的PBR架构图请可以去下载查看[1]
清晰了上图的架构之后 ,就可以按各分支详细分解了。
几个分支组合之后的效果如下:
二、资源列表
示例模型为网上下载的一个复古电脑,仅1920个三角面的低模,等有时间了再自己做一个高模的示例模型吧。
贴图有5张: Albedo基本色贴图、自发光贴图、金属度贴图、粗糙度贴图、法线贴图、以及AO遮挡贴图。


Properties属性内容

在面板中Option添加了几大分支的开关与选择项,在编写时便于调试使用。
Shader代码片段:
Properties
{
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
[Space(10)]_MainTex ("BaseTexture", 2D) = "white" {}
[NoScaleOffset]_BumpMap("BumpMap",2D)="white"{}
_BumpScale("BumpScale",Range(0,1))=1
[Space(20)]
[NoScaleOffset]_MetallicTex("MetallicTex",2D)="white"{}
_MetallicScale("MetallicScale",Range(0.0,1.0))=0.0
[NoScaleOffset]_RoughnessTex("RoughnessTex",2D)="white"{}
_RoughnessScale("RoughnessScale",Range(0,1))=0.5
[Space(20)] [NoScaleOffset]_OcclusionTex("OcclusionTex",2D)="white"{}
_OcclusionScale("OcclusionScale",Range(0,1))=1
[Space(10)] [NoScaleOffset]_EmssionTex("EmssionTex",2D)="white"{}
[HDR]_EmissionColor("EmissionColor",Color)=(1,1,1)
[Header(Aniso)]
[Space(10)]
[KeywordEnum(None,GTR2,GGX)] _ANISO("AnisoType",float)=0
_Aniso("Aniso",Range(0,1))=0
_AnisoOffset("AnisoOffset",Range(-10,10))=0
_AnisoRoat("AnisoRoat",Range(-10,10))=0
// [Space(10)] _ClearCoat("ClearCoat",Range(0,1))=0
[Header(Option)]
[Space(10)]
[Toggle] _IS_AMBIENT("自发光",float)=1
[Toggle] _IS_DIFFUSE("漫反射",float)=1
[Toggle] _IsSpecular("镜面反射",float)=1
[KeywordEnum(None,GGXTerm,GTR1,GTR2,GND)] _UIDNF("法线分布 D项",float)=0
[KeywordEnum(None,Unity,Schlick,SG)] _UIF("菲涅尔 F项",float)=0
[KeywordEnum(None,Filament,GGX,GSF,Beckman)] _UIG("几何遮蔽 G项",float)=0
[Toggle] _ISIBL("环境光照",float)=1
}
Pass中需要引入与Shader文件同目录的自定义的文件: #include "Pbr_Function.cginc"
三、漫反射项
3.1漫反射和镜面反射
先从漫反射和镜面反射开始,当灯光照射一个标准的BRDF材质时可以拆分成两部分:漫反射+镜面反射,如下图可以有一个很直观的了解。

3.2公式
漫反射项使用的是Unity内置的Disney Diffuse函数。
3.3效果展示

3.4 相关代码
以下为UnityShader中内置的Disney Diffuse代码部分。
inline half Diffuse_Disney(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
{
half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
// Two schlick fresnel term
half lightScatter = (1 + (fd90 - 1) * Pows5(1 - NdotL));
half viewScatter = (1 + (fd90 - 1) * Pows5(1 - NdotV));
return lightScatter * viewScatter;
}
inline half Pow5 (half x){
return x*x * x*x * x;
}
Pow5这个方法,有一维到四维,但为什么不直接用pow函数,而是使用相同的量相乘5次,其在文中也有说到其两个好处:
- 更好的优化渲染管线
- 不用担心出现异常
四、镜面反射项
整个镜面反射的公式结构如下:
分母 4(n·l)(n·v):校正因子(correctionfactor),作为微观几何的局部空间和整个宏观表面的局部空间之间变换的微平面量的校正。
4.1 D项 法线分布函数
D(h):法线分布函数(Normal Distribution Function),描述微面元法线分布的概率,即正确朝向的法线的浓度,即具有正确朝向,能够将来自L的光反射到V的表面点的相对于表面面积的浓度。
4.11 大致原理
把一个平面哪怕是镜子放大到微观层面,会发现表面会是凸凹不平的表面,以此来理解就容易懂了,看下面的示意图。
