UML依赖关系速记
前言
最近学习unreal时,有时候要看大佬前辈们画的类图,有时候自己也想总结代码用uml画一下图,但是类与类之间的连线有时候看起来不是很清晰,有点影响体验,这里速记一下最常用的几种依赖关系类型。
最常见的依赖关系大概有以下六种。
依赖 Dependency
关联 Association
聚合 Aggregation
组合 Composition
泛化 Generalization
实现 Realization
最近学习unreal时,有时候要看大佬前辈们画的类图,有时候自己也想总结代码用uml画一下图,但是类与类之间的连线有时候看起来不是很清晰,有点影响体验,这里速记一下最常用的几种依赖关系类型。
最常见的依赖关系大概有以下六种。
依赖 Dependency
关联 Association
聚合 Aggregation
组合 Composition
泛化 Generalization
实现 Realization
基于物理的渲染,即PBR,是一种有别于传统经验模型的光照模型。折腾这个PBR,一方面是想对渲染管线和流程有更进一步的理解,对GPU编程有一定的认识和经验,另一方面,通过对基于OpenGL的3D游戏引擎的实现做一种尝试,不仅可以锻炼架构能力,还能对c++新的语法有更进一步的认识,了解了一些图形学的基础功能的实现,并锻炼使用了c++宏编程实现了委托机制和反射机制,为进一步实现序列化提供基础。同时编写了自己的数学库,推导了矩阵的各种仿射变换,以及旋转矩阵的推导,利用四元数进行旋转的原理与实现,正交/透视投影矩阵的推导和实现等,加强了图形学知识的数学基础,为进一步学习做准备。
书接前文,让我们来继续实现specular IBL
。上回书说到我们可以使用暴力的手段,在片段着色器里实时计算积分来求得最终的光强,只不过采样的时候通过一些数学方法(重要性采样)来提高效率。但是Epic提供了一个更高效的方案,虽然损失了一些精度,但是图形学的本质就是欺骗嘛。
此处列一下后面需要引用到的以前的文章里介绍过的方程。
\[ L_{specular}(p, \vec{v}) = \int_{\vec{l}_i \in \Omega} \color{red}{f_s}\color{black}{} \ast L(p, \vec{l}_i) \ast (\vec{n} \cdot \vec{l}_{i}) \ast d\vec{l}_i \tag{0.1} \]
重要性采样近似方程。
\[ L_{specular}(p, \vec{v}) \approx \frac{1}{N} \sum_{i = 0}^{i \lt N} \frac { f_s \ast L(p, \vec{l_i}) \ast (\vec{n} \cdot \vec{l_i}) } { p_{s}(p, \vec{l_i}, \vec{v}) } \tag{0.14} \]
书接上文,让我们继续来实现IBL,上回我们实现了低频域的漫反射间接光,即下面反射率方程的左边部分。
\[ L(p, \vec{v}) = \int_{\vec{l}_i \in \Omega} f_d \ast L(p, \vec{l}_i) \ast (\vec{n} \cdot \vec{l}_{i}) \ast d\vec{l}_i + \int_{\vec{l}_i \in \Omega} f_s \ast L(p, \vec{l}_i) \ast (\vec{n} \cdot \vec{l}_{i}) \ast d\vec{l}_i \tag{0.1} \]
那么本篇则是实现该方程的右边部分。
\[ L_{specular}(p, \vec{v}) = \int_{\vec{l}_i \in \Omega} f_s \ast L(p, \vec{l}_i) \ast (\vec{n} \cdot \vec{l}_{i}) \ast d\vec{l}_i \tag{0.2} \]
这也是一个连续型的积分,所以也需要通过离散化的方程去计算积分,但是这里不能再使用前文提到的黎曼和的方法,因为镜面反射项对精度要求更高,是一个高频域的信号。所以这里采用的是使用重要性采样加速的蒙特卡洛积分, 该方法又称Brute force specular IBL
, 很暴力。
下面分别介绍蒙特卡洛积分
和重要性采样
的数学原理,以及我们为了实现specular IBL
真正的采样手段所需要的前置数学知识。笔者主观意识里是比较想把这部分拆解清楚的,所以并不会吝啬篇幅(又不是写毕设),所以内容可能有些小长,建议脾气暴躁者先码后看。
在OpenGL
中,每一个三角形面片,都是一个平坦的平面。在该平面上的所有像素点,其法线方向都是相同的。
我们知道,无论是传统的光照模型,还是基于物理的光照模型,法线决定平面的朝向,对于光照的计算至关重要。
所以一个模型的表面法线越接近于真实世界中的法线,获得的光照就越正确,光照细节就越多。
但是目前为止,我们的法线都是跟随顶点数据传到GPU的,并没有精确到像素级的(虽然传到片段着色器时会进行插值,但是同一个表面的法线始终是一样的),这就导致在计算光照的时候我们只能得到平坦的平面,而丢失了平面的凹凸细节。比如,一个砖墙,由许多砖块组成,但是整个砖墙都只有一个法线朝向,完全忽略了砖与砖接缝处的凹凸痕迹。
于是法线贴图就应运而生了。
由于这个效果的ps shader有1400多行代码,且复杂度太高,会导致渲染时间比较长,内部为了节约性能做了限制渲染时长的逻辑,即默认如果一个shader等了1秒钟还没绘制好,那就会停止渲染,场景就会不动了。
为了玛丽,这里额外加了一个可供div配置的参数wait_max
,以自定义渲染等待时长,所以下面场景的div声明如下所示。
除此之外,还额外加了一个可供div配置的参数time_rate
来控制shader里iTime
这个uniform值每帧增加多少,该值默认是0.01。
1 | <div class="shadertoy" |
该效果用到了贴图,可以在div上添加channel0..4的属性作为贴图的路径,如下例所示。
1 | <div class="shadertoy" |
上篇使用Cook-Torrance BRDF
模型实现了精准光源直接光照的计算。本篇继续基于PBR的理论基础,通过对微平面进行半球领域近似积分来计算周围环境作用于物体上的间接光源。
本人能力有限,对其中的某些步骤尚有疑惑之处,希望以后通过进一步的深入学习可以真正地理解每一个过程,共勉。
下面将需要在本篇引用到的理论篇
推导过的公式列举一下,方便后面引用。
\[ L(p, \vec{v}) = \int_{\vec{l}_i \in \Omega} f(p, \vec{l}_i, \vec{v}) \ast L(p, \vec{l}_i) \ast (\vec{n} \cdot \vec{l}_{i}) \ast d\vec{l}_i \tag{0.19} \]
\[ f(p, \vec{l}, \vec{v}) = f_{lambert} (p, \vec{l}, \vec{v}) + f_{cook\_torrance} (p, \vec{l}, \vec{v}) \tag{0.21} \]
\[ f_{lambert} (p, \vec{l}, \vec{v}) = k_d \ast \frac {C_{diffuse}} {\pi} \tag{0.22} \]
基于图像的光照(Image Based Lighting)
是计算PBR间接光照的一种方法,用于计算除了精准光源之外的其他环境光照。
该方法将物体周围的整体环境作为一个大的光源作用于物体。所以我们需要对物体的每一个平面的半球领域进行积分才能获得最终的光照强度,即对每一个微平面来说,它会接收任意方向上的光源。
我们同样将间接光照分为漫反射项
和镜面反射项
两部分进行计算。