opengl累积缓存

opengl中的累积缓存(Accumulation buffer)
2010-06-28 18:36
累积缓存是为合成多幅图像而设计的,它不是简单的用引入象素片元来代替象素值,而是将片元进行缩放,然后加到已有的象素值上。为了经过一系列的混合操作后能够保持精度,累积缓存每个颜色分量的位数要比一般的可视化系统要多。
我们可以象其他缓存一样清空累积缓存,可以用glClearAccum()来设置红、绿和蓝色分量的清空值,按位顺序清空累积缓存或以GL_ACCUM_BUFFER_BIT调用glClear()命令。
你不能直接渲染进累积缓存,而是应该渲染到一个选定的缓存,然后用glAccum()来将在那缓存中的当前图像累积进累积缓存。glAccum()用当前选择的读取缓存来拷贝。你可以用glReadBuffer()来设置你想读取的缓存。
glAccum()有2个参数:op和value。op值可为下面中的一个:
表1 glAccum()的op值


因为你必须在累积之前渲染到另一个缓存,所以累积图像典型的方法是,将图像渲染到后缓存若干次,累积每幅图像到累积缓存中,当所需的图像数目已累积后,将内容拷贝回后缓存中,然后交换前后缓存。这样,只有在最后,才显示累积的图像。
下面是累积n幅图像的一个示例程序:
1. 调用glDrawBuffer(GL_BACK)来只渲染到后缓存;
2. 调用glReadBuffer(GL_BACK),这样累积缓存将从后缓存读取。
注意:前2步只有当应用程序已经改变了所选的写和读缓存时才需要。若可视化系统是双缓存,这些选择是默认的。
3. 调用glClear(bitfield)清空后缓存,然后渲染第1幅图像;
4. 调用glAccum(GL_LOAD,1.f/n);这允许你避免用分开的步骤来清空累积缓存;
5. 改变你的图像的参数,再重绘它;
6. 调用glAccum(GL_ACCUM,1.f/n)来将第2幅图像加到第1幅上;
7. 重复前面2个步骤≥n-2次……
8. 调用glAccum(GL_RETURN,1.f)来将完成的图像拷贝到后缓存中;
9. 调用glutSwapBuffers()(若使用GLUT)或SwapBuffers()(若使用Win32)来交换前后缓存。
累积缓存提供了一种在保持好的颜色分辨率下实现在场景中“多重曝光(multiple exposures)”的方法。使用累积缓存可以产生许多图像效果来提高图像的真实性,其中包括:反走样、运动模糊、软阴影、深度域(景深)和卷积。要产生这些效果,必须将图像渲染多次,对场景位置(或所选的物体)进行微小的、渐增的改变,然后累积结果。

OpenGL中存在各种缓冲区,用以实现不同功能,(参考缓冲区简介) 。累积缓存(Accumulate Buffer)就是一种,它主要为了辅助累积计算。例如:运动模糊和全局反走样。对于累积缓存的操作,需要使用glAccum()等函数进行。下面是涉及到的主要函数:
(一)void glClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
         指定累积缓冲区清空时设置的默认值。包括红、绿、蓝、透明度。默认为0。
(二)void glClear( GLbitfield mask );
         设置mask包括GL_ACCUM_BUFFER_BIT,以实际清除累积缓存中的内容。
(三)void glAccum( GLenum op, GLfloat value);
         对于累积缓存中的内容进行操作。op定义操作,value为操作相关值。具体如下:
         1:GL_LOAD
              将当前缓冲内容装入累积缓存中(直接的复制)。value为装载时的比例系数(0~1.0)。
              R' = R * value

              G' = G * value

              B' = B * value,A' = A * value
         2:GL_ACCUM
              将当前缓冲中的内容和累积缓存中内容进行累积混合,结果放入累积缓存中。
              R' = R*(1.0-value) + R*value

              G' = G*(1.0-value) + G*value
              B' = B*(1.0-value) + B*value

              A' = A*(1.0-value) + A*value
         3:GL_ADD
              将累积缓存中的内容加特定值。
              R' = R' + value

              G' = G' + value

              B' = B' + value

              A' = A' + value
         4:GL_MULT
              将累积缓存中的内容乘以特定值。
              R' = R' * value

              G' = G' * value

              B' = B' * value

              A' = A' * value
         5:GL_RETURN
              将累积缓存中的内容拷贝到颜色缓存或当前选择缓冲区。
              R = R' * value

              G = G' * value

              B = B' * value

              A = A' * value

