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