« 一个读取3DS文件的类CLoad3DS浅析ⅡShadow Volume 阴影锥技术之探Ⅲ »

Shadow Volume 阴影锥技术之探Ⅱ

本文是ZwqXin -Shadow Volume 阴影锥技术之探Ⅰ的后续。xinxin将继续记录Shadow Volume的Z-PASS算法的实现之程。在上一篇中,我突破了Z-PASS算法的核心,利用一个简单的三角面为跳板,生成了对应的阴影。这个步骤当然要好好领悟Shadow Volume的原理。当然,Shadow Volume的难点不仅在此,更在于对多个三角面的阴影渲染。本篇记录的是这几天所做的,针对复杂模型的阴影生成。——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
      原文地址:http://www.zwqxin.cn/archives/opengl/shadow-volume-2.html

在上一篇中“画”阴影锥比较简单,因为在连接锥体各面所需要的“点”(三角面三个角的坐标)是已经知道的,所以一气呵成。但是,3D世界中的3D物件,除了这种自己定义的简单物件,还有模型。这些模型通常是由美术人员用建模工具塑造出来的,连他们自己也很难知道(或理会)模型中的“点”的位置,成千上万个顶点的模型是实在是普通至极。这些模型的格式也多种多样,活跃于3D世界的其中一种,正是之前提过的3DS格式模型。而我算得上比较熟悉的也是这种模型。前一阵子好好地了解了3DS文件结构一种简单导入方法(通过CLoad3DS类导入OPENGL),现在是应用的时候了!

与以前一样,我使用了CLoad3DS类,不过这次并不是直接用它在客户端载入和渲染模型,而是新建一个CShadow3DS并派生自它。(最初是直接在CLoad3DS上增加一些函数的,但最后还是不忍心,或者说被眼前混乱的结构吓坏了,马上作出移植的打算,希望这次的继承没有把事情搞糟。)我是这么想的:假设原本我有一个场景,要导入一个3DS模型:好的,我用CLoad3DS。而突然需求有点点改变,我希望导入一个具有阴影的3DS模型,于是就用了它的儿子CShadow3DS。CShadow3DS继承了CLoad3DS的一切,并且有自己的新特点(有阴影)。

针对模型的Shadow Volume生成,我觉得自己虽然说原理是懂了(比较好懂)但是至今不能说驾御了这种技术。如果没有NEHE 27#的帮助,恐怕自己是写不出这种算法。大概来说吧,我们首先要获得模型里面每一个三角面的3条边的相临的边,把其所在面作为该三角面的“邻面”(SetConnectivity,后面有用),在模型接受某个光源的照射的时候,往往只有一部分被照亮(想象一个人面朝朝阳,身体只有前侧受光,背部是没被太阳光照到的),我们处理这受光部分的面片,把外轮廓找出来,因为阴影锥面是光源与外轮廓线连接后延伸,其中延伸的那一部分形成的锥体。(想象一幅长方形的拼图,我们最后不关心每一个拼图小块的形状,我们只要关心这个拼图的整个形状——长方形就够了。)原理就是这样而已,我们就是要得到这个“外轮廓”上的模型顶点!它们跟上一篇中三角形的三个角点一样,勾画出最终阴影的形状。

问题是:1. 怎么知道模型哪部分被照亮?2.怎么把被照亮的那部分(可能由N个三角面片拼接而成)的外轮廓找出来(围成外轮廓的那些模型顶点)?首先要明白,光可以移动,模型更可以移动,这样光和模型的相对位置是可以变化的。这样的话可以假定每时刻模型被照亮的部分是会变化的(相应地,阴影锥跟着变化),因此要在每帧渲染时就做一次判断,判断哪些面向光,进而画出“该帧的Shadow Volume”。(你面朝大海,春暖花开,站个一天不动,朝阳拐个弧就变夕阳,而你身体被照亮的部分也跟着这个弧变化,由前亮逐渐变后亮,后来就是背部被太阳照着了。)

我们看看第一个问题。判断面片的向光性。当然,到达某面的光线向量与该面法向量做次点乘,单位化得两向量夹角,夹角绝对值小于90说明面片朝光,大于90则反之。面法向量容易得到(事实上CLoad3DS的顶点法向量计算过程中就计算过所有面的面法向量[顶点法向量是各个包含该点的面的面法向量均值]),但是到达该面的光线呢?我们容易得到光源和某点的连线向量,但无法直接得到光到平面的连线向量啊。于是采用另一种同样可行但比较简便的方法:把每个面片(Faces)转化为平面(Planes),然后想一想如果引一条光源点到该平面正面的垂线...一个正面不朝光源的平面,又怎能引垂线呢——这样不就判断出了?具体操作是:按照面片上各个点的绕序(索引顺序)能暗中确定出面片的正面和反面,因此根据绕序生成的该面的平面方程(a*x+b*y+c*z+d =0)具有正面反面;把光源坐标(xl,yl,zl)代入等号左边:如果(a*xl+b*yl+c*zl+d)等于0说明点在平面上,大于0说明在平面正面那侧,小于0说明在平面负面那侧——那我们取使结果大于0的那些平面(由a,b,c,d标识)对应的面片为向光面就可以了。问题是,这里的处理要让3DS模型的面片有这样一个性质才成立:看上去逆时针绕序的面为“正面”,顺时针绕序的面为“反面”;事实上这也是确保Shadow Volume的Z-PASS算法正确的重要假设!(我在上一篇中就担心这个问题了。)要弄清楚这个假设成立不成立,还得之后继续求证(此前查、问多次无果),但就最终结果来看似乎又反过来验证了这个假设,但毕竟只是独立随机事件.......

