以内外存交换方式实现基于点的全局光照效果的渲染方法
技术领域
本发明涉及图形学真实感渲染领域,具体涉及一种以内外存交换方式实现基于点的全局光照效果的渲染方法。
背景技术
在计算机体系结构的发展过程中,存储结构逐渐形成了一种类似于金字塔的结构。上层的存储部件,空间少,价格高,但速度快;下层的存储部件,空间大,价格低,但速度慢。通常,计算机内存处于“金字塔”的上层,空间小、速度快,外存处于“金字塔”下层,空间大、速度慢。优秀的内存和外存调度算法是解决计算机内存瓶颈的有效途径。
三维影视动画作品,带给观众置身于其中的真实感受。在作品的制作过程中,艺术家们不断追求场景中接近真实的光照效果。现实世界中很多物体大部分或者全部被其他物体反射的光照亮,这部分光照被称为间接光照。全局光照效果是指由直接光照和间接光照共同产生的光照效果,这种光照效果更加符合现实中的场景。比如人物皮肤的次表面散射效果,墙壁上的光影等。全局光照效果比直接光照展示的细节更丰富和真实。随着人们对真实感的追求,以及计算机中复杂场景模型的出现,渲染时间和计算机内存占用空间成为制约三维影视作品创作的瓶颈。如何在渲染的效果与时间上找到平衡点,从而加快制作周期,成为影视动画制作的关键问题。
Kajiya于1986年提出渲染方程[The rendering equation],描述原始光能传递方程。公式(1)描述了如何计算三维场景中某点x处的直接光照值和间接光照值。
Lr(x,wr)=Le(x,wr)+∫ΩLr(x′,-wi)f(x,wi,wr)cosθidwi 公式(1)
Le(x,wr)表示点x在方向wr上对外辐射的总能量,f(x,wi,wr)表示光线发生变化的方程,描述了光源处发射的光线与场景中的物体相互作用,θi表示入射光线与点x所在表面的夹角,是已知量;Lr(x′,-wi)表示从周围表面传递来的光照,是求解全局光照的关键;Lr(x,wr)为点x处的最终全局光照值。但此方程是无法通过直接求解得到全局光照结果。
Christensen在文章[Point-based approximate color bleeding]中提出基于点的全局光照方法。该方法分为两步进行,第一步生成点云文件,点云文件保存了带有直接光照的三维场景信息;第二步利用前面生成的点云文件计算全局光照效果。由于计算简单,该算法与传统的计算全局光照的算法相比,在时间及内存方面占有很大优势,被广泛应用于影视动画的制作中。但 随着大规模复杂场景的出现,所使用的点云文件会越来越大,是该算法的内存瓶颈。在电影《功夫熊猫2》的制作过程中,一个复杂场景生成的点云文件达到了88G。
为了在有限的内存空间,正确有效的进行全局光照的计算,Kontkanen在文章[Coherent Out-of-Core Point-Based Global Illumination]中提出了核外基于点的一致性全局光照渲染方法。采用特殊的方法来构建八叉树的数据结构,利用这样的数据结构组织点云文件,将三维空间中的点进行编码压缩到一维空间,以最少的内存将点云文件的数据组织成一棵八叉树,并采用分块的方法将八叉树存储在磁盘上,在渲染时采用类似于缓存的方法,对点云文件进行有效的内外存调度。
基于内外存调度的渲染方法成为将来渲染的必要选择,但这种渲染方式,需要大量的I/O操作。聚类点云数据时,传统的自顶向下的八叉树构建方式效率非常低。如何以最少的I/O操作正确构建八叉树,并提高内外存调度的命中率,成为提高渲染效率的关键。
发明内容
为解决现有技术存在的不足,本发明公开了以内外存交换方式实现基于点的全局光照效果的渲染方法,当渲染大规模场景时,生成中间文件的大小远远大于内存空间,参考Kontkanen提出的核外基于点的一致性全局光照效果渲染的理论方法,对该项功能进行扩充和实践,优化了渲染着色阶段的数据调度,使最终能够在有限的内存空间限制下,取得正确的全局光照渲染结果。
为实现上述目的,本发明的具体方案如下:
以内外存交换方式实现基于点的全局光照效果的渲染方法,包括以下步骤:
步骤一:利用已有直接光照的场景数据经过采样生成包含直接光照数据的点云文件,点云文件是由内存分块逐步写出到外存中储存;
步骤二:将步骤一中生成的点云文件进行Morton编码,使外存中存放的点云文件是有序的Morton编码;
步骤三:步骤二中Morton编码后的点云文件,其数据点三维空间的信息对应三维Z-curve曲线,以内外存交换的方式构建一棵八叉树,最后整棵点云树层次结构存储到外存文件中;
步骤四:利用步骤三中构建的八叉树来渲染带有全局光照效果的场景。
所述步骤一具体包括如下步骤:
步骤(1.1):使用的建模工具导出物体几何信息被细分成了小面片,利用渲染引擎渲染三维场景,场景中物体几何的小面片信息拥有了直接光照效果后对小面片进行采样,保存采样点的法向、面积、位置即取中心点以及直接光照值;
步骤(1.2):将保存到内存中的采样点信息存储到外存中,以点云文件的形式存放。
所述步骤二具体包括如下步骤:
步骤(2.1):首先将步骤一中生成的点云文件中的点进行Morton编码预处理,将点云文件中缓存的点的位置是浮点类型进行空间映射变换为正整数;
步骤(2.2):将点云坐标转换到正整数空间后,依次沿着x,y,z方向,转化为二进制表示形式,然后将三维数据转化为一维空间中二进制数据;
步骤(2.3):将点云数据转化为Morton编码后,对编码后的点云数据进行排序,得到有序的Morton编码。
所述步骤三中:得到Morton编码有序的点云文件后,三维空间的点对应三维Z-curve曲线,隐含了一棵空间八叉树,边读入有序的点,边通过深度优先遍历的方法自底向上构建八叉树。
所述步骤四包括以下步骤:
步骤(4.1):在进行渲染着色时,可利用多核处理器的多线程同时进行多个着色点的渲染着色;
步骤(4.2):在多个线程在遍历八叉树时,同时访问叶子结点和中间结点,由于只能加载八叉树的部分区域到内存中,叶子结点和中间结点被分别进行隐式分页处理,各个线程访问时,需要保证同一时刻只有一个线程能访问该内存页;
步骤(4.3):在内存的调度方式上,采用二级缓存的思想进行调度,即共享缓存之上每个线程有自己的局部缓存,当一个线程需要访问某一页,首先从其本身的局部缓存进行查找,如果该页没有找到,访问共享缓存进行查找,共享缓存或者直接提供查找的页,或者从磁盘中加载到共享缓存中,由于在着色时,访问的中间结点次数要多于叶子结点,所以将3/4的内存分配给中间结点,1/4的内存分配给叶子中的点。
所述步骤(2.1)中将点云文件中缓存的点的位置是浮点类型进行空间映射变换为正整数,具体为:根据三维场景按坐标轴对齐的包围盒,将点云中的点转换到正整数空间,在引擎构建场景树时,获取整个场景空间的包围盒,对x,y,z方向的数据均扩大221整数倍,使用64-bit的二进制数来表示点云文件中的点,变换方法如公式(2)所示:
coordinate[i]=(unsigned int)(p[i]-worldB min[i])/
(worldB max[i]-worldB min[i])*221 公式(2)
其中,p[i]是所要转换的浮点数坐标,i=0,1,2下标表示点位置的x、y、z坐标,wordB max和wordB min分别表示场景按坐标轴对齐包围盒的两个最大点和最小点, coordinate是映射得到的整数坐标。
所述步骤(2.3)中对编码后的点云数据进行排序,使用外部排序,采用一种N-路归并的方法,具体:首先将点数据划分为几个块,保证每块能完全加载到内存中,先将每一块中的Morton编码排序,最后将所有的块合并比较,进行整体排序,得到整体有序的Morton编码。
所述自底向上构建八叉树时,处理叶子结点,具体为:
首先加载8个点到一个队列中,队列具有先进先出的性质,从根节点进行深度优先遍历,递归分解八叉树,直到找到一个结点不包含队列中的所有点,随着递归分解八叉树的同时,父节点被压入栈中,为了找到满足条件的结点,只需要测试队列中的最后一个点是否在这个结点中,如果最后一个点在则队列中的所有点都在,此时需要进一步分解八叉树,始终从这棵树的左下角进行查找,因为当找到第一个不包含最后一个点的结点,这个点即为叶子结点,将叶子点写入磁盘文件中,并记录叶子结点中第一个点的索引信息和叶子结点中点的数量,一旦叶子点构建完成叶子结点中包含的点不会再被访问到,读入余下的点到处理队列中,继续访问该叶子点的兄弟,如果包含的点小于等于8个点,兄弟结点成为下一个叶子结点,否则继续递归分解八叉树,直到找到一个结点包含的队列中的点小于等于8个。
所述自底向上构建八叉树时,当处理完叶子结点,逐步向上递归,构建中间结点,根据其叶子结点进行聚类计算,计算点的位置、法向、颜色、面积信息的平均值,并将构建好的中间结点写入磁盘文件中,并将其直接孩子结点从内存中释放,当所有点被处理完时,所有的中间结点也被写入磁盘文件中。
所述步骤(4.1)中:控制是否继续向下遍历如公式(3)所示:
solidAngle=S2/distance 公式(3)
其中solidAngle表示阈值,S2是结点面积的平方,distance是结点位置到着色点位置的距离。
如果solidAngle小于等某个阈值,则判断该八叉树结点的光照能否传递到着色点上,如果能,则将其光照值累加到着色点的全局光照值中,否则忽略,solidAngle大于某个阈值时,需要继续向下遍历八叉树,遍历新的结点,重复此项判断,直到叶子结点,叶子结点直接判断该结点是否能传递到着色点,如果能,则将其光照值累加到着色点的全局光照值中,否则忽略它。
本发明的有益效果:
1.本发明提出了内外存交换方式实现基于点的全局光照效果渲染的具体实施方法。当渲 染大规模场景时,生成中间文件的大小远远大于内存空间,参考Kontkanen提出的核外基于点的一致性全局光照效果渲染的理论方法,对其进行改进、扩充和实践,优化了渲染着色阶段的数据调度,使最终能够在有限的内存空间限制下,取得正确的全局光照渲染结果。
2.本发明提出的内外存调度策略和方法能够有效的提升内外存交换实现基于点的全局光照效果的渲染速度。
具体实施方式:
下面对本发明进行详细说明:
采用大规模点云进行全局光照渲染时,内外存之间的互相交换是渲染速度的瓶颈。整个渲染周期中,内外存数据的调度可分为三个阶段。首先,第一阶段的内外存交换数据发生在第一步生成点云文件,从内存中将点的数据以分块的形式写出到外存的文件中。
第二阶段的内外存交换发生在生成点云文件后,将点云数据从外存读入到内存中,点数量很大时,构建八叉树过程中内外存之间的交换非常耗时,如何尽可能的减少内外存的交换,成为我们优先需要考虑的问题。由于有序的Morton编码能映射到空间Z-curve曲线,而Z-curve曲线又隐含了一颗八叉树,可以实现一边从外存的点云文件读入数据到内存,一边自底向上在内存中构建八叉树的方法。并在构建树的过程中,将这棵八叉树的叶子结点和中间结点分别隐含分页的形式换到磁盘文件中,及时释放暂时不用的内存。
最后一阶段内外存交换发生在利用点云文件计算全局光照。进行全局光照渲染时,需要计算着色点的全局光照值。全局光照值存放在前一步构建好的八叉树中,按照树节点与着色点间的距离和树结点代表的面积这两者作为影响因子,调整八叉树的遍历程度。考虑到整个内存大小是一定的,构建好的八叉树不能全部放入到内存中。而八叉树的中间结点比叶子结点的访问量大,我们将内存的3/4分配给中间结点,1/4分配给叶子结,按照最近最少使用的调度策略,即:把最近一段时间使用次数最少的结点切换到外存中,把需要的结点换入到内存中。
一种以内外存交换方式实现基于点的全局光照效果的渲染方法包括以下几个步骤:
步骤(1):基于点的全局光照效果渲染方法分两步进行,第一步是利用已有直接光照的场景数据经过采样生成包含直接光照数据的点云文件。第二步根据点云文件进行全局光照效果的计算。由于点云文件是由内存分块逐步写出到外存中储存。
步骤(2):将步骤(1)中生成好的点云文件进行Morton编码;
步骤(3):将步骤(2)中Morton编码后的点云数据对应三维Z-curve曲线,构建一棵八叉树,存储到外存(如磁盘)文件中;
步骤(4):利用步骤(3)中构建的八叉树来渲染带有全局光照效果的场景。
所述步骤(1)包括如下步骤:
步骤(1.1):利用渲染引擎渲染三维场景,场景中物体几何的小面片信息拥有了直接光照效果后(这里要求所使用的建模工具导出的物体几何信息被细分成了小面片),对小面片进行采样,保存采样点的法向、面积、位置(取中心点)以及直接光照值。
步骤(1.2):将保存到内存中的采样点信息存储到外存中,以点云文件的形式存放。
所述步骤(2)包括如下步骤:
步骤(2.1):Morton编码:首先将步骤(1)中生成的点云文件中的点进行Morton编码预处理,Morton编码的对象需要是正整数,点云文件中缓存的点的位置是浮点类型,需要进行空间映射。根据场景的包围盒,将点云中的点转换到正整数空间。在引擎构建场景树时,获取整个场景空间的包围盒。对x,y,z方向的数据均扩大221整数倍,使用64-bit的二进制数来表示点云文件中的点,变换方法如公式(2)所示:
coordinate[i]=(unsigned int)(p[i]-worldB min[i])/
(worldB max[i]-worldB min[i])*221 公式(2)
其中,p[i]是所要转换的浮点数坐标,i=0,1,2下标表示点位置的x、y、z坐标,wordB max和wordB min分别表示场景按坐标轴对齐包围盒的两个最大点和最小点,coordinate是映射得到的整数坐标。
步骤(2.2):将点云坐标转换到正整数空间后,依次沿着x,y,z方向,转化为二进制表示形式,然后将三维数据转化为一维空间中64bit长度的二进制数据。转化方式以2-bit表示的点(X1X0,Y1Y0,Z1Z0),转换为6-bit表示的Morton编码为例,结果是一个二进制整数:Z1Y1X1Z0Y0X0。
步骤(2.3):将点云数据转化为Morton编码后,对编码后的点云数据进行排序,得到有序的Morton编码。但在处理大规模点云数据时,内存通常不够,所以我们需要使用外部排序。这里采用一种N-路归并的方法。首先将点数据划分为几个块,保证每块能完全加载到内存中,先将每一块中的Morton编码排序,最后将所有的块合并比较,进行整体排序,得到整体有序的Morton编码。
所述步骤(3)包括如下步骤:
步骤(3.1):得到Morton编码有序的点云文件后,三维空间的点对应三维Z-curve曲线,隐含了一棵空间八叉树,我们边读入有序的点,边通过深度优先遍历的方法自底向上构建八 叉树。首先加载8个点到一个队列(队列具有先进先出的性质)中,从根节点进行深度优先遍历,递归分解八叉树,直到找到一个结点不包含队列中的所有点,随着递归分解八叉树的同时,父节点被压入栈中。为了找到满足条件的结点,只需要测试队列中的最后一个点是否在这个结点中,如果最后一个点在则队列中的所有点都在,此时需要进一步分解八叉树,始终从这棵树的左下角进行查找,因为当找到第一个不包含最后一个点的结点,这个点即为叶子结点。将叶子点写入磁盘文件中,并记录叶子结点中第一个点的索引信息和叶子结点中点的数量。一旦叶子点构建完成叶子结点中包含的点不会再被访问到。读入余下的点到处理队列中,继续访问该叶子点的兄弟,如果包含的点小于等于8个点,兄弟结点成为下一个叶子结点。否则继续递归分解八叉树,直到找到一个结点包含的队列中的点小于等于8个。
步骤(3.2)当处理完叶子结点,逐步向上递归,构建中间结点,根据其叶子结点进行聚类计算,计算点的位置、法向、颜色、面积等信息的平均值,并将构建好的中间结点写入磁盘文件中,并将其孩子结点从内存中释放。当所有点被处理完时,所有的中间结点也被写入磁盘文件中。只需按Morton编码的顺序访问点一遍,便可按照深度优先遍历的方式,自底向上后序构建起八叉树。
所述步骤(4)包括如下步骤:
步骤(4.1):在进行渲染着色时,可利用多核处理器的多线程同时进行多个着色点的渲染着色。根据着色点与八叉树中各个结点距离distance和结点面积S作为影响因子,控制遍历八叉树的遍历深度。控制是否继续向下遍历如公式(3)所示:
solidAngle=S2/distance 公式(3)
其中solidAngle表示阈值,S2是结点面积的平方,distance是结点位置到着色点位置的距离;
如果solidAngle小于等某个阈值(如0.05),则判断该八叉树结点的光照能否传递到着色点上,如果能,则将其光照值累加到着色点的全局光照值中,否则忽略。solidAngle大于某个阈值时,我们需要继续向下遍历八叉树,遍历新的结点,重复此项判断,直到叶结点。叶子结点直接判断该结点是否能传递到着色点,如果能,则将其光照值累加到着色点的全局光照值中,否则忽略它。
步骤(4.2):在多个线程在遍历八叉树时,同时访问叶子结点和中间结点,由于只能加载八叉树的部分区域到内存中,叶子结点和中间结点被分别进行隐式分页处理。各个线程访问时,需要保证同一时刻只有一个线程能访问该内存页。
步骤(4.3):在内存的调度方式上,我们采用二级缓存的思想进行调度,即共享缓存之上每个线程有自己的局部缓存。当一个线程需要访问某一页,首先从其本身的局部缓存进行查找,如果该页没有找到,访问共享缓存进行查找,共享缓存或者直接提供查找的页,或者从磁盘中加载到共享缓存中,由于在着色时,访问的中间结点次数要多于叶子结点,所以将3/4的内存分配给中间结点,1/4的内存分配给叶子中的点,将内存平均分为两部分,一半分给共享缓存,另一半平均分给每个线程。
考虑到整个内存大小是一定的,如果分出一半作为共享缓存,每个局部线程的缓存大小会减少,而且每个线程缓存之间不能互相访问,内存资源没有被充分利用。我们将内存的3/4分配给中间结点,1/4分配给叶子中的点,根据最近最少使用者换出的调度策略(LRU),进行内外存调度。在对叶结点缓存及中间结点缓存加锁时,只对当前需要换入换出的内存页面进行加锁。记录每个内存页面当前访问的线程数,当没有线程访问且符合LRU策略时,将该页换出。