PBR-实践(二)

函数

// Main Physically Based BRDF
// Derived from Disney work and based on Torrance-Sparrow micro-facet model
//
//   BRDF = kD / pi + kS * (D * V * F) / 4
//   I = BRDF * NdotL
//
// * NDF (depending on UNITY_BRDF_GGX):
//  a) Normalized BlinnPhong
//  b) GGX
// * Smith for Visiblity term
// * Schlick approximation for Fresnel
half4 BRDF1_Unity_PBS (
half3 diffColor, 
half3 specColor, 
half oneMinusReflectivity, 
half smoothness,
half3 normal, 
half3 viewDir,
UnityLight light, 
UnityIndirect gi)

D 分布函数


在这里h表示用来与平面上微平面做比较用的中间向量,而a表示表面粗糙度。

G 遮蔽函数

F 涅菲尔:

上面一种是Fresnel-Schlick近似法求得的常用版本,下面一种是虚幻引擎用的拟合版本,后面一种由于exp2函数的高效率算起来会快一些。

其次,方程中的F0理论上是平面的基础反射率,但实际实现时需要考虑另一个情况,即菲涅尔方程只对非金属有效,在表面为金属时需要用到跟金属表面颜色相关的另一个方程。为了能够用同一个材质表示金属和非金属的不同属性,将材料的金属性参数整合到F0的计算中,实际F0计算的代码如下:

float3 F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, Albedo, _Metallic);

漫反射 - 间接漫反射

//漫反射系数 =  折射率(漫)* 非金属率
float3 kd = (1 - F)*(1 - _Metallic);

根据粗糙度对以上这张贴图进行三次线性采样,采样得到的颜色就是方程左边括号内的结果。

Unity自然也给了这张图,就存储在unity_SpecCube0这个变量里。
存储的是场景和天空盒的反射探针数据(还有一个变量叫unity_SpecCube1,存储的离物体最近的反射探针的数据)。

有了图我们就开始采样,采样代码如下:

float mip_roughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness);
float3 reflectVec = reflect(-viewDir, i.normal);

half mip = mip_roughness * UNITY_SPECCUBE_LOD_STEPS;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectVec, mip); 

float3 iblSpecular = DecodeHDR(rgbm, unity_SpecCube0_HDR);

第二行好说,就是根据视线方向和法线求出个反射向量留着以后用。

第三行是用从0到1之间的mip_roughness函数换算出用于实际采样的mip层级,UNITY_SPECCUBE_LOD_STEPS是一个定义在UnityStandardConfig.cginc文件中的常量,没改的话就是6。

第四行的UNITY_SAMPLE_TEXCUBE_LOD是一个采样函数,粗糙度越高采样出的结果就越模糊。cubemap的采样使用三线性插值,即从两张最近的mipmap层级上各做一次二次线性插值再将结果插值。

最后一行使用DecodeHDR将颜色从HDR编码下解码。可以看到采样出的rgbm是一个4通道的值,最后一个m存的是一个参数,解码时将前三个通道表示的颜色乘上xM^y,x和y都是由环境贴图定义的系数,存储在unity_SpecCube0_HDR这个结构中。

于是我们得到了iblSpeclar,也就是上面间接高光的方程里左边括号的值。

原文地址:https://www.cnblogs.com/Jaysonhome/p/14109106.html