基于物理的渲染PBR(一):pbr的基础理论和推导

article/2025/9/17 7:53:11

初始PBR

最近刚接触pbr不久,我搜寻了许多文章进行阅读并了解后发现,pbr涉及到的知识点繁琐且不容易理解,所以想在博客上给自己记录并总结一下,方便以后回顾并加深记忆。

这里首先借用知乎上的大佬毛星云关于pbr所涉及到的知识要点:
在这里插入图片描述
下面就整理下自己的知识点

一. PBR的基本介绍

1.1 PBR的概念

PBR是Physically based rendering的缩写,直译的意思就是基于物理的渲染,即利用真实世界的规律和理论,使用数学和物理的相关知识和公式推导出一系列渲染的方程,近似的模拟真实世界。这里为什么说近似呢,因为在真实世界里,人眼的像素级别是以亿为单位的,而目前人类所使用的硬件水平和知识水平暂时还达不到这种高度,所以只能最大程度的模拟真实世界

1.2 PBR涉及的参数

在Unity3d里有一个最标准的内置的pbr着色器(Standard Shader),里面包含着许多重要的参数,下面列举一些比较重要的参数:

1.2.1 Albedo
定义了物体整体的颜色,也可以使用纹理进行采样,非金属材质的亮度范围通常在50-243,而金属材质的亮度一般在186-255之间

1.2.2 Metallic
定义了物体像金属的程度,该值同样可以用纹理进行采样,对应的是纹理的R通道值,如果该值为0,则表示该物体为一个绝缘体,如果该值为1,则表示该物体完全是一个金属材质

1.2.3 Smoothness
定义了物体表面的光滑程度,该属性非常的重要,因为在后面涉及到光照计算时,该值要作为参数进行计算。该值作为Metallic的附属属性,如果在定义Metallic时使用了纹理进行取值,那么该纹理的A通道值则会作为Smoothness的值

另外还有许多参数,诸如法线贴图、高度图、遮蔽图等属性,这些暂时就不一一细说,下面给出Unity提供的校准表格,分别是金属工作流和高光反射工作流的校准表格。

在这里插入图片描述
在这里插入图片描述

二. PBR的基础理论和推导

要先满足PBR,我们首先要满足一下三个条件:

  • 基于微平面模型(Be based on the microfacet surface model)
  • 能量守恒(Be energy conserving)
  • 使用基于物理的BRDF(Use a physically based BRDF)

下面就一一细说以上三个条件

2.1 微平面(Microfacet)

大部分的PBR技术都是基于微平面理论。微平面理论认为,在微观上,所有材质表面都是由很多不同的朝向不一的微小平面组成,有的材质光滑而有的材质粗糙,如图左是较为粗糙的,图右是较为光滑的。
在这里插入图片描述
这样的结果会导致什么呢?当光线射入这些平面时,通常会发生镜面反射,而较为粗糙的平面的反射光的朝向会更加的乱序,光滑的平面会较为的平齐。如图所示

在这里插入图片描述
从微观角度上讲,没有任何平面是绝对的光滑的,而微平面已经无法进行逐像素的细分,所以我们一般使用上面提到的参数Smoothness粗糙度,用统计学的方式来估算平面的粗糙程度。

下面要引入一个重要的参数,这个参数是光照向量 l 和视线方向 v 之间的中间向量,我们一般称它为半角向量(Halfway Vector),用 h 来表示,下面是基于Unity的计算公式:

//lightdir 为光照方向 viewdir 为视角方向 worldpos 为计算的在世界的顶点坐标
fixed3 lightdir = normalize(UnityWorldSpaceLightDir(worldpos));
fixed3 viewdir = normalize(UnityWorldSpaceViewDir(worldpos));
fixed3 halfdir = normalize(lightdir + viewdir);

2.2 能量守恒(Energy Conservation)

