图形渲染 Physically Based Rendering
注意!!!:本文章重点在Unity中实现PBR的shader部分知识即BRDF模型。基于物理的材质和灯光不是重点关注的对象。
注意!!!:本文章目的是作者学习理解PBR而写的,所以资料是不全的。两个原因:1. 作者会的东西,就写的简单,只是留下关键字为复习做个提示。 2. 作者不懂不了解的。
路线发展
目前的路线是大致两条:(一个理想一个现实)
- 往高效果方向做,力求在PC平台上搞出最好的效果。搜集各种吊炸天的模型公式。
- 往性价比高的方向做,力求在移动端搞出最好效果。大致是各种优化技巧和拟合公式。
学习路径
- 一个是借助引擎,做业务方面的探索。务实
- 一个是从基础补起,摆脱引擎的限制。理论和基础。
- 数学 数学 数学
这篇文章作为关于PBR总目录入口。
本来在搞 游戏优化 集合,结果公司内开了场PBR分享会。收获颇丰。现在暂停优化,先搞出来PBR。
1. PBR概念
0. 历史
迪士尼在SIGGRAPH 2012上提出了著名的“迪士尼原则的BRDF(Disney Principled BRDF)”之后,由于其高度的易用性以及方便的工作流,已经被电影和游戏业界广泛使用。迪士尼的《无敌破坏王》就是基于BRDF的原理
1. 什么是PBR
PBR是一套渲染解决方案,包括基于物理的材质制作,基于物理的灯光PBL,基于物理的镜头,等等。
这里只是将下PBS。
简单的讲,PBS其实是一套成熟理论下的光照模型。
以我目前的理解,我认为shader分为PBR和非PBR。区别就是有没有遵循理论。
2. PBR涉及到的理论
- 能量守恒原则:
出射光线的能量永远小于入射光线的能量(自发光除外) - 物质的光学特质:(渲染领域大多分为2类:金属和非金属)
- 金属:
金属具有很高的反射率(>=0.5),不会出现任何次表面散射和透明效果。金属的所有可见光都来自反射,即所有可见颜色都来自反射。 - 非金属反射率低(<=0.06),产生镜面反射和漫反射,镜面反射为单色/灰色。
- 金属:
- 微平面理论:
我认为,是渲染精度从顶点提高到像素,现在不满足像素了,提高到亚像素级别的发展趋势。未来可能会深入到微观领域。量子力学??? - 菲涅尔反射:
光源入射方向与平面法线法线夹角的对应关系。夹角越大,亮度也就越大,反之夹角越小,亮度也就越小。 - 线性空间光照:
为了保证光照渲染的正确性,最好是在线性空间中进行操作与计算。PBR计算要放在线性空间中。 - 次表面散射(Subsurface Scattering):
这个在BRDF中不讨论,实现方式在另一个更物理的模型BSSRDF内实现。
– 为什么现在流行BRDF,而不使用更物理的BSSRDF ? 我的理解是,虽然万物都有次表面散射现象,但是这个效果在大部分材质上表现并不明显。而为了实现这个性能开销却很大,简单来说就是性价比不高,所以就去掉了这部分计算。但是在另一部分物体中,次表面散射表现明显:玉石、翡翠、牛奶、皮肤 … …。所以遇到这类材质时如果要求好的表现效果,就只能改用BSSRDF光照模型了。(这个做进阶计划)
2. 我们为什么要接入PBR(相比传统,它牛逼在哪里)
从0 章节中就看出:其高度的易用性以及方便的工作流
- 针对初级水平的非PBR,就是更真实的效果 (这个只是表象)
- 在复杂的场景下,非PBR非常容易崩。
- 一张shader实现几十种效果,减少了工作量。(巫师3引入PBR后shader数量减少了百分之八十以上)
- 参数都有明确的物理意义
- 不同美工大量的作品风格可以稳定可靠。不会出现辛苦调出来的效果,一换光照环境就完蛋了的悲剧。
- 在Unity中,可以大量合批,减少DC。
更多信息查询 关键字 :迪斯尼pbr论文 Physically-Based Shading at Disney
知乎:PBR除了工作流还有什么优势?
3. PBR的缺点
- 和传统相比需要复杂的光照环境
- 需要美术变革,PBR材质和之前的材质是不同的。
- 需要一定数学基础。
4. 我们为什么不用官方自带的PBR
- Shader变体太多
- 虽然分成了三个等级,但性能还是太耗。
- 自己写一套自己能把控。
- PBR只是基本的光照模型,还要在上面编写业务效果。
- 官方的需要光照贴图探针等等配合使用,比较复杂。
3. 全局光照
(现实中光线只要分为三类:1.直射光(太阳光)2.大气散射 3.其他物体的反射光)
全局光照是PBR效果表现真实的重要原因之一。巧妇难为无米之炊,公式原理再厉害也不可能只凭借一张 Albedo 反照率贴图就得到吊炸天的效果。所以PBR在传统的基础上额外增加了输入的数据量,比如间接光照和金属度粗糙度等数据。这里只研究直接光照和间接光照信息。
这一块和PBR链接的比较低,且很多都是操作,所以分开讲:
https://blog.csdn.net/Bilibili_Cheers/article/details/108287871
2. 输入数据
这里单独把这个列出来,是为了更好的理解,就好比做菜,我们如果把做菜所需要的原材料个个都了然如胸,那就能更挥洒自如。能更好的帮助我们理解公式。也知道这些数据是哪个方程需要的,也知道怎么处理这个数据。知其然也知其所以然。
-
贴图纹理
· 1. 颜色贴图(Albedo)金属度(Metallic||Metalness),粗糙度 (Roughness),法线贴图。 -
光照信息
2. 渲染方程
因为本人是个数学白痴,所以方程这块懵懵懂懂,所以本想把方程放入概念内,一笔带过就行了。结果在后面的学习中发现,根本不行啊!!如果对方程没有比较深刻的了解,在最后求最终函数时就是懵逼的。还是过一过吧,最好拿笔写几遍,理一理。
1. 大名鼎鼎渲染方程公式:
想象我们现在要计算表面上某点的出射辐射率,我们已知到该点的观察方向,该点的出射辐射率是由从许多不同方向的入射辐
射率叠加后的结果。其中,f (ωi , v)表示了不同方向的入射光在该观察方向上的权重分布。我们把这些不同方向的光辐射率(L i (ω i )部分)乘以观察方向上所占的权重(f (ω i , v)部分),再乘以它们在该表面的投影结果((n · ω i ) 部分),最后再把这些值加起来(即做积分)就是最后的出射辐射率。
但是,积分累加部分在实时渲染中基本无法实现,因此积分部分通常会被若干精确光源的叠加所代替,而不需要计算所有入射光线在半球面上的积分。(庆幸?)
2. 出射辐射率:
对于一个精确光源,我们可以使用下面的等式来计算它在某个观察方向 v 上的出射辐射率:
l c 表示精确光源的方向,c light 表示它的颜色。
式子使用一个特定方向的 **f (l c , v)**值来代替积分操作,这大大简化了计算。
如果场景中包含了多个精确光源,我们可以把它们分别代入上面的式子进行计算,然后把它们的结果相加即可。也就是说,反射等式可以简化成下面的形式:
现在剩下的问题就是 f (l c , v)项怎么算?f (l c , v)实际上描述了当前点是如何与入射光线进行交互的:当给定某个入射方向的入射光后,有多少百分比的光照被反射到了观察方向上。在图形学中,这一项有一个专门的名字,那就是双向反射分布函数,即 BRDF。
3. 双向反射分布函数(BRDF)
定量描述了物体表面的一点是如何和光进行交互的。
BRDF的理解:当给定观察方向(即出射方向)后,BRDF 可以给出从所有入射方向到该出射方向的光线分布。
BRDF 可以用于描述两种不同的物理现象:表面反射(高光反射项specular term)和次表面散射(漫反射项diffuse term)。
1. 漫反射项(两种)
①. 最简单但也是最广泛的漫反射模型: Lambert (虚幻4)
给定入射方向 l 的光源在表面某点的出射漫反射辐射率为:
②. Disney BRDF:(unity)
许多材质在掠射角度表现出了明显的高光反射峰值,而且还与表面的粗糙度有着强烈的联系。粗糙表面在掠射角容易形成一条亮边,而相反地光滑表面则容易在掠射角形成一条阴影边。这些都是 Lambert 模型所无法描述的。
baseColor 是表面颜色,通常由纹理采样得到,roughness 是表面的粗糙度。
这个公式使得光滑材质可以在掠射角具有更为明显的阴影边,而又使得粗糙材质在掠射角具有亮边。
2. 高光反射项
微面元理论中,表面法线为 n,这些微面元的法线 m 并不都等于 n。
因此,不同的微面元会把同一入射方向的光线反射到不同的方向上。这意味着只有一部分微面元反射的光线才会进入到我们的眼睛中,这部分微面元会恰好把光线反射到方向 v 上,即它们的法线 m 等于 l 和 v 的一半,也就是半角度矢量 h。
但是,这些 m = h 的微面元反射也并不会全部添加到 BRDF 的计算中。这是因为,它们其中一部分会在入射方向 l 上被其他微面元挡住。
微面元理论的BRDF 高光反射项:
分母 4(n ⋅ l)(n ⋅ v) 是用于校正从微面元的局部空间到整体宏观表面数量差异的校正因子。
-
F(l, h)则是这些活跃微面元的菲涅尔反射 ( Fresnel reflectance )函数。告诉我们每个活跃的微面元会把多少入射光线反射到观察方向上,即表示了反射光线占入射光线的比率。
大多数 PBS 实现选择使用Schlick 菲涅耳近似等式来得到近似的菲涅尔反射效果:
c spec 是材质的高光反射颜色。
通过对真实世界材质的观察,发现金属的高光反射颜色值比较大,而非金属的则较小。 -
G(l, v, h)是阴影—遮掩函数(shadowing-masking function)。它用于计算那些满足 m=h 的微面元中有多少会由于遮挡而不会被人眼看到,因此它给出了活跃的微面元(active microfacets)所占的浓度,只有活跃的微面元才会成功地把光线反射到观察方向上。
阴影-遮挡函数 G(l, v, h)也被称为 几何函数( geometry function ) ,它表明了具有给定面法线 m的微面元在沿着入射方向 l 和观察方向 v 上不会被其他微面元挡住的概率。
由于G(l, v, h)表示的是一个概率值,因此它的值是一个范围在 0 到 1 之间的标量。- 某些BRDF中,将该项的值设为 1。这意味着,这些 BRDF 中的G(l, v, h)表达式等同于:
这个会和分母进行抵消。但是这种 G implicit 的实现忽略了材质粗糙度的影响,缺乏一定的物理真实性,因为我们希望粗糙的表面具有更高阴影和遮挡概率。(简化公式时可选这个) - 通常阴影-遮挡函数 G(l, v, h)依赖于法线分布函数 D(h), 因为它需要结合 D(h)来保持 BRDF能量守恒的规定。
- 目前在图形学中广受推崇的是 Smith 阴影-遮掩函数:
θ v 表示观察方向 v 和表面法线 n 之间的夹角。
- 某些BRDF中,将该项的值设为 1。这意味着,这些 BRDF 中的G(l, v, h)表达式等同于:
-
D(h)是微面元的法线分布函数 ( normal distribution function , NDF )
法线分布函数的值必须是非负的标量值,它决定了高光区域的大小、亮度和形状,因此是高光反射项中非常重要的一项。
GGX (也被称为 Trowbridge-Reitz 法线分布函数)公式如下:
参数 α 是与表面粗糙度相关的参数。
https://editor.csdn.net/md/?articleId=108270452
3. 关键字解释
包含折射的BSDF,包含次表面反射的BSSRDF
2. PBR技术涉及公式
1. GGX法线分布函数
2. 虚拟置换技术 - 使用高度图的视差贴图
3.
3. 解析下入门精要代码,热热手。
3. 解析Unity官方PBR代码
源码从官方网站处下载,版本为2017版的。
unity 官方标准PBR,里面大概分三级显示标准。(从高到低)
- 中高端PC,PS4,XBoxOne 等高等硬件平台运行的3.0效果
- 移动端的简易2.0效果
- 移动端的基本效果
我们分别分析这三种,主要是为了了解官方定义的”标准“PBR是什么机制,用了哪些公式,然后为了移动端考虑性能,又简化了哪些计算。这些操作手段可以为我们在实际项目中遇到性能瓶颈时提供权威的参考意见。最后看下保底效果就ok了,这个应该是性能最好的了[笑]。
0. 解析Standard结构
Standard文件有两个SubStandard和一个Fallback组成。Fallback大家经常用,不熟悉的自己去看看,这里就不说了。
其中第一个SubStandard里面有两种效果,一种为标准Standard,一种为简易Simple。首先说说Simple。
1. Standard材质属性
首先看看 一个完整的PBR需要哪些数据,好心里有数。
Properties
{
_Color("Color", Color) = (1,1,1,1)
//反照率贴图
_MainTex("Albedo", 2D) = "white" {}
//当片元的Alpha低于此值时,会被丢弃。需要设置Mode为Cutoff
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
//粗糙度
_Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
[Enum(Metallic Alpha,0,Albedo Alpha,1)] _SmoothnessTextureChannel ("Smoothness texture channel", Float) = 0
//金属性 注意Gamma
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {}
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0
_BumpScale("Scale", Float) = 1.0
_BumpMap("Normal Map", 2D) = "bump" {}
//高度图
_Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
_ParallaxMap ("Height Map", 2D) = "black" {}
//环境光遮罩,是控制物体受影响的基本环境,一般是纯黑色的,也就是完全受周围真实环境的影响。但你可以在某些部位的贴图上绘制一些固定的颜色,让贴图颜色和真实环境同时对模型产生影响。比如你要让某个部位一直发光,不受外部光线的影响,可以在环境贴图上控制。
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
//自发光
_EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
//细节遮罩
_DetailMask("Detail Mask", 2D) = "white" {}
//细节遮罩2
_DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
//法线
_DetailNormalMapScale("Scale", Float) = 1.0
_DetailNormalMap("Normal Map", 2D) = "bump" {}
[Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
// Blending state
[HideInInspector] _Mode ("__mode", Float) = 0.0
[HideInInspector] _SrcBlend ("__src", Float) = 1.0
[HideInInspector] _DstBlend ("__dst", Float) = 0.0
[HideInInspector] _ZWrite ("__zw", Float) = 1.0
}
2. PC平台效果解析
2. 移动端高等效果解析
https://blog.csdn.net/Bilibili_Cheers/article/details/108307000
3. 移动端基本效果解析
4. 移植移动端的技术点
1. 如何廉价高效的获取环境间接光
2. 那些拟合的函数
3. 需要抛弃的高耗能函数
1. 虚拟置换技术 - 使用高度图的视差贴图
原因:高耗能,未知
5. 移动版PBR
除了官方标准版PBR实现,其他的方案多如牛毛,这里把手机版单独拎出来是因为现在基本是手游的天下了。
6. 其他版本PBR
剩余的其他版本PBR最好也熟悉下,触类旁通。也能吸收各家的优点整合下方案。官方的不一定是最好的。
1.UE4 PBR
虚幻4 中各个等级的PBR,很有参考价值。
2. 优秀PBR插件 实现方案
商店里PBR方案 看看学习下。
3. 项目内部的简化版PBR
公司项目内部的PBR,可以看看,只不过被改过很多,问题不少,不能全信。也不能公开。不过实战意义极大。
7. 扩展新PBR效果
实现PBR只是基础,类似一个大厦的地基。虽然这个地基本身就比普通的楼房高了,但对于大厦本身来说只是个起点。
1. SSS次表面效果,添加到PBR内尝试(BSSRDF)
思路:将SSS效果添加进PBR渲染方程
好吧,查到资料了,原来这个叫BSSRDF 渲染方程
Unity3d shader之次表面散射(Subsurface Scattering)
2. 艺术化风格的PBR效果,也可以叫NPR的PBR
思路:将梯度漫反射和描边效果添加进PBR渲染方程
参考资料(排名不分先后)
必读资料
《UnityShader入门精要》 18章
https://learnopengl-cn.github.io/07%20PBR/02%20Lighting/
https://learnopengl-cn.github.io/07%20PBR/01%20Theory/