好了,看第2个问题:怎么把被照亮的那部分的外轮廓找出来?首先要明白,一旦找全那些面向光源的面,“外轮廓”就已经确定了是不?问题是要找出围成这个外轮廓上的每条边是哪个面片的边(也就是说,是哪个面片上的哪两个顶点构成的边)?还记得SetConnectivity(找每个面片的各3条邻边)这个最开始的步骤吗?用处就在这了:对每个面上每条边,有邻边者亡,无邻边者生!头脑还清晰了吗?呵呵。找邻边过程是这样的:遍历每个面,对比除这个面外的其他面,把它们的顶点两两比较,如果两顶点均一致,说明是共边的面——这样不是找到了邻边所在的“邻面”了吗?然后,在渲染Shadow Volume时,只对那些面向光源的面和那些无邻边的面作处理。

一个面不是有3个邻面吗?是的,所以每个面都要判断3次,每次不同地找其中两个点(譬如三角形ABC,3次分别找点A和B,B和C,C和A,注意这里又是一次逆时针查找,即A-B-C应该是逆时针绕序的),只要其中的1次发现某两点(譬如A和B)构成的边本来就无邻边,或者其邻边不朝光源(本来就说好只针对朝光面嘛,所以这时候也当作“无邻边”——有邻边者亡,无邻边者生),这两点就是我们要找的“外轮廓”上其中一边(A-B)的两模型顶点啦!一路遍历找下来就找到了“外轮廓”上所有顶点(一般来说每个点都被找除两次)。恰好,A-B是逆时针的,作为Shadow Volume(阴影锥)其中一个四边形侧面(silhouette edge)的一边,另外在“光源-A”和“光源-B”线的延长线各上找一点,这4点就构成阴影锥的一个侧面了!至于那延长线上哪个位置定点,就视乎你要阴影锥延伸多长了。(在本DEMO中,我取其长度为10*“光源-A”和10*“光源-B”。)另外,从这个步骤中也可以看出“面片逆时针绕序”假设的重要性。

好吧。理一下思路:我们把以上步骤结合阴影生成步骤,分成两类:初始化步骤和渲染实时步骤。

初始化步骤:ImportShadowModel()

  • 1.用CLoad3DS载入模型(ImportModel)
  • 2.把CLoad3DS中的Model结构,转化为CShadow3DS的ShadowModel结构(多了邻面,可见性等等属性,见下回分解
  • 3.找邻面(边)(SetConnectivity
  • 4.Face To Plane转换

渲染实时步骤:RenderShadowModel()

  • 1.转换光源到模型空间(所必要的逆矩阵操作)
  • 2.渲染原Model
  • 3.判断面的朝光性
  • 4.蒙板判断,画Shadow Volume
  • 5.渲染阴影


Q1:既然最后处理的都是朝光的面,为什么不针对朝光的面SetConnectivityFaceToPlane,而是一开始针对全部面片呢?A:很简单:邻边和平面转换都是一个模型每个面的固定属性和行为,不像可见性那样随时改变,故放在初始化这里,只要执行一次,整个程序周期可用,而如果放渲染部分就惨了:每次渲染都得作一次巨大的计算。

Q2:平面方程每次都一样?模型动一动,那个面所在平面不就变样了嘛?A:这里说的不变,说的是它在模型空间的不变。一个模型在其模型空间里永远就是刚载入进来的那个样儿。当然在视图空间(经过模型变换和照相机视觉变换后)模型可以动来动去,不再呆守原样。FaceToPlane失效了吗?这里我说:没有,反而是朝光性失效了。因为FaceToPlane联系的是面和光源的关系,光源位置是定义在视图空间的,如果面永远维持在模型空间时那个样儿,光源就也得映射到模型空间,才能得到正确的朝光性判断。毕竟,模型和光源的相对位置能确定就行,在哪个空间确定有什么关系呢 ?(比如模型相对光源左移1单位,跟光源相对模型右移1单位是等效的,其余类同。)你想想,是转换一个点(光源)简单,还是转换一个N个点(模型的各个面)简单?事实上整个模型在渲染时都来了视图空间了,你还再单独再从模型空间里抽出模型的每个面(faces)来一次视图空间,在每次渲染时转一次为平面(planes),把结果用来判断朝光性——这不是拿来搞吗?还是转换光源吧!看下回分解。

好吧。。。。一切一切,下回分解!
Shadow Volume 阴影锥技术之探Ⅲ

本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
      原文地址:http://www.zwqxin.cn/archives/opengl/shadow-volume-2.html

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2024 ZwqXin. All Rights Reserved. Theme edited from ipati.