在基于微平面理论下,我们采用近似的能量守恒,即出射光总能量不能大于入射光总能量(自发光材质除外)。为了进一步了解该定律,我们首先要知道,当一束光照射下来时,光路具体的变化。
当光照射到物体表面时,通常会发生反射折射两个部分。反射就是我们常说的镜面反射,即不经过物体内部,直接从表面反射出去,而从常理来说,一般光滑的物体漫反射较少,高光反射比较多,比如金属一类的材质。反之则是塑料一类的粗糙物体,漫反射较多,高光反射比较少。而折射即指光线进入了物体的内部,而该部分的光线会跟物体内部的粒子碰撞,产生损耗,下面又分几种情况。
第一种情况是光线经过碰撞后完全消耗掉,即在内部消失。
第二种情况是经过多次碰撞后又从物体表面折射出,我们一般称这个光学现象叫次表面散射,这种应用一般用于皮肤、大理石、蜡像等材质渲染。
第三种情况是经过多次碰撞后从物体的另一个表面折射出,我们一般称这个光学现象叫透射
不过这几种情况在这里不多做讨论,等以后深入了解了在记录。

回到能量守恒这个话题,通常PBR会简化折射部分,将平面上的折射光都视为完全吸收而不会散开,即不存在次表面散射。所以我们可以先计算镜面反射部分,此部分等于入射光线被反射的能量所占的百分比。而折射部分可以由镜面反射部分计算得出。

float kS = calculateSpecularComponent(...); // 反射/镜面部分
float kD = 1.0 - kS;                        // 折射/漫反射部分

所以我们可以看出,镜面反射部分和漫反射部分的和肯定不会超过1.0,从而近似地达到能量守恒。

★2.3 反射方程(Reflectance Equation)

该章节非常的重要,所以标注了一个星星。

渲染方程(Render Equation)是用来模拟光的视觉效果最好的模型。而PBR的渲染方程是用以抽象地描述PBR光照计算过程的特化版本的渲染方程,被称为反射方程

PBR的反射方程可以抽象成下面的公式:
在这里插入图片描述
一眼看去,好像很复杂,其实不然。我们把该公式拆分,并一一阐述,就一目了然了。
首先,我们先把公式下的参数了解清楚,Lo为出射光的辐射率,一般光的测量便是用辐射度量来计算,而p便是该点的辐射率,ωo指的是视角方向,所以该式子计算的便是该p点在所有视线方向ωo上被反射出来的综合,即总辐照度。
下面式子里分为三个部分,第一个部分为反射函数,ωi一般指的是光照方向,该函数为重点,待会儿我们将细细拆分。第二个部分是入射光的辐射度,该部分一般可以由光源处获得。最后一个部分是一个点乘计算,即受到入射光与平面法线夹角影响。
最后我们在把以该点法线为基准的一个半球,做一个积分运算,因为有多个方向的入射光,所以我们需要计算所有相关的光照,即如图所示:

在这里插入图片描述

★2.4 双向反射分布函数(BRDF)

在反射率方程里,我们提到了三大关键的部分。而其中,BRDF反射函数部分是最为关键的一部分,它是一个使用入射光方向ωi作为输入参数的函数,输出参数为出射光ωo,表面法线为n,参数a表示的是微平面的粗糙度。

首先我们先理解该函数的意义,该函数的意义是计算每一束不同的光线对不透明的物体最终的反射光的贡献量。而BRDF也是基于之前提到的微平面理论和能量守恒定律,BRDF函数有多种模拟表面光照的算法,具体在一开始给出的图里可以看到,下面以Cook-Torrance BRDF为例,基本在大部分的实时渲染上都是使用该算法。

首先该算法分为镜面反射项漫反射项两个部分,具体公式如下:

在这里插入图片描述
其中Kd是入射光中被折射的比例,Ks是另外一部分被镜面反射的入射光。在等式左边部分的Flambert表示的是漫反射部分,与我们之前学习的普通光照模型漫反射类似,但它是一个恒定的算式:
在这里插入图片描述
其中C代表的是Alebedo或表面颜色,而除于π是为了后面的积分做准备,当然我们记得在以前兰伯特模型里,后面还需要乘上表面法线和光照的点积,其实并没有省略,只是我们把该点积移出了反射方程后面的位置,即之前讲到的n · Wi

BRDF另外一部分为高光反射项,它具体的公式为:

在这里插入图片描述
该式子由三个函数DFG和一个标准化因子构成。而D、F、G的函数意义则是充分模拟了特定部分的表面反射属性,下面就来一一介绍。

  • D(Normal Distribution Function,NDF): 法线分布函数,估算在受到粗糙度影响下,微平面的法线与中间向量方向一致的比率。
  • F (Fresnel equation): 菲涅尔方程,描述的是在不同的表面角下表面反射的光线所占的比率
  • G (Geometry function): 几何函数,也可以称为阴影-遮掩函数,含义是计算当一些微平面相对粗糙,导致遮挡住其他微平面所反射的光线

★2.4.1 D (Normal Distribution Function,NDF)

法线分布函数,从统计学上近似的表示了微平面法线与中间向量h取向一致的微平面的比率。对于该函数已经衍生出许多不同的计算方法和模型,我们这里选用的是Trowbridge-Reitz GGX(GGXTR) 模型,具体公式如下:

在这里插入图片描述

简单介绍下这里的参数,首先n为表面的法线,h为上文曾提到的中间向量,a为表面的粗糙度。
对于该函数,通俗的来说,我们可以这样理解。我们在宏观上先把平面的法线定义为n,在微观上我们先把微平面上的法线定义为m,然后把光照方向l和视角方向v的中间向量定义为h即上文所说的计算。因为我们都知道,镜面反射是入射角和反射角相同的反射,因此,当中间向量h和微平面的法线m一致的时候,就会发生镜面反射。所以该函数实际上可以改成,当给定光的向量l和视角方向v都给定的时候,与他们计算得到的中间向量h一样的微平面法线m有多少?下面给出更加直观的图:

在这里插入图片描述

在这里插入图片描述

在Unity,该模型的代码如下:

float DistributionGGX(fixed3 N, fixed3 H, float a)
{float a2     = a*a;float NdotH  = max(dot(N, H), 0.0);float NdotH2 = NdotH*NdotH;float nom    = a2;float denom  = (NdotH2 * (a2 - 1.0) + 1.0);denom        = PI * denom * denom;return nom / denom;
}

★2.4.2 F(Fresnel equation)

菲涅尔方程定义的是在不同的观察方向上,表面上被反射的光除于被折射的光的比例。举一个现实例子,一般我们在水面上的时候,低头看水面,这个时候视角方向几乎是90°,所以这个时候光线的折射占比非常大,而反射非常少。反之,我们看远处的水面时,有很多时候都看不到底面,都是一层高光的反射,这个时候便是反射占比大,而折射占比少。

对于菲涅尔方程,我们使用的是Fresnel-Schlick来近似:

在这里插入图片描述

其中,F0表示的表面基础反射率,需要注意的是,Fresnel-Schlick算法仅仅是为了电介质表面定义的算法,所以如果我们需要计算金属表面时,需要预先计算F0,下图是一些基础反射率:

在这里插入图片描述

另外对于金属来说,它的基础反射率一般是带有色彩的,所以我们一般会这样处理:

vec3 F0 = vec3(0.04);
F0      = mix(F0, surfaceColor.rgb, metalness);

最后在Unity中,我们可以这样定义Fresnel Schlick

//cosTheta为表面法向量n与观察方向v的点乘的结果
fixed3 fresnelSchlick(float cosTheta, fixed3 F0)
{return F0 + (1.0 - F0) * pow(1.0 - cosTheta,5.0);
}

★2.4.3 G(Geometry function)

几何函数则是拿来模拟微平面间的相互遮挡导致光线能量减少的现象。

在这里插入图片描述

在函数里,我们使用了粗糙度作为参数,这也意味着物体表面越粗糙,微平面产生自阴影的概率越高,该几何函数我们选用了由GGX和Schlick-Beckmann组合而成的模拟函数Schlick-GGX

在这里插入图片描述

其中参数k是使用粗糙度a计算得来的,我们根据不同的光照给出了不同的k值,对于直接光照,k的取值如下:
在这里插入图片描述
对于IBL光照(Image Based Lighting),即环境光照,k的取值如下:

在这里插入图片描述

为了有效地模拟几何体,我们需要同时考虑两个视角,视线方向(几何遮挡)跟光线方向(几何阴影),我们可以用Smith函数将两部分放到一起:

在这里插入图片描述
其中v表示视线向量,Gsub(n,v,k) 表示视线方向的几何遮挡;l 表示光线向量,Gsub(n,l,k) 表示光线方向的几何阴影。

