课堂笔记_阴影生成

时间:2024-03-27 07:20:32

阴影

阴影是在构建真实感图像时的一个十分重要的元素,它可以为物体的位置和摆放提供视觉上的提示

课堂笔记_阴影生成

定义:考虑一个由光源L照明的场景,场景中的每个物体作为接收者,都有可能被光源L照明到。但是场景中有一点P为本影,即从P点不能看到光源L的任何一部分。如果从P点可以看到光源L的某一部分,却不能看到全部,那么P称为半影本影和半影统称为阴影,对阴影中的任一点,光源中至少有一点会被遮挡。挡住由光源大阴影区域的物体被称为遮挡物。

阴影的类型

附着阴影:

发生于接收者的法向背离光源方向时。

投射阴影:

发生于接收者的法向朝着光源方向,但光源被遮挡物所遮挡时。

自阴影:

是一类特殊的投射阴影;对于自投影,接收者和遮挡物来自于同一物体。

阴影的重要性

阴影能够帮助我们理解场景中物体的相对位置及大小关系
阴影能够帮助我们理解复杂接收者的几何形状
课堂笔记_阴影生成
阴影能够帮助我们理解复杂遮挡物的几何形状
课堂笔记_阴影生成

硬阴影和软阴影

课堂笔记_阴影生成
课堂笔记_阴影生成
通常会将“阴影”理解为一个二值的状态:每个点要么在阴影中,要么不在。这样的理解对应的其实是硬阴影,硬阴影由点光源产生

然而,点光源在实际中并不存在,并且硬阴影会给图像带来一种相当不真实的感觉。注意到,即使是我们日常生活中最常见的光源:太阳,也具有很显著的角展,产生的也并不是硬阴影。尽管如此,由于点光源在图形学中容易被模拟,对硬阴影的计算存在不少实时算法。

1.平面阴影绘制

当物体的阴影被投射到平的表面时,会产生平面阴影。平面阴影是一种最简单的阴影。阴影投影算法:在阴影投影方法中,三维空间中的物体需要被绘制两次以实现阴影效果。具体来说,是使用一个矩阵将遮挡物表面的点投影到需要计算阴影的平面上。
课堂笔记_阴影生成
课堂笔记_阴影生成
课堂笔记_阴影生成
课堂笔记_阴影生成
为了绘制阴影,可以简单地利用以上矩阵将三维场景中的物体投影到目标平面上,然后对投影物体使用暗色并去除光照进行绘制。

阴影投影算法的局限性
接收者必须是平面;
每一帧的阴影需要重新绘制,即使这些阴影没有变化;
课堂笔记_阴影生成

2.曲面上的阴影

要将前面平面阴影的计算方法扩展到曲面上,一个自然的想法是:使用阴影图像作为投影的纹理。试想,将光源作为视点。从光源看得到的区域,就被绘制;从光源看不到的区域,就是阴影。

阴影纹理技术:从光源出发进行绘制,将遮挡物绘制成黑白场景。在白色的背景上,所有的遮挡物都被绘制成黑色。这样可以获得一张阴影的纹理图。这张纹理被投影到需要绘制阴影的曲面之上,于是,在曲面上的每一点都可以计算一个其纹理图上的UV坐标,而利用此坐标,则可以直接判定该点是否属于阴影区域。
课堂笔记_阴影生成
但是这种方法不能做自投影。

阴影的绘制算法

1.阴影域(Shadow Volume,体阴影)算法:

由Cow提出,可以将阴影投射到任何物体表面。SIGGRAPH 1977,整个八九十年代,阴影是计算机图形学的主题。
课堂笔记_阴影生成
课堂笔记_阴影生成
假设视点在所有的shadow volume之外,我们维护一个计数器,其初始值是零。每次当从视点射向目标像素的射线进入到一个shadow volume中时,将计数器+1;而当射线从一个shadow volume中射出时,将计数器-1;这样,我们只需检验当射线到达交点时,计数器是否大于零:如果大于零,则交点位于阴影中;否则不属于阴影区域。

使用模板缓存
像上边提到的用几何算法去求交是很麻烦的,会使用硬件中的模板缓存。模板缓存可以对每个像素存储一个整数值;它与深度缓存z-buffer的区别在于z-buffer存储的是实数的深度值。

