计算机图形学(OPENGL):帧缓冲

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机图形学(OPENGL):帧缓冲相关的知识,希望对你有一定的参考价值。

参考技术A 本文同时发布在我的个人博客上: https://dragon_boy.gitee.io

  至今为止我们已经使用了几种不同类型的屏幕缓冲:颜色缓冲,深度缓冲,模板缓冲。这些缓冲的结合被称为帧缓冲。OpenGL允许我们定义自己的帧缓冲,一次来定义我们自己的颜色、深度、模板缓冲。
  我们到目前位置所做的一些渲染操作全都是在默认的帧缓冲之上实现的。在使用GLFW时,它默认会创建并配置这个默认的帧缓冲。通过自定义帧缓冲我们会得到一个新的可渲染目标。
  使用不同的帧缓冲,我们可以得到类似创建镜像的效果,或者进行一些特殊的后期处理。接下来我们介绍帧缓冲如何工作的以及如何使用帧缓冲。

  和OpenGL中的其它对象一样,我们通过glGenFramebuffers来创建帧缓冲(FBO):

  接着我们将这个FBO对象绑定至帧缓冲区目标:

  当然我们可以单独的绑定到可读帧缓冲区(GL_READ_FRAMEBUFFER)和可绘制帧缓冲区(GL_DRAW_FRAMEBUFFER)。
  但现在还不能使用这个帧缓冲,我们还需要进行以下步骤:

  由于我们的帧缓冲不是默认的,在我们帧缓冲上面执行的渲染命令不会输出到屏幕上。因为这种原因,使用不同的帧缓冲渲染被称为离屏渲染。为了能够使用我们自定义的帧缓冲,我们需要将默认的帧缓冲与0绑定来取消激活:

  在执行完所有的帧缓冲操作后,我们删除创建的帧缓冲对象:

  在帧缓冲完整度检查前,我们需要为帧缓冲添加一些附加项。一个附加项对帧缓冲来说是一个表现为一片缓冲区的存储位置。当创建附加项时,我们有两个选项,纹理或渲染缓冲对象。

  当为一个帧缓冲附加一个纹理时,所有的渲染命令会像对待颜色、深度和模板缓冲一样写入纹理。使用纹理的好处是渲染输出可以保存在一张纹理图片中,随后我们在着色器中使用。
  为帧缓冲创建一张纹理和普通的纹理一致:

  不同之处在于我们将纹理的宽和高设为屏幕的宽和高(不是必须的),并将传入纹理的数据设为NULL,相当于创建空纹理。之后再帧缓冲上渲染时会写入数据。注意这里我们不需要考虑纹理映射和mipmaps。
  接下来我们将纹理附加到帧缓冲上:

  glFramebufferTexture2D有五个参数:

  和纹理一样,渲染缓冲对象也是一种缓冲,然而,渲染缓冲不能直接读取,但这样也让OpenGL可以进行一些内存优化,在针对离屏渲染时有更大的优势。
  渲染缓冲对象直接将所有的渲染数据存储到它的缓冲区中,不需要转化到特定的纹理模式,可以作为一个快速的可写入存储中心。虽不能直接读取,但我们可以使用glReadPixels缓慢读取,这个方法返回当前帧缓冲区特定的一片区域的像素,而不是附加项本身。
  由于数据以一种特殊的格式存储,所有在写入数据或将数据复制到其它缓冲时非常迅速。使用渲染缓冲会大大提升切换缓冲时的速度。我们之前使用的glSwapBuffers正是渲染缓冲的一种应用:我们将数据写入一个渲染缓冲区,最后与另一个交换。
  创建一个渲染缓冲对象也是使用类似的方法:

  接着我们将rbo绑定至渲染缓冲区:

  由于渲染缓冲对象不可读,所以常作为深度和模板附加项使用(大多数时候我们不考虑读取值,而更关心深度和模板检测)。我们需要深度和模板检测,但不需要采样值,所以渲染缓冲对象非常合适。
  我们使用glRenderbufferStorage来创建深度和模板的渲染缓冲对象:

  创建渲染缓冲对象和纹理对象一致,但渲染缓冲对象是专门设计来作为帧缓冲附加项的。我们使用GL_DEPTH24_STENCIL_8来作为内部格式。
  最后我们附加上渲染缓冲对象:

  注意纹理对象和渲染缓冲对象的选择。当我们不需要从缓冲进行采样数据时,我们选择渲染缓冲对象,反之,我们使用纹理对象。

  我们已经知道了帧缓冲的工作原理,现在来使用帧缓冲。我们将渲染场景到一张附加到帧缓冲对象上的颜色纹理上,并将纹理绘制到覆盖整个屏幕的四边形上。
  首先我们创建帧缓冲对象以及绑定:

  接下来创建一张纹理(屏幕大小)来作为颜色附加项附加到帧缓冲上:

  我们希望也可以进行深度测试(模板测试可选),这里使用渲染缓冲对象来作为深度附加项。
  创建渲染缓冲对象并绑定到缓冲区域:

  将渲染缓冲对象附加到帧缓冲上:

  接着我们检查帧缓冲是否完整:

  记得解绑帧缓冲来确保在帧缓冲上不会有其它的渲染操作。
  之后在渲染时绑定帧缓冲对象替代默认的帧缓冲对象,所有的渲染操作都将影响当前的帧缓冲。所有的深度和模板操作将从当前的帧缓冲读取相应的值。
  所以,将场景绘制到一张纹理需要以下的步骤:

  在片元着色器中我们定义一张纹理,并将其作为颜色输出:

  在渲染前我们创建四边形的VAO并配置好。之后再渲染循环中大致这样进行:

  渲染结果如下:

  左侧和平常一样,在右侧,当我们使用线框显示时会发现我们只再默认的帧缓冲中绘制了一个四边形。

  所有的场景都被渲染到了一张纹理中,所以我们可以通过操作这张纹理来进行一些特殊的处理。

  我们在屏幕四边形的片元着色器中用 减去纹理颜色来进行反相:

  结果如下:

  非常有意思!

  我们还可以灰度显示场景。我们可以将纹理颜色的RGB通道做一个平均来得到灰度结果:

  但人眼对绿色更敏感,对蓝色最不敏感,我们可以使用加权平均来进行灰度显示:

  结果如下:

  由于整个场景被渲染成一张纹理,我们可以进行所有针对纹理的处理。我们可以使用卷积和滤波来进行图片处理(有关内容请关注数字图像处理这门学科)。
  克尔效应使用下列的卷积矩阵进行变化:

  我们将像素的邻接的8个像素逐一与矩阵的每个元素相乘最后相加的到像素的某一通道的值,遍历所有的像素进行同样的操作。在片元着色器中这样使用:

  若运行程序,结果如下:

  模糊的操作为:

  相当于将像素与8邻接像素进行加权平均。内核阵列为:

  同样进行相关操作,结果如下:

  边缘检测的核心矩阵为:

以上是关于计算机图形学(OPENGL):帧缓冲的主要内容,如果未能解决你的问题,请参考以下文章

计算机图形学_图元的属性_3_OpenGL颜色函数(上)

计算机图形学_图元的属性_3_OpenGL颜色函数(上)

计算机图形学输出图元_16_字符函数

计算机图形学输出图元_16_字符函数

图形学计算机图形学知识点提纲2

计算机图形学(OPENGL):PBR理论