在Unity里,该几何函数的代码如下:

float GeometrySchlickGGX(float NdotV, float k)
{float nom   = NdotV;float denom = NdotV * (1.0 - k) + k;return nom / denom;
}
float GeometrySmith(vec3 N, fixed3 V, fixed3 L, float k)
{float NdotV = max(dot(N, V), 0.0);float NdotL = max(dot(N, L), 0.0);float ggx1 = GeometrySchlickGGX(NdotV, k); // 视线方向的几何遮挡float ggx2 = GeometrySchlickGGX(NdotL, k); // 光线方向的几何阴影return ggx1 * ggx2;
}

★2.4.4 Cook-Torrance反射方程(Cook-Torrance reflectance equation)

最后,我们把上述讲到的漫反射项和高光反射项(包括重要的DFG)合并起来,可以得到最终的反射方程:

在这里插入图片描述

又因为在菲涅尔项F代表了光的比率,所以该式子是隐含了因子Ks,所以我们把它去掉,最终的式子即为:

在这里插入图片描述

该方程完整的定义了一个基于物理的渲染模型,也就是我们说的PBR。

引用:

1.由浅入深学习PBR的原理和实现

2.PBR Specular D的几何学含义

3.【基于物理的渲染(PBR)白皮书】(四)法线分布函数相关总结

4.Unity Shader入门精要(书籍)


http://chatgpt.dhexx.cn/article/lkCGToZG.shtml

相关文章

策略路由(PBR)

1、基本概念 PBR (Policy-Based Routing,策略路由): PBR使得网络设备不仅能够基于报文的目的IP地址进行数据转发,更能基于其他元素进行数据转发,例如源IP地址、源MAc地址、目的MAc地址、源端口号、目的端口号、VLAN-ID等等。 用户还可以使用A…

技术美术知识学习_04:PBR的个人理解

一、什么是PBR? PBR(Physically Based Rendering),中文翻译为基于物理的渲染。 PBR是一种渲染方式,是使用基于物理原理和微平面理论的光照模型,以及使用从现实中测量的表面参数来准确表示真实世界材质的渲…

图灵直播|《第一行代码》作者郭霖在线Coding,今晚八点,给你留位!

图源来自Pexels “我们为什么需要 Kotlin?答:消失的 Getter 和 Setter、又见空指针、Smart Cast、打日志、再见Utils、晚安ButterKnife……” 相信很多人初识Kotlin,都是基于谷歌技术大牛 Steve Yegge的一篇文章《为什么说 Kotlin 比你们用的…

撸了郭霖大神写的Framework源码笔记,offer拿到手软

前不久听我一个字节的朋友说了一个神转折的故事。 一名大专生,异常执着地向他们公司投简历,屡战屡败,屡败屡战,前前后后向字节跳动投了九次简历。 你猜后面怎么着?还真让他成功了,第九次居然拿到了offer! 看到这里,不过是一个普普通通的励志故事吧,一个菜鸡凭借自己…

(郭霖)Android图片加载框架最全解析(一),Glide的基本用法

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53759439 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每天都有文章更新。 现在Android上的图片加载框架非常成熟,从最早…

图灵专访:郭霖的成长之路

各位小伙伴们大家早上好,最近这段时间我真的是快要忙疯了,好多件事情同时在做,实在抽不出时间写原创文章。 正巧想到前段时间和图灵做了一次专访,记录了一些我的成长经历。这篇专访姑且也可以算是一篇原创吧,因为里面的…

第一行代码-第二版(郭霖著)笔记十一(Material Design)

目录 一、什么是Material Design 二、Toolbar 三、滑动菜单 1.DrawerLayout 2.NavigationView 四、悬浮按钮和可交互提示 1.FloatingActionButton:悬浮按钮 2.Snackbar:提示工具 3.CoordinatorLayout:加强版FrameLayout 五、卡片式…

第一行代码-第二版(郭霖著)笔记二(Activity)

目录 一、Activity的用法 1.Activity 2.Toast 3.菜单 4.销毁一个活动 二、Intent 1.使用显示Intent 2.使用隐式Intent 3.更多隐式Intent的用法 4.向下一个活动传递数据 5.返回数据给上一个活动 三、活动的生命周期 1.返回栈 2.活动的四种状态 3.活动的生存期 4…

