Unity TA 学习笔记系列(2) - 《Unity Shader 入门精要》基础PBR光照(一)
一、前言又是鸽了好久的更新之前写过一份基础光照就关联度而言这次来理理基础PBR光照。在《入门精要》第18章中介绍了Unity自己的实现原理由于PBR计算涉及多个组成部分每个部分都有不同的算法实现这次我会围绕自己的PBR实现以金属-粗糙度工作流为主结合书中原理进行整理。二、基础概念1、概述PBRPhysically Based Rendering基于物理的渲染指的是一套符合真实物理光学规律的渲染理论并非某一个特定的算法相较于Lambert、Phong等经验模型PBR旨在实现光照无关、全局统一、物理可信的渲染效果。PBR渲染需要遵循以下核心原则能量守恒、微表面理论、菲涅尔效应和基于物理的BRDF双向反射分布函数。2、能量守恒能量守恒是PBR最根本的原则即入射光的总能量 ≥ 出射光的总能量具体表现为以下几点a. 反射光与折射光漫反射之和不能超过入射光光线照射到物体表面后一部分被反射其余部分折射进入物体内部b. 折射光要么被吸收要么经历多次散射后从表面其他位置漫反射出来c. 物体的反射率越高漫反射越弱。3、微平面理论微平面理论提出任何光滑表面在微观尺度上都由大量朝向各异的微小平面组成主要表现为以下两点a. 粗糙度金属-粗糙度工作流的核心参数之一定义了微平面法线的统计分布。粗糙表面微平面方向混乱反射光分散形成模糊高光光滑表面微平面朝向统一反射光集中形成清晰高光b. 遮蔽和阴影粗糙表面的微平面会相互遮挡从而影响最终光照的强度。4、菲涅尔效应菲涅尔效应描述了视线与表面法线夹角入射角对反射率的影响主要表现为以下两点a. 当视线几乎平行于表面掠射角即入射角接近90度时几乎所有材质包括非金属的反射率会趋近100%当视线垂直于表面时反射率最低b. 对于金属菲涅尔反射率高且反射颜色为金属本身颜色对于非金属电介质菲涅尔反射率低且反射无色。5、基于物理的BRDF双向反射分布函数基于物理的BRDF是对以上三个原则的数学描述通常由高光反射项和漫反射项两个部分组成在《入门精要》中给出了以下两种理解方式a. 当给定入射角度后BRDF可以给出所有出射方向上的反射和散射光线的相对分布情况b. 当给定观察方向后BRDF可以给出所有入射方向到该出射方向的光线分布。或者更直观来说当一束光沿入射方向到达表面某点时BRDF表示有多少部分的能量反射到了观察方向上。三、高光反射项高光反射项用于描述表面反射这部分计算中Cook-Torrance BRDF是最常用的BRDF在我自己的实现中也采用了这个BRDF主要包含以下三个核心项法线分布函数NDF、几何遮蔽函数G和菲涅尔项F。1、法线分布函数NDF法线分布函数基于微平面理论用于描述微平面法线朝向分布计算了多少比例的微平面将光线从入射方向反射到观察方向上由粗糙度参数进行控制。在我的实现中NDF采用了GGX模型与Unity相同GGX模型的数学表达式如下其中需要注意的是roughness为线性粗糙度通常情况下粗糙度/光滑度贴图中获得的是感知粗糙度/光滑度感知粗糙度 1 - 感知光滑度线性粗糙度为感知粗糙度的平方。与Beckmann、Phong等NDF模型相比GGX拥有以下几点优势a. 更长的尾部在掠射角或粗糙表面附近GGX 的高光衰减非常缓慢产生一个从高光中心向外逐渐扩散的大范围“光晕”或“光尘”效果更符合真实世界的测量数据b. 更自然的高光过渡高光形状不再是简单的椭圆形而是朝边缘拉伸并且在粗糙金属或塑料上表现出更加柔和、真实的焦散样外观c. 能量保持较好配合合适的几何函数如 Smith-GGX能很好地满足能量守恒d. 计算相对高效公式简洁无超越函数适合实时渲染2、几何遮蔽项G几何遮蔽项也基于微平面理论描述微平面间的相互遮蔽计算了那些满足能将入射光反射到观察方向的微平面中有多少比例不会被遮蔽由粗糙度参数控制。由于NDF中采用了GGX模型这里选择采用GGX模型衍生的Smith-Schlick模型进行计算数学表达式如下其中在使用IBL光照基于图像的照明时通常使用而在直接光照下通常会使用进行计算。Smith-Schlick模型是对Smith-GGX模型模型的Schlick近似物理准确性稍逊于完整Smith模型但性能高效。3、菲涅尔项F菲涅尔项用于描述菲涅尔效应由金属度和基础反射率F0即垂直入射的反射率控制这里选择采用Schlick菲涅尔近似等式进行计算数学表达式如下其中对于非金属为标量灰度值通常取0.04对于金属则为金属本身颜色因此采用金属度对0.04和物体本身颜色进行插值计算。Schlick近似物理准确性稍低于基于折射率的菲涅尔方程但性能更高效且算式表达统一。4、Cook-Torrance BRDF根据上文计算得到的三个核心项我们对其进行统合得到Cook-Torrance的高光反射项 其数学表达式如下至此PBR中的高光反射项计算完毕。四、漫反射项漫反射项用于描述次表面散射这部分计算中Cook-Torrance原始算法中采用了简单的Lambert模型但更加常见的会选择使用Disney模型Unity就采用了该模型作为漫反射项。1、Lambert模型Lambert模型算式非常简单其数学表达式如下该式子实际是一个定值公式中需要除以是因为我们假设漫反射在所有方向上的强度都是相同的而BRDF要求在半球内的积分值为1。2、Disney模型Disney模型相较于Lambert模型在以下两方面修正了Lambert漫反射不随粗糙度和角度变化的缺陷a. 引入双向菲涅尔因子同时考虑光照入射角和视线出射角模拟掠角时光线折射减少导致的漫反射变化b. 粗糙度参与漫反射计算对于光滑材质掠射角漫反射减弱对于粗糙材质掠射角漫反射增强微平面朝向随机边缘不会发黑。Disney模型的数学表达式如下其中为90度掠射角处的漫反射菲涅尔系数和粗糙度强绑定。3、改进型Disney模型在寒霜引擎的PBR实践这篇文章中作者对寒霜引擎发表的论文进行了讲解其中就提到了寒霜引擎对于Disney模型的改进文章中通过分析其半球方向上的反射数据发现Disney模型的最终反射值高于1也就是说无法遵循能量守恒于是寒霜对其进行了简单的修改使其保留原有反射下对结果进行归一化使反射能量接近1我的实现中就采用了这套改进后的模型。修正后的数学表达式如下其中是基于粗糙度对90度掠射角漫反射的能量偏置降低光滑表面的掠射角漫反射是对反射能量的归一化降低粗糙表面的散射1.51是基于测量数据的经验值整体上就是将高粗糙度下的Disney反射能量拉回能量守恒范围。五、HLSL实现1、高光反射项a. 法线分布函数//GGX法线分布函数 float NDF_GGX(float NdotH, float linearRoughness) { float a linearRoughness * linearRoughness; float a2 a * a; float NdotH2 NdotH * NdotH; float denom NdotH2 * (a2 - 1) 1; denom PI * denom * denom; return a2 / denom; }b. Smith-Schlick几何遮蔽项//Smith-Schlick几何遮蔽 float Geometry_SchlickGGX(float NdotX, float k) { float nom NdotX; float denom NdotX * (1 - k) k; return nom / max(denom, EPSILON); } float Geometry_Smith(float NdotV, float NdotL, float k) { float ggx_v Geometry_SchlickGGX(NdotV, k); float ggx_l Geometry_SchlickGGX(NdotL, k); return ggx_v * ggx_l; }c. 菲涅尔项//辅助函数 float Pow5(float a) { float a2 a * a; return a2 * a2 * a; } //菲涅尔项 float3 Fresnel_Schlick(float3 F0, float VdotH) { return F0 (1 - F0) * Pow5(1 - VdotH); }d. Cook-Torrance BRDF//本方法只是对Shader中的计算进行统括存在上下文问题需根据实际情况修改 float3 CookTorranceSpecularBRDF() { //菲涅尔项 float3 F Fresnel_SchlickFrostbite(F0, VdotH, linearRoughness); //法线分布函数 float D NDF_GGX(NdotH, roughness); //几何遮蔽项 float k (1 roughness) * (1 roughness) / 8;//直接光k项 float G Geometry_Smith(NdotV, NdotL, k); //Cook-Torrance BRDF float3 nominator F * D * G; float denominator 4 * NdotV * NdotL 0.001; //0.001防止分母为0 float3 specularBRDF nominator / denominator; return specularBRDF; }2、漫反射项a. Frostbite-Disney漫反射float DisneyDiffuseFrostbite(float NdotV, float NdotL, float LdotH, float roughness) { float energyBias lerp(0, 0.5, roughness); float energyFactor lerp(1, 1 / 1.51, roughness); float fd90 energyBias 2 * LdotH * LdotH * roughness; float lightScatter 1 (fd90 - 1) * Pow5(1 - NdotL); float viewScatter 1 (fd90 - 1) * Pow5(1 - NdotV); return lightScatter * viewScatter * energyFactor; }b. 漫反射BRDF//本方法只是对Shader中的计算进行统括存在上下文问题需根据实际情况修改 float3 DiffuseBRDF() { float diffuseFactor DisneyDiffuseFrostbite(NdotV, NdotL, LdotH, roughness); //菲涅尔越强漫反射越弱金属度越高漫反射越弱 //F为高光反射中的菲涅尔项 float3 kD (1 - F) * (1 - metallic) * diffuseFactor; //物体自身颜色影响、半球积分归一化 float3 diffuseBRDF kD * albedo / PI; }3、最终颜色//本方法只是对Shader中的计算进行统括存在上下文问题需根据实际情况修改 float3 FinalColor() { float3 diffuseBRDF DiffuseBRDF(); float3 specularBRDF CookTorranceSpecularBRDF(); float3 lightInfluence light.color * NdotL; float3 finalColor (diffuseBRDF specularBRDF)*lightInfluence; return finalColor; }4、效果图*不知道为什么图片传不上来后面再更新试试……六、总结本文对PBR高光反射项和漫反射项中常用的BRDF模型进行整理和介绍。当然仅仅靠本文内容是不够的还是无法在Unity中完成PBR渲染毕竟目前还没有加上环境光和自发光后面会找时间再开一篇文章完整把PBR的渲染流程贴上来。如果文章中有什么错误还请各位大佬多多指出。