下图中,点光源向物体投射光线,有三个障碍物,三对前向后向面,在视点方向有屏幕像素表示。
课堂笔记_阴影生成

使用模板缓存阴影绘制
第一步:首先,清空模板缓存;
第二步:然后,将整个场景绘制到帧缓存中,这次绘制只使用环境分量和发光分量,并获取相应的颜色信息(在color buffer中)及深度信息(在z-buffer中);
第三步:关闭颜色缓存的写入和深度检测,绘制所有shadow volume的正面(即射线射入shadow volume时相交的面):在这个过程中,如果一个像素其深度值小于之前算好的z-buffer中的深度值,那么将这个像素的模板缓存的计数器+1;
第四步:类似于前一步骤,将所有shadow volume的反面绘制一遍,只是这是每发现一个像素其深度值小于之前算好的z-buffer中的深度值,将该像素的模板缓存上的计数器-1;
第五步:再将整个场景根据模板缓存的信息绘制上漫反射分量和高光分量:只有模板缓存是0的像素才绘制,以实现阴影效果。

阴影域算法优点
1.它可以使用通用的图形学硬件实现,而仅仅需要一个模板缓存
2.它不是基于图像的方法(不想下面将要介绍的shadow map算法),shadow volume算法并不会产生由采样和分辨率带来的各种问题,从而可以在任何地方生成正确和清晰的阴影。
阴影域算法缺点:主要体现在性能方面。
由于shadow volume的多边形面片数通常都比较多,且会覆盖住大量的像素,这在很大程度上影响了算法运行和光栅化过程的速度,使得绘制的效率较低。

2.阴影图(Shadow Map)算法:

1978年,Williams提出了一个基于z-buffer的通用的算法,对于任意的场景物体快速地计算阴影。Casting curved shadows on curved sufaces,ACM SIGGRAPH 1978。这个算法的思想是:以光源作为视点,使用z-buffer算法绘制场景,获得阴影图,并将其结果用于任意场景的阴影绘制。

1.使用深度缓存z-Buffer,可以获得从光源出发到任意一个方向最近点的距离,并以图像的形式存储下来,如下图。我们将这张深度图像称为阴影图。
课堂笔记_阴影生成
2.使用阴影图可以在任意视点位置对场景进行带阴影效果的第二次绘制。在进行绘制时,对于沿视点向屏幕每个像素发出的光线与场景中物体的交点,使用阴影图判断该点是否位于阴影区域:

如果该交点到光源的距离大于阴影图上对应点所存储的深度值,则该点位于阴影中。
课堂笔记_阴影生成
课堂笔记_阴影生成
在示意图中,阴影图存储了由光源到场景中物体表面的深度信息;右图中,由视点Va和Vb这两个位置进行观察:

对于Va:Va在阴影图上的纹理坐标为a,而存储在a的阴影图的深度信息*不小于*Va到光源的距离,因此Va不属于阴影区域;
对于Vb:Vb在阴影图上的纹理坐标为b,而存储在b的阴影图的深度信息*大于*Vb到光源的距离,因此Vb属于阴影区域。

阴影图算法的优点:
1.绝大部分的硬件都可以直接支持阴影图算法,以用于绘制任意场景的阴影效果。
2.阴影图算法很快,阴影图的构建开销与要绘制的基元数目成正比;而使用阴影图进行任意视点的阴影绘制时,每个像素只需要额外对阴影图进行一次查询,以及一次距离的比较,这可以在常数时间内完成。
阴影图算法的不足:
1.由于阴影图算法是基于图像的算法,因此其绘制质量会受到阴影图的分辨率、以及z-buffer的数值精度的影响。
2.当距离比较时所用的epilson值太小时,可能会在物体表面产生莫尔干涉条纹。当距离比较时所用的epilson值太大时,会使得阴影形状发生变形,并产生一种类似于物体漂浮的感觉。

3.两种算法总结

阴影图和阴影域算法是最为广泛使用的阴影生成与绘制算法(其中阴影图算法使用更为广泛)。后续算法都是对其的改进。

更多信息参考:http://www.realtimerendering.com/