android动态权限依赖库,动态申请app权限:郭霖大神的PermissionX库带你告别原生

引言 为什么那么多人想要自定义Android的权限申请PermissonX?因为PermissionX默认的权限提醒弹出实在是太丑了!而且,需要在你需要提醒用户弹出Dialog时,显得捉襟见肘,你可能就在想有没有一款能封装进去Dialog提醒用户,具有超棒的用户体验,还能看起来美观大气的Permissio…

跟随郭霖学Volley

volley 下载导入volleyjar 学习地址: https://blog.csdn.net/guolin_blog/article/details/17482095 2013在Google I/O大会提出 github地址: https://github.com/google/volley 下载volley导入到as 具体的操作是: project模式下 具体看图: 之后的操作是打开lib 选择jar 右…

android 6.0权限 郭霖,Permission——郭霖认为最优的运行时权限方案

Android6.0发布这么久,对运行时权限也看了很多资料,对比过几个流行的库。但是个人还是喜欢在项目里用自己动手封装的东西,哪怕照抄也好。。。不知道是什么原因。 前天无意听郭神的直播。讲解的是运行时权限的封装,收益颇多。依样画…

郭霖LitePal

由于项目需要开始学习sqlite 一开始先学习使用的是 android ormlite 操作 从最基本的建表增删改查一路走来 磕磕碰碰很多 都是在内存中操作sqlite 只能通过sqlitestudio工具进行查看 不能导出 并且应用卸载数据表就丢失 最终考虑在sd卡中操作sqlite 但是ormlite 并没有这方面…

android郭霖博客,Runtime Permissions(郭霖CSDN公开课)

运行时权限 Api23开始,Android权限机制更改,有一部分权限不再是简单的在AndroidManifest.xml中声明即可。而是需要在运行时让用户去选择是否允许该项权限的操作。 那么哪些权限需要在运行时申请呢?危险权限需要这么做,而普通权限仍…

看一看Facebook工程师是怎么评价《第一行代码》的

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。 大家好,我是一名Facebook的工程师,同时也是《第一行代码——Android》的忠实读者。 虽然我最近几年是在国外读书和工…

郭霖:手把手教你实现 App 360 度旋转看车效果

这是郭神号前阵子的推送,应该有不少人还没有看过,现在分享给大家,希望对大家的Android工作和学习有所帮助。 / 作者简介 / 本篇文章来自Youth Lee的投稿,分享了他自己结合Glide写的一个控件,希望对大家有所帮助&#…

第一行代码-第二版(郭霖著)笔记(初识Android)

系列文章目录 第一章 第一行代码-第二版(郭霖著)笔记(初识Android) 目录 一、Android简介 1.android系统架构 2.Android应用开发特色 二、工具准备 Tips:新建项目的时候是否勾选use legacy android.support libraries 三、…

专访郭霖:成长无止境

留意文末赠书活动 嘉宾 | 郭霖 文 | 张霞 郭霖,Android开发工程师,Android GDE(Google认证开发者专家)。从事Android开发工作9年,有着丰富的项目实战经验,负责及参与开发过多款移动应用与游戏,开…

解决http响应状态为canceled

最近写登录的页面,发现通过ajax请求后台的时候,监控台返回该请求的状态是canceled。 原因 仅仅是由于之前为了在输入账号时让浏览器进行自动补全,而将原先的div更换为了form,而不巧的是之前的登录事件源使用的是button。 而至于为什么stat…

ajax请求导致status为canceled的原因

在使用layui的form表单提交以后,请求状态总是canceled。后来在form表单的后面添加了一行代码: return false; 就可以了。 文档:https://www.layui.com/doc/modules/form.html#onsubmit 错误: 解决方法: 总结一下&…

ajax请求文件状态为 canceled 的解决办法

ajax请求文件状态为 canceled 的解决办法 场景还原原因分析解决 场景还原 最近做一个表单提交的需求时,遇到了这种情况,输完账号密码后回车提交,报错,f12打开看到是请求的status为canceled了,震惊一秒钟。。。如下图&…