一种融合GPU与CPU计算辐射度光照的方法
技术领域
本发明属于计算机图形技术领域,涉及通过渲染技术对场景进行光照处理,为一种融合GPU与CPU计算辐射度光照的方法。
背景技术
二十世纪中后期,随着真实感图形学的出现,标志着计算机图形的发展进入了新的阶段。对场景进行光照处理的方法大致可以分为两大类:直接照明和全局照明。1980年Whitted提出了光线跟踪模型,并且具体给出了算法的实例,该模型成功的利用跟踪光线的传播路径来解决光照问题;1984年,美国和日本的学者成功地在计算机图形学中加入了热辐射工程中的辐射度思想,通过计算环境中整体能量传递来模拟光照效果,并且利用这种思想成功的模拟出了在纯漫反射环境下光照的多重漫反射效果。这两种算法的提出与应用,标志着真实感图形的生成技术已经逐渐的成熟起来。
计算机图形学中有三种主流的渲染技术:z缓冲技术、光线跟踪技术和辐射度技术:
1)z缓冲技术的基本思想是:把三维场景通过透视矩阵进行投影到视平面,并在视平面上对其光栅化处理。然而这并不是基于真实的光照模型,图像表现不太真实。并且它本身是衍生于一种局部光照模型的,因此从算法理论讲场景中物体的相互反射和折射等现象是无法表现出的。更不用说表现焦散、衍射等高级光照效果。
2)光线跟踪技术能产生Gouraud和Phong等局部光照模型很难产生的光照效果,如阴影、折射、反射和运动模糊等类似的真实感效果。但光线跟踪算法很难模拟景物的多重漫反射效果,从而不能模拟景物之间的互相色彩辉映等类似现象。
3)辐射度技术:辐射度算法与光线跟踪法不同,它由物理学中的总能量守衡原理推的,通过采用数值求解的方法来近似计算物体表面的辐射度值,这就使得辐射度具有视点无关性。这一特点也让辐射度算法特别适合于虚拟场景中的漫游。通过把场景看成一个封闭***,***内的景物表面均是理想漫反射表面,再由能量平衡原理计算景物表面的辐射度分布,由于把封闭***中各个物体表面的能量传递考虑在内,所以会从整体上计算出环境中的光照分布。因此,辐射度算法和上述提到的算法不同,它是一种整体求解光照值的技术。但是对于一个复杂场景来说,使用辐射度算法来渲染整个场景会花费很长时间,这也给算法带来了很多不利的因素。
正如前面对现有技术的介绍,现阶段生成真实感图像最重要的光照模型是辐射度这一全局光照模型。它可以计算物体之间的漫反射,具有视点无关性,为虚拟漫游提供了快速的显示。设Bi为在单位时间单位面积内离开第i个面片的能量,它是发射和反射的能量之和:这是根据其他所有面片j的辐射度值得到面片i的辐射度值。
其中:dAi为第i个面片的面积;
Ei为第i个面片的自发射光值;
为其余面片j(j≠i)传递到第i个面片的总光能;
ρi为第i个面片的反射率(在0~1之间);
为面片i与j的形状因子,式中θi和θj为面片i与j之间的连线与各自法线的夹角;r为两面片之间的距离;Hij为两面片间的遮挡情况:若二者连线与场景中其它面片不相交则Hij=1,否则Hij=0;由此可以推出形状因子的互换关系FijAi=FjiAj。
在实际应用中通常要将环境各表面离散化为单位面积的面元,并由于FijAi=FjiAj,将等式两边的dAi约掉,则上式可以转化为:对于一个封闭的环境中,每个面片都有一个这样的方程,那么对于整个环境来说,就产生了n个这样的方程:
传统Gauss-Seidel求解方法中某一行的辐射度方程反应了单独一个面片i的辐射度,这是根据其他所有面片j的辐射度值得到面片i的辐射度值,这个过程称为“吸收”(gathering)。这个方程的意思是:对于面片i,逐一访问场景中的其他所有面片,根据形状因子关系,从面片j传递适当量的光线给面片i。以行为单位,逐行处理,把这个矩阵都处理一遍后才更新所有的解。
Cohen等人对传统Gauss-Seidel算法做了一个重要的然而很巧妙的改变,使得无需实时计算全部形状因子,改变了Gauss-Seidel迭代法的执行顺序,提出了逐步求精辐射度算法,它可以做到即时地计算形状因子,避免了大量的存储要求。下面介绍所述逐步求精辐射度算法。以单独一项表示了面片i对面片j的辐射度的贡献:
Bj依赖于Bi=ρjBiFji
利用它们之间关于形状因子的等式FijAi=FjiAj,上式可以变为:
Bj依赖于
这个关系对所有的面片j都成立,它可以用于决定面片i对环境中每个面片j的贡献,单个辐射源(面片i)向环境中发出光线,每一个面片j的辐射度同时更新。因此只要计算出第一列的形状因子就可以得到整个场景的第一个近似值。这样就可以消除很高的启动和预计算开销。在逐步求精辐射度算法中,面片的辐射度同时更新,即每个面片i的能量是发射到其他所有面片上,根据每一面片对场景辐射度的贡献,对所有面片进行排序并顺序处理每一面片。开始时所有面片的辐射度设置为0或者它们的非零自发射值。在这个计算迭代过程中,每一步所有面片的辐射度都被更新。随着求解的进行,面片辐射度的估计值会越来越精确,逐步迭代直至收敛到精确解。
传统辐射度光照算法构造和求解了大量含有成对形状因子的线性方程组,这些方程组描述了每个面片的辐射度光照值是由其它面片传递给该面片总能量的函数,权重是形状因子和面片的反射率。因此方程求解的复杂度是O(N2),N是整个场景被分割成的面片的数目,这需要很大的计算量和存储空间。虽然Cohen等人提出了逐步求精辐射度算法,它可以做到即时地计算形状因子,避免了大量的存储要求,但是对于复杂场景,逐步迭代计算次数与复杂度成正比,整个计算过程仍然非常耗时。
发明内容
本发明要解决的问题是:现有的逐步求精辐射度光照算法对于复杂场景来说,光照渲染的计算仍然是非常耗时的,并不能达到实时计算。而且如果为了得到比较好的光照质量,需要对场景的光照计算求解很多次,这又进一步增加了计算时间。
本发明的技术方案为:一种融合GPU与CPU计算辐射度光照的方法,在计算机图形光照渲染中,采用逐步求精辐射度光照算法对光照辐射度进行迭代计算,采用GPU来进行辐射度迭代计算,GPU对场景中的多边形进行光栅化处理,得到片段,包括以下步骤:
1)初始化场景中多边形的辐射度E,选择其中具有最大辐射度的多边形作为发射者;
2)以发射者的视角,把每个多边形的ID作为颜色通过GPU进行第一次渲染,并用顶点着色器进行半球投影,另外在CPU中创建帧缓冲区A,将第一次渲染后可见的多边形的片段ID存储到所述帧缓冲区A的纹理中,同时把多边形的光照贴图中每个像素的坐标同样存储在纹理中;
3)以发射者的视角,以每个多边形的ID作为颜色进行第二次渲染,判断第二次渲染后的片段ID是否在第一次渲染得到的纹理中,如果在则片段可见,计算可见片段的形状因子FF,再计算接收到的辐射度△E=ρ*FF*E,ρ为多边形的反射率,同时在CPU中创建帧缓冲区B,将计算得到的辐射度△E存储到帧缓冲区B的渲染缓冲区对象中;如果第二次渲染后的片段ID不在第一次渲染得到的纹理中,则不可见,不可见片段接收辐射度值为0;
4)根据第一次渲染得到的纹理中的每个像素的坐标值和第二次渲染得到的渲染缓冲区对象中的辐射度值,来更新每个多边形的光照贴图,完成一次逐步求精辐射度算法的求解;然后将发射者的光照贴图辐射度清零,计算此时场景中具有最大辐射度值的多边形,作为为下一个发射者;
5)根据步骤4)得到的下一个发射者,重复步骤2)-4),直至计算得到的场景中最大辐射度值低于设定的阈值时,停止迭代计算,最近一次得到的解即为每个多边形精确辐射度值,完成光照渲染。
步骤2)所述第一次渲染为可见遍渲染,从发射者的视角来离线渲染场景,把场景中每个多边形的ID作为颜色来进行渲染,利用可编程顶点着色器把顶点投影到一个半球体上,最终把结果渲染到纹理中存储;同时把每个多边形光照贴图中像素的的坐标写入纹理中存储,纹理中的每个单元存储的即为对当前发射者可见的多边形的ID和像素的的坐标,包括以下步骤:
11)创建一个空的纹理texture,用来存储接下来执行离屏渲染的结果;
12)创建帧缓冲区A,所述帧缓冲区A用于实现离屏渲染,即用于把当前一次的渲染结果存储到创建的纹理texture中作为临时变量存储;
13)在顶点着色器中创建执行半球投影的着色器程序,并对着色器中用到的一致变量进行初始化赋值,用它代替OpenGL固定管线上顶点处理操作;
14)渲染场景:把场景中每个多边形的ID作为颜色来进行渲染,并将渲染结果以及多边形的光照贴图中每个像素的坐标存储到纹理texture。
步骤3)所述第二次渲染为重建遍渲染,创建帧缓存区B,并且为其附加渲染缓冲区对象,通过离屏渲染操作,用渲染缓冲区对象来存储辐射度计算过程中每个片段的辐射度光照值,包括以下步骤:
21)在CPU中创建帧缓冲区B,为其附加连接渲染缓冲区对象,渲染缓冲区对象是一个数据存储区,存储的是片段着色器计算完每个片段得到的辐射度值,对应于每个像素的辐射度值;
22)CPU为创建的渲染缓冲区对象分配存储空间并指定图像格式,其中存储空间的大小与第一次渲染的纹理大小一样,使得光照贴图纹理中的像素与帧缓存区B的每个片段建立一一对应的关系,用于把每个多边形计算得到的辐射度值从帧缓存区B中重新复制到多边形的纹理中,为下一次迭代求解准备,一旦CPU为渲染缓冲区对象创建了存储空间后,就可以把渲染缓冲区对象附加到创建的帧缓冲区B上,然后向帧缓冲区B进行渲染;
23)通过GPU以每个多边形的ID作为颜色进行第二次渲染,在片段着色器中对光栅化后的每个片段进行可见性判断,通过在片段着色器中读取第一次渲染得到的帧缓冲区A的纹理,来判断当前片段是否在该纹理中,若在则对发射者可见,计算可见片段的形状因子值,进而求出该片段接收到的辐射度,并把结果存储到渲染缓冲区对象中;
24)从帧缓冲区A和帧缓冲区B读取第一次渲染得到的纹理中存储的像素坐标数据和第二次渲染得到的渲染缓冲区对象中的每个像素的辐射度值,并把所述坐标数据和辐射度值更新到每个多边形自身的光照贴图中,然后对于发射者的光照贴图纹理值清零,这样就得到一次逐步求精辐射度算法的解。
进一步的,步骤5)中,得到每个多边形精确辐射度值后,把此时每个多边形对应的光照贴图进行纹理打包,然后把整个场景渲染到窗口***提供的帧缓冲区中用于显示。
现有的逐步求精辐射度光照算法对于复杂场景来说,光照渲染的计算仍然是非常耗时的,并不能达到实时计算。而且如果为了得到比较好的光照质量,需要对场景的光照计算求解很多次,这又进一步增加了计算时间。
本发明通过在GPU中来实现逐步求精辐射度算法,充分利用了GPU本身具有的强大计算能力来加快辐射度光照求解的计算,减少渲染时间。为了利用GPU本身具有的强大计算能力来进行图形计算,必须对它的结构有一定的了解,把GPU应用于通用计算问题的最大困难是它们的设计非常专用化,而且GPU计算中的一些结果数据通常需要反馈到CPU中或者在GPU的下一阶段计算中用到,而GPU本身对显存是没有任何间接写指令的。为了解决这些问题,本发明通过CPU建立自己的帧缓冲区,并为其附加相应的纹理对象和渲染缓冲区对象,通过采用离屏渲染操作,把GPU计算过程中的中间结果采用渲染到纹理的技术来存储,这样既可以在CPU中通过图形API来对GPU计算后的结果进行相应的操作,也可以在接下来的GPU计算中读取该数据。在此基础上,本发明提出了两次渲染的方法,通过上述改进方法,本发明对于简单场景的光照计算可以达到交互的速度,对复杂场景的光照计算可以极大的提高计算速度,减少光照渲染的时间,并且同时取得了良好的光照效果。
附图说明
图1采用基于GPU硬件加速的简单场景逐步求精辐射度光照效果。
图2采用基于GPU硬件加速的复杂场景逐步求精辐射度光照效果。
具体实施方式
传统辐射度光照算法构造和求解了大量含有成对形状因子的线性方程组,这些方程组描述了每个面片的辐射度光照值是由其它面片传递给该面片总能量的函数,权重是形状因子和面片的反射率。因此方程求解的复杂度是O(N2),N是整个场景被分割成的面片的数目,这需要很大的计算量和存储空间。随后Cohen等人提出了逐步求精辐射度算法,它可以做到即时地计算形状因子,避免了大量的存储要求,但是对于复杂场景它的整个计算过程仍然非常耗时。
为了从根本上加速辐射度算法的求解和改善光照的效果,本发明采用了基于GPU硬件加速来实现逐步求精辐射度光照算法,该方法利用可编程图形处理单元GPU的并行计算能力,将辐射度算法中面片可见性的判断和形状因子的计算完全在可编程图形硬件中完成。在片段着色器程序实现形状因子的计算和每个面片辐射度的求解。通过应用程序创建自己的帧缓冲区对象,利用离屏渲染、纹理缓存等技术来进行渲染和中间结果存储。该方法对于简单场景可以达到交互的速度,对于复杂场景会大大提高其计算速度,改进其光照渲染的效果。
本发明采用GPU来实现逐步求精辐射度光照算法,首先要从发射者的视角来渲染场景,然后把能量散射到所有其他可见多边形的纹理中,这是常规的方法,其性能很差,这个方法的问题是需要GPU写入多个任意纹理的任意位置,这种散布操作是当前的图形硬件很难做到的。本发明为此提出了两次渲染的方法,可见遍和重建遍,利用GPU对可能接收光照辐射的多边形进行迭代运算,并测试每个多边形片段的可见性,利用GPU的并行计算能力对许多片段并行执行相同的一小段片段程序,即不是散射能量到可见片段,而是反之计算片段的可见。该方法在快速收敛的情况下既保证了接受多边形辐射度的高质量重建,又保证了发射辐射度的低存储量要求。
GPU对场景中的多边形进行光栅化处理,得到片段,本发明进行以下步骤:
1)初始化场景中多边形的辐射度E,选择其中具有最大辐射度的多边形作为发射者;这里的初始化指辐射度根据场景的环境光进行一个初始设定,用于后续的迭代计算;
2)以发射者的视角,把每个多边形的ID作为颜色通过GPU进行第一次渲染,并用顶点着色器进行半球投影,另外在CPU中创建帧缓冲区A,将第一次渲染后可见的多边形的片段ID存储到所述帧缓冲区A的纹理中,同时把多边形的光照贴图中每个像素的坐标同样存储在纹理中;
3)以发射者的视角,以每个多边形的ID作为颜色进行第二次渲染,判断第二次渲染后的片段ID是否在第一次渲染得到的纹理中,如果在则片段可见,计算可见片段的形状因子FF,再计算接收到的辐射度△E=ρ*FF*E,ρ为多边形的反射率,同时在CPU中创建帧缓冲区B,将计算得到的辐射度△E存储到帧缓冲区B的渲染缓冲区对象中;如果第二次渲染后的片段ID不在第一次渲染得到的纹理中,则不可见,不可见片段接收辐射度值为0;
4)根据第一次渲染得到的纹理中的每个像素的坐标值和第二次渲染得到的渲染缓冲区对象中的辐射度值,来更新每个多边形的光照贴图,完成一次逐步求精辐射度算法的求解;然后将发射者的光照贴图辐射度清零,计算此时场景中具有最大辐射度值的多边形,作为为下一个发射者;
5)根据步骤4)得到的下一个发射者,重复步骤2)-4),直至计算得到的场景中最大辐射度值低于设定的阈值时,停止迭代计算,最近一次得到的解即为每个多边形精确辐射度值,完成光照渲染。
基于GPU的逐步求精辐射度算法的伪代码如下所示:
初始化多边形的辐射度E,选择其中具有最大辐射度的多边形作为发射者;
While(没有收敛时)
{
先把每个多边形的ID作为颜色进行第一次渲染,用顶点着色器进行半球投影,结果存储在创建的帧缓冲区A的纹理中,同时把多边形的光照贴图中每个像素的坐标同样存储在纹理中;
再进行第二次渲染,读取第一次渲染得到的纹理中的ID值,判断可见性;
If(片段可见)
{
计算形状因子FF;
计算接收到的辐射度△E=ρ*FF*E;
然后把计算的辐射度渲染到帧缓冲区B的渲染缓冲区对象中存储;
}
根据第一次渲染得到的纹理中的每个像素的坐标值和第二次渲染得到的渲染缓冲区中的辐射度值进行结果读取,来更新每个多边形的光照贴图;
发射者的光照贴图能量值清零;
再次计算具有最大能量值的多边形即为下一个发射者;
}
这种辐射度解法的每次发射能量都需要两次渲染计算:可见遍和重建遍。本发明的关键也在于此。
可见遍是从发射者的视角来离线渲染场景,把场景中每个多边形的ID作为颜色来进行渲染,利用可编程顶点着色器来把顶点投影到一个半球体上,最终把结果渲染到纹理中存储;同时把每个多边形光照贴图中像素的的坐标写入纹理中存储。这样该纹理中的每个单元存储的就是对当前发射者可见的多边形的ID和像素的的坐标。具体步骤如下:
11)创建一个空的纹理texture,用来存储接下来执行离屏渲染的结果。
12)在CPU创建自己的帧缓冲区A(Frame Buffer Objet)而不是用窗口***所提供的默认帧缓冲区,默认帧缓冲区是图形服务器的显示***可用的唯一帧缓冲区,它是用户自己可以在屏幕上看到的唯一帧缓冲区。而本发明在CPU中创建的帧缓冲区A用于实现离屏渲染,即把当前一次的渲染结果存储到提前创建的纹理texture中作为临时变量存储,是不能在显示器上显示的。
13)在顶点着色器中创建执行半球投影的着色器程序,并对顶点着色器中用到的一致变量进行初始化赋值,用它代替OpenGL固定管线上顶点处理操作。
14)渲染场景:即第一次渲染,把场景中每个多边形的ID作为颜色来进行渲染,并将渲染结果以及多边形的光照贴图中每个像素的坐标存储到纹理texture。
重建遍把每个潜在的接受光照辐射的多边形用正交投影绘制到一个和多边形纹理分辨率大小一样的帧缓存区B中。这样在帧缓冲区A辐射度纹理的单元和帧缓存区B的每个片段建立一一对应的关系。此处的帧缓存区B也是在CPU中另外创建的,并且为其附加渲染缓冲区对象,通过离屏渲染操作,用渲染缓冲区对象来存储辐射度计算过程中每个片段的辐射度光照值。再从创建的帧缓冲区B中读取存储计算结果的图像,替换现有的纹理图像。具体步骤如下:
21)、在CPU中创建一个帧缓冲区B(Frame Buffer Objet),帧缓冲区B本身是没有任何存储空间的,必须为其附加连接纹理对象或者渲染缓冲区对象,不同于第一次渲染对帧缓冲区A附加的是纹理对象,此处是为帧缓冲区B附加连接渲染缓冲区对象(RenderBuffer Object),渲染缓冲区对象是一个数据存储区,包含一副图像和内部渲染格式。此处存储的是片段着色器计算完每个片段得到的辐射度光照值。
22)、CPU为创建的渲染缓冲区对象分配存储空间并指定图像格式,其中存储空间的大小与第一次渲染得到的纹理大小一样。使得光照贴图纹理中的像素与帧缓存区B的每个片段建立一一对应的关系,这样就可以把每个多边形计算得到的辐射度光照值从帧缓冲区B中重新复制到多边形的纹理中,为下一次求解准备。一旦为渲染缓冲区对象创建了存储空间后,就可以把其附加到创建的帧缓冲区B上,然后就可以向其进行渲染。
23)、编写着色器程序替换OpenGL固定管线操作,对每个多边形进行渲染,在片段着色器中对光栅化后的每个片段进行可见性判断,通过在片段着色其中读取第一次渲染得到的帧缓冲区A的纹理对象,判断当前片段是否在该纹理中,若在则对发射者可见,计算可见片段形状因子值,进而求出该片段接受到的光照值,即辐射度,并把结果存储到渲染缓冲区对象中。
24)、GPU从帧缓冲区A和帧缓冲区B读取数据,根据第一次渲染得到的纹理对象中存储的像素坐标数据和第二次得到的渲染缓冲区对象中的每个像素的辐射度值,根据坐标数据把辐射度值更新到每个多边形自身的光照贴图中,完成一次逐步求精辐射度算法的求解。
本发明重复可见遍和重建遍渲染进行迭代计算,得到一次逐步求精辐射度算法的解后,计算每个多边形对应的纹理的总辐射度值,找出具有最大辐射度值的纹理对应的多边形ID,将此多边形作为下一个发射者,重复两次渲染,直到计算得到的最大辐射度值低于设定的阈值时,停止迭代求解。此时把每个多边形对应的光照贴图进行纹理打包成大的光照贴图纹理,这样就可以把整个场景渲染到窗口***提供的帧缓冲区中用于显示。
表1展示了对于图1和图2所示的简单场景、复杂场景,现有用CPU完成的逐步求精辐射度算法,以及本发明改进后的融合GOU与CPU的方法的渲染时间比较结果。
表1渲染时间比较表
场景 |
场景面片数 |
场景中光源数 |
现有技术(s) |
本发明(s) |
简单场景 |
14445 |
2 |
5 |
3 |
复杂场景 |
86077 |
6 |
146 |
81 |
本发明通过采用GPU硬件加速的方法来求解辐射度,把逐步求精辐射度光照算法的计算由CPU和GPU共同完成。随着可编程图形硬件的发展,使得用户可以自己编制着色程序(Shader)来替换固定流水线中的一些功能模块,把在CPU中实现的算法照搬到GPU中来执行,从而能够利用GPU本身具有的强大计算能力来加快辐射度光照求解的计算,减少渲染时间。本发明对于简单场景的光照计算可以达到交互的速度,对复杂场景的光照计算可以极大的提高计算速度,减少光照渲染的时间,并且同时取得了良好的光照效果。