LightMap 生成

来源:互联网 发布:linux awk 替换字符串 编辑:程序博客网 时间:2024/05/20 21:48

为了增加场景的表现力,在渲染中添加光照效果必不可少。首先用某种算法对整个场景生成光照渲染效果,然后再将光照渲染效果以纹理的方式来存储,将场景中各个多边形所对应的光照效果存于光照贴图中,这样的话在实时渲染时,只需要对场景在原有纹理渲染的基础上添加多纹理的融合即可渲染出光照效果。然而这样得到的光照贴图是和场景中每个单一多边形相对应的,因此若场景中有N个多边形就需要对应N个光照贴图,这样在渲染时就需要对每个多边形进行纹理绑定操作,受限于硬件的性质,这个操作是比较耗时,因此就影响了整个渲染的效率,于是就需要将每个多边形对应的光照贴图进行合并得到整个场景的光照贴图从而避免渲染时的纹理切换。于是,整个场景光照贴图的生成过程就包括:

     1.       场景带有光照的渲染

     2.       单个多边形光照纹理贴图的生成

     3.       整个场景中多边形光照纹理贴图的合并

 

场景带有光照的渲染

这个操作是下一步生成每个多边形的光照纹理贴图的关键。一般来说游戏中使用的预处理光照效果都是全局光照,包含阴影、漫射等这些常规渲染管线无法达到的效果。由于采用预处理来生成,故而也就需要光照算法与视点无关,这样才可以对整个场景生成光照效果。辐射度算法就比较符合上述特点,因此就常被用来生成场景的光照效果,而且得到的光照效果很容易与每个多边形进行映射得到光照纹理贴图。

 

多边形光照贴图的生成

单个多边形光照贴图的生成与辐射度算法的实现方式密切相关。辐射度算法是将场景进行网格细分,然后对每个很小的面片进行光照着色。因此对于输入的一个原始场景,在用辐射度算法对其进行渲染之后即成为网格细化后的另外一个场景,但是这两个场景之间很容易进行对应。在实现中,辐射度算法的预处理操作是用八叉树来对场景进行划分,根据这种划分原则就很容易将一个原始多边形与网格细分后的多个子多边形进行对应。

RadiosityGrid       RadiosityTexture

上图所示即为对一个原始多边形进行空间分割之后得到的多个子多边形,辐射度算法的操作单元即是这些子多边形。得到每个子多边形的着色效果之后再将多个子多边形进行合并即可得到原始多边形的光照着色纹理。

由于在场景分割时采用八叉树,而它的分割平面具有轴对齐的性质,利用此性质就很容易将子多形与原始多边形进行对应。将子多边形与原始多边形进行对应之后就需要由这样的对应关系来生成原始多边形的光照贴图。在辐射度算法生成全局光照时,由于其算法代价比较大,因此空间分割不会进行得太细,否则子多边形数量太多会导致全局光照生成时间消耗太大。这样的话在由子多边形合并生成原始多边形的纹理时需另外确定纹理贴图分割粒度,一般来说可以使光照贴图的分割精细程度大于场景分割的精细程度,这样在生成光照贴图时只需要额外的纹理颜色插值就可以得到很平滑的光照贴图。

 

SkinGrid

如上图中所示的,蓝色网格为多边形纹理的分格网格,它比辐射度算法中的场景分割、更细。

具体的光照纹理生成操作如下:

1.  将原始多边形和它的所有子多边形,根据其法向量旋转到某个轴对齐平面上(XoY , XoZ , YoZ),这样

   在判断纹理网格处的颜色值时就变成了退化为平面的AABB求交操作,更加高效。

2.  求出旋转后的原始多边形和子多边形的AABB包围盒(包围平面)。

3.  根据原始多边形包围矩形的大小及纹理的精度确定纹理网格的尺寸,并对包围矩形进行细分。

4.  对于纹理网格中的每小网格块,遍历整个子多边形列表并用此小块的包围矩形与子多边形的包围矩形

   进行求交,根据相交测试的结果来确定此小块的最终颜色值。此处可以根据插值的范围来确定需要进

   行求交断送的子多边形的数目。

5.  用纹理网格及各网格块的颜色来生成最终整个原始多边形的光照贴图。

6.  根据投影关系确定每个顶点的纹理坐标。

 

多边形光照纹理贴图的合并

在完成上述操作之后即可以得到每个多边形的纹理及多边形的纹理坐标。但由于场景中的原始多边形的大小不定,得到的原始多边形的光照贴图大小也不定,且其变化范围可能会很大,如果直接用这样的多个纹理进行场景渲染时效率较低,也不能够很好地利用硬件资源,因此就需要将各个多边形的纹理进行合并得到整个场景的光照纹理。

纹理合并问题其实可以描述为这样一个算法问题:给定N个大小不等的矩形,将其排放在一起,相互之间不能重叠,求一种排布方式,使其按这种方式排布后得到的总区域面积最小。这其实是一个NP问题,找到一个最优解比较困难,因而只能用近似于最优解的方法来代替最优解。采用贪心算法可以较好地解决这类NP问题。贪心算法的求解方法即是每次操作都使问题向着最优的方向进行,这样最终不一定能得到最优解,但往往很近似最优解。

将贪心算法应用到纹理合并操作中可以得到这样的一个操作原则:对于每个纹理,选择一个能产生最少空白区域的位置来进行放置。对于操作时使用的数据结构,结合三维场景的空间分割操作,也可以用一种树的结构来组织。对于每个待放置的纹理,应用贪心原则,对整棵树进行遍历来寻找一个最佳的位置然后放置并更新整棵树即可。

具体操作如下:

1.       统计所有的纹理面积,求出最终纹理的宽度(或高度)maxWidth

确定原则如下:maxWidth = max(所有纹理中的最大宽度 , sqrt(allArea))

2.       初始化一个空白纹理: height = 0 , width = maxWidth并生成维护它的二叉树结构。

3.       对于所有的纹理,根据其的高度进行排序,先处理高度大的,后处理高度小的。

4.       对于每个纹理,寻找其最佳的放置位置并更新树中相关结点的信息及整个树的结构。

 

EmptySkin                       PSTreeNode

纹理的填充                                                   树结点的结构

 

注意:由于此算法使用贪心算法,而且首先确定了最终纹理的最大宽度值(否则两个自由变量的增长,算法无法控制),因而贪心的原则就转变为使总纹理的高度值最小,因此将所有纹理按高度值进行排序就是必要的。否则得到的排布效果就比较差

 

 

NoHeightSorted                 HeightSorted

    末按高度排序                                                                         按高度排序

 

在完成上述操作之后即可得到合并出的整个场景的光照贴图:

 

PackedSkin

0 0
原创粉丝点击