实际应用时,可以遵循下列参考步骤:
(一)调用glAccum(GL_LOAD, value)将选定缓冲区内容装入累积缓存中。
(二)调用glAccum(GL_ACCUM, value)、glAccum(GL_ADD, value)或glAccum(GL_MULT, value)进行累积计算。
(三)glAccum(GL_RETURN, value)将结果返回给指定的缓冲区。
注:当前选定缓冲区可以由glReadBuffer()指定,默认为后颜色缓冲区。

下面为一个实际的运动模糊实例:

void DrawMotionBlur()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0,0.0,10.0,0.0,0.0,0.0,0.0,1.0,0.0);

    GLfloat angle = 0.0f;
    for(int i = 0; i < COUNT; i++)

    {
        DrawObject(angle);
        angle += 10.0f;
        if(0==i)
             glAccum(GL_LOAD,1.0f/COUNT);
        else
             glAccum(GL_ACCUM,1.0f/COUNT);

    }
    glAccum(GL_RETURN, 1.0f);
}

 

OpenGL 是一个与硬件图形发生器的软件接口,它包括了100多个图形操作函数,开发者可以利用这些函数来构造景物模型、进行三维图形交互软件的开发。 OpenGL 是一个高性能的图形开发软件包。OpenGL 支持网络,在网络系统中用户可以在不同的图形终端上运行程序显示图形。OpenGL 作为一个与硬件独立的图形接口,它不提供与硬件密切相关的设备操作函数,同时,它也不提供描述类似于飞机、汽车、分子形状等复杂形体的图形操作函数。用户必须从点、线、面等最基本的图形单元开始构造自己的三维模型。当然,象 Blender 那样更高一级的基于 OpenGL 的三维图形建模开发软件包将提供方便的工具。因此 OpenGL 的图形操作函数十分基本、灵活。例如 OpenGL 中的模型绘制过程就多种多样,内容十分丰富,OpenGL 提供了以下的对三维物体的绘制方式:

  • 网格线绘图方式(wireframe): 这种方式仅绘制三维物体的网格轮廓线。
  • 深度优先网格线绘图方式(depth_cued): 用网格线方式绘图,增加模拟人眼看物体一样,远处的物体比近处的物体要暗些。
  • 反走样网格线绘图方式(antialiased): 用网格线方式绘图,绘图时采用反走样技术以减少图形线条的参差不齐。
  • 平面消隐绘图方式(flat_shade): 对模型的隐藏面进行消隐,对模型的平面单元按光照程度进行着色但不进行光滑处理。
  • 光滑消隐绘图方式(smooth_shade): 对模型进行消隐按光照渲染着色的过程中再进行光滑处理,这种方式更接近于现实。
  • 加阴影和纹理的绘图方式(shadows、textures): 在模型表面贴上纹理甚至于加上光照阴影,使得三维景观象照片一样。
  • 运动模糊的绘图方式(motion-blured): 模拟物体运动时人眼观察所感觉的动感现象。
  • 大气环境效果(atmosphere-effects): 在三维景观中加入如雾等大气环境效果,使人身临其境。
  • 深度域效果(depth-of-effects): 类似于照相机镜头效果,模型在聚焦点处清晰,反之则模糊。

这些三维物体绘图和特殊效果处理方式,说明OpenGL已经能够模拟比较复杂的三维物体或自然景观。

OpenGL工作流程2

整个OpenGL的基本工作流程如下图:

其中几何顶点数据包括模型的顶点集、线集、多边形集,这些数据经过流程图的上部,包括运算器、逐个顶点操作等;图像数据包括象素集、影像集、位图集等,图像象素数据的处理方式与几何顶点数据的处理方式是不同的,但它们都经过光栅化、逐个片元(Fragment)处理直至把最后的光栅数据写入帧缓冲器。在 OpenGL中的所有数据包括几何顶点数据和象素数据都可以被存储在显示列表中或者立即可以得到处理。OpenGL中,显示列表技术是一项重要的技术。

OpenGL要求把所有的几何图形单元都用顶点来描述,这样运算器和逐个顶点计算操作都可以针对每个顶点进行计算和操作,然后进行光栅化形成图形碎片;对于象素数据,象素操作结果被存储在纹理组装用的内存中,再象几何顶点操作一样光栅化形成图形片元。

整个流程操作的最后,图形片元都要进行一系列的逐个片元操作,这样最后的象素值BZ送入帧缓冲器实现图形的显示。

OpenGL图形操作步骤2

