跳转到主要内容
Game Engine Jul 29, 2022 1 tags

L13~14 :

L13~14 :

cover

1 引入——ShadowMapping阴影贴图

之前提到光栅化的着色,我们知道这是一种局部的现象

着色的过程中,我们只会考虑着色点自己,光源,以及摄像机

我们不考虑其他物体,甚至不考虑物资自身的其他部分对着色点的影响

而事实上是会有遮挡的关系的,是会有阴影的

之前我们解决不了阴影问题,现在来试着在光栅化的范围里面解决一下

  • 一种解决方法即为Shadow Mapping
  • 本质上Shadow Mapping这个方法是一种图像空间的做法,也就是在生成阴影这一步,我们不需要知道场景的几何信息(会在之后的实现步骤中讲述) 同时这个方法也会产生走样问题,并且经典的Shadow Mapping只能处理点光源(接下来也会以点光源为例进行讲解) 这个方法的关键思想在于——如果有点不在阴影里,那么这个点可以被摄像机和光源都看到 从这个思想我们看出阴影应该会有很明显的边界,也就是硬阴影

1.1 阴影贴图原理

  • 首先我们从光源看向场景,做一遍光栅化,我们就会得到光源能看到什么,得到一幅图

  • 我们不进行着色,把这个图的深度记下来

  • 我们从摄像机出发,再次看向这个场景

  • 我们把现在看到的点,投影回光源刚才看到的投影平面上

  • 如果深度不一致,那就看不到 下面举例

  • 我们做Shadow Mapping的一个结果

  • 从光源视角看到的图,以及深度

  • 从相机看过去,做完测试的结果 看着很脏,为什么呢?这也是这个方法存在的问题之一 浮点数的相等比较存在精度问题 人们处理精度的方法很多,但是都不能本质解决问题 还有一个问题是,一开始我们从光源看向场景,我们要把它存到一个图里面 这个图本身存在分辨率,它与渲染时的分辨率的搭配不好的话,会存在走样 更大的深度图的分辨率,开销也会变大

  • 即使存在问题,依然是目前的主流方法

1.2 存在的问题

  • 总结一下提到的问题硬阴影(点光源)
  • 关于软硬阴影的问题

2 Why Ray Tracing?

  • 光栅化不好做全局的效果比如软阴影,光泽反射,间接光照有一些巧妙的方法可以处理,但是不能保证正确性
  • 光栅化很快速,但是质量不高
  • 光线追踪是很准确的,但是会比较慢

3 基本光线跟踪算法

3.1 光线定义

  • 我们首先需要对光线定义光沿直线传播,不发生碰撞,是从光源到人眼的对于第三个性质,我们在根据光路可逆性,应用时会采取从人眼到光源的方法

3.2 Ray Casting 光线投射

光线追踪既然是追踪,我们会从终点开始,也就是从眼睛/相机开始

我们首先需要做的是光线投射

我们假设往虚拟的世界中看,眼前放了一个成像平面,成像平面被我们画成不同的像素格子 对于每一个像素,我们可以从相机连一条线,穿过这个像素,这样就可以打出一根光线,可以打到场景中 如果光线和场景的某一物体相交,那么交点和光源连线,看光源是否可见这个点(这个点在不在阴影里),如果可见,那么就形成一条有效的光路 那么就可以计算这条光路上的能量,进行着色

在下面的例子中,我们永远考虑眼睛是一个针孔摄像机,即眼睛是一个点,一个位置,不考虑实际相机的处理,以及镜头什么的(这部分会在路径追踪说) 对于场景中的物体,我们假设光打到它之后会发生完美的折射与反射 下图从眼睛开始,穿过成像平面的一个像素,投射一条光线(eye ray) 这个光线会打到场景的某一个位置上,我们取最近的交点 (这一步其实就解决了深度测试的问题)

当我们发现了一个点之后,我们要考虑这个点会不会被照亮

我们从这点到光源连一条线(shadow ray)

如果可以连上就表示能被照亮(下图黑线箭头为法线)

有了法线,入射方向,出射方向,我们就可以做着色,写入像素的值,这时候可以用各种各样的着色模型 比如之前的Blinn Phong

光线投射做了这么一件事,每一个像素投出去一个光线,和场景相交求的话求最近交点,最近交点和光源连线,判定是否可见,然后算着色,写回像素的值.

3.3 递归(Whitted风格)光线追踪

之前就是用光线投射的方法,我们还是只考虑光线弹射一次,但其实光线可以弹射很多次,这也就是接下来要介绍的这个方法能做的

我们还是从光线投射开始

在这个点上,我们先考虑这个球是一个玻璃球

光线打到这个球上肯定发生两个事情,一个是要被反射掉,一个是被折射进去

在算着色的过程中也发生了一点变化

之前是光线投射到这个点之后,看这个点能不能被照亮,然后再计算它的着色

在光线弹射次数多了以后,我们在每一个弹射点都会去计算着色的值(能量损失什么也要算),然后把它们都加回这个像素的值里面去

4 光线与物体相交

4.1 光线与隐式表面相交

我们要判断光线投射出去之后要打到什么,也就是要求交点

那么求交点之前我们先把数学上的光线定义出来

光线定义也就是一条射线,有一个起点,有一个方向,有这两个量就可以定义一条光线

光线上的任何一个点都可以用 t 为自变量的函数表示

为了说明光线与曲面的交点

我们从光线与球求交的情况开始切入,交点即这个点又在球上又在光线上

方程于是可以建立起来了

解这个方程可以有很多方法,我们根据解的情况可以得到位置关系

我们拓展到一般性的隐式表面,方法都是一样的

4.2 光线与显式表面求交

对于显式表面的渲染,光线如何与三角形求交是一个很重要的话题 在几何上,通过这个办法也可以判断一个点在不在物体内(点如果在封闭形状内,向外打一条光线,得到的交点数量一定是奇数) 话题回到光线与三角形求交 在下图的小奶牛中,判断光线是否与它相交 最最简单的做法就是把它的三角形面挨个判断一遍(每个三角形面都会有0个或者1个交点) 很直观但是很慢(之后会介绍加速方法)

怎么样做三角形和光线的交点呢?

三角形肯定在一个平面内,所以问题可以被分成两部分

光线是否和平面有交点

这个交点在不在三角形内部

平面的定义采用点法式的定义方式

即用一个平面上的点与平面上的法线,利用点乘为0的方式建立平面方程

我们把光线方程带入平面方程,解出来光线与平面的交点

再之后可以判断在不在三角形内部

但是人们想两步合成一步,也就是下面的方法

左边是光线上的点,右边是用重心坐标表示的三角形内的点

解法如下图

解出来之后要判断是否合理,首先 t 得是正的,并且b1 b2 b3都是非负的

##加速光线求交效率

5 轴对齐包围盒(AABB)的求交

我们与每一个三角形求交,可以找到最近的交点但是 计算次数 = 像素数×三角形数×弹射数 这样太慢了所以我们要对这个过程进行改进加速,方法之一是包围盒

Related Articles

继续阅读