根据上面讲的 OpenGL 工作流程可以归纳出在 OpenGL 中进行主要的图形操作直至在计算机屏幕上渲染绘制出三维图形景观的基本步骤:

  1. 根据基本图形单元建立景物模型,并且对所建立的模型进行数学描述(OpenGL 中把:点、线、多边形、图像和位图都作为基本图形单元);
  2. 把景物模型放在三维空间中的合适的位置,并且设置视点(viewpoint)以观察所感兴趣的景观;
  3. 计算模型中所有物体的色彩,其中的色彩根据应用要求来确定,同时确定光照条件、纹理粘贴方式等;
  4. 把景物模型的数学描述及其色彩信息转换至计算机屏幕上的象素,这个过程也就是光栅化(rasterization)。

在这些步骤的执行过程中,OpenGL 可能执行其他的一些操作,例如自动消隐处理等。另外,景物光栅化之后被送入帧缓冲器之前还可以根据需要对象素数据进行操作,即所谓的片元(fragment)操作。

片元就是栅格化以后的像素点,它比像素多一些位置啊、法向量等属性。逐个片元操作有像素所有权操作(确定目标像素可见还是被一个重叠的窗口盖住了),剪切测试、Alpha测试、模板测试、混合等。比如,反走样时,OpenGL 要基于屏幕像素块所覆盖的区域, 对每一 块片元的覆盖范围值进行计算。在 RGBA 模块中, OpenGL 要用该值与片元的 alpha 值相乘, 然后, 利用结果alpha 值将片元与缓存中的对应像素混合。

从FRAGMENT到PIXEL(framebuffer 帧缓存)3

关于帧缓存

帧缓存包括颜色、scissor、alpha、stencil、depth 这些缓存,所以帧缓存不是一片缓存,而是所有这些缓存的组合,帧缓存它对应了屏幕上的每一个 pixel (不是真正的 pixel,而是一个 fragment 所对应的位置)的各种这些信息(颜色、ZBUFFER、等等),几何体的 fragment 是没有帧缓存的,帧缓存是屏幕上的,一个缓存的一位就对应一个位片面。

Colorbuffer是最终表现在屏幕上的,但是其他缓存决定了最后这些color是否和怎样绘制在屏幕上.,其中除了colorbuffer的那些帧缓存也称作辅助缓存。

在光栅化得到fragment后,就要按照以下的顺序进行各种测试,那些通过测试了的像素才有资格最后放到屏幕上,在通过所有测试后,这些fragment将可能被写入原有的帧缓存(但是在最后混合出来的颜色值可能与屏幕原有的帧缓存中 的颜色缓存做各种运算,所以颜色缓存有时可能不会被简单覆盖,但其他缓存会。

颜色缓存的读写:glDrawBuffer和glReadBuffer(),然后调用GlwritePixel、 glreadpixel等。

缓存掩码(MASK)

可以设置每种帧缓存的掩码,再写入相应缓存时就会被掩码作用,具体表现为

GlIndexmask(Glint mask):如果在索引模式下,当mask的第i位为0时,则第i个索引的颜色不会被写入到颜色缓存中,你会看到原本为那个颜色的区域变成空空的了,为1正常绘制,可以利用他进行层动画。

Glcolormask(GLboolean red,GLboolean green,GLboolean blue,GLboolean alpha):它可以控制颜色值中的那个通道绘制那个不被绘制(1为绘制),这个可以让你查看各种颜色的渲染,如图是只显示红色的情形:

color mask
color mask

GlDepthMask(GLboolean flag):当flag为真时写入深度缓存,否则就不写入。默认是写入的,那如果为否不写入你将看到什么效果呢?这样每一帧后绘制的那个物体它不写帧缓存,也就是它的帧缓存默认为最小的0,所以此时你永远看到后绘制的在前面(这跟正常情况下不带开深度测试看到的场景是一样的)。

深度缓存的掩码有很多的应用,例如你可以随时置它与FALSE控制以后的物体为后绘制的现实在最上层。

fragment的测试

注意这里要进行的所有测试都是在fragmen级别上的。测试的意义是;当一个被计算出来的fragment要最终成为屏幕的一个pixel时要按照下面顺序通过测试,如果在哪个阶段不被通过,就会不绘制,如果最后都通过了,确定要在屏幕的颜色缓存上绘制这一点时,还要将这点屏幕缓存该点原先颜色进行某种混合。

Scissor Test:这里通过glScissor()定义一个矩形区域(Scissor box),如果片段在这和矩形内,则通过,不在则不通过(不显示),这个相当于 StencilTest的矩形版。效果如下:就像从一小窗口观看的感觉

scissor test
scissor test

Alpha test:它的基本功能时利用alpha(RGBA的A) 缓存来判定是否绘制一个像素,虽然它的值经常被用来进行混合效果。用glAlphaFunc()来定义参考值和通过测试的条件。测试的过程是,用fragment的alpha值遇一个定义的参考值做比较,比较的方式由glAlphaFunc指定,通过这个比较来判断是否通过测试。

Alpha test可以制作镂空效果或是billboard,制作billboard的过程是:制作一个贴有rgba四通道的贴图,其中透明部分的alpha为0,反之为1,在场景中为一个细分的多边形贴上这个贴图作为纹理,其中顶点的ALPHA值就为其贴图的点的 ALPHA值,最后使用alpha测试,并将比较函数设置为GL_GRETER,参考值为0.5,这样0的那部分就会不通过,变成完全透明的了。

Stencil test:它是一个模板测试,形象的比喻就是在stencil test处理中,会依据stencil buffer这个记忆体中所存放的资料,来决定是否要舍弃像素。它的测试过程是这样的,在所有像素的stencil buffer里面存储了一些值,然后定义了一个比较方法和一个参考值,如果参考值与该点的stencilbuffer里面的值在这种比较方式下通过,那么这点通过测试,被绘制。你不能直接写入 stencilbuffer,但是可以通过glStencilOp()来间接写入,这个函数设定当一个fragment通过或者不通过Stencil缓存时Stencil缓存起什么变化。

Stencil test最常用的用途就是在屏幕上定义一块不规则的区域,然后限制在这个区域内显示。它的实现方法是这样的:

首先要清空当前的Stencil缓存为0:glClearStencil(0);glClear(GL_STENCIL_BUFFER_BIT)

然后设定glStencilFunc()为GLALWAYS,1,就是都通过该测试,并且设定 GLSENCILOP(GL_REPLACE,GL_REPLACE,GL_REPLACE)接着绘制你希望的不规则区域的形状。这段代码的意思就是让你绘制的那个形状都通过Stencil测试,而且通过后都用1来取代那点的Stencil缓存。这样就把Stencil缓存变成了那块形状的区域为1,定制了一个这种形状的模板,注意每次RESIZE的时候stencil会自动清空掉。

最后要清空屏幕的颜色和深度缓存,然后设定glStencilFunc为GL_EQUAL,1, GLSENCILOP为GL_KEEP(不变Stencil缓存了,最后后绘制你要绘制的场景,这样那些等于1的stencil位置被绘制上了,不等于的就不绘制了,这是这种算法的效果:里面在一个人形的区域内绘制,这对一些游戏的GUI来说很有帮助。

stencil test
stencil test

Depth test:通常进行深度测试的过程是这样的,在每一帧开始用一个很大的值来清空深度缓存,然后在绘制时,会自动产生当前的深度,默认采用的比较函数是较小的深度通过测试进行绘制,这样就剔除了背景。当然也可以用 glDepthFunc来设置比较函数。

测试之后

当进行过上述测试后,所有即将认为要被写到同一屏幕位置的片段将混合他们的颜色值,作为最后的颜色在最后一步写在屏幕上,在这里可以设置这个混合方式。

Dithering:这一歩时用在索引模式,因为在索引模式下如果混合后的颜色不再索引中,他将找一种所颜色拼凑的方式来替代,打开GL_DITHER的开关即可。

最后一步逻辑操作:最后一步时逻辑操作,我们在前面生成了将写在屏幕上的颜色,而此时COLORBUFFER有刚才的颜色值,这是可以选择对这两个值进行操作,默认是GL_COPY,也就是单纯拷贝新值到屏幕上,但是事实上还有很多可选的逻辑操作,当然要打开GL_COLOR_LOGIC_OP/INDEX开关。其实这个操作时非常有用的,它可以直接控制最后在屏幕上的显示状况,它只是改变最后屏幕上的像素,所以可以很轻松的实现肯多小TRIK,如颜色反置,切换场景与清空等等。

关于Accumulation Buffer

这个framebuffer和测试无关,它用来累计颜色缓存上的值,也就是说他可以将每帧的颜色缓存的值类加到她这里,然后她还可以把他累加的内容重新写入颜色缓存,我们看上去就像一幅几张图像叠加的照片。我们可以控制这些过程。glAccum()控制对这个缓存的操作,通常有开始累加,清空重新累加、写到屏幕颜色缓存、数值运算等操作。可以利用buffer进行反走样和模糊处理。使用前要在最前面打开GLUT_ACCUM模式,这个缓存有很多用处:如反走样、模拟运动模糊、模拟多重曝光(即产生HDR图)、产生软影(soft shadow)等等。

原文地址:https://www.cnblogs.com/lizhengjin/p/1837204.html