【OpenGL实践12】关于缓存区Framebuffer的运用

文章目录

  • 一、说明
  • 二、帧缓冲区
  • 三、创建新的帧缓冲区
  • 四、附属装饰
    • 4.1 纹理图像
    • 4.2 渲染缓冲区对象图像
  • 五、使用帧缓冲区
    • 5.1 后期处理
    • 5.2 更改代码
  • 六、后期处理效果
    • 6.1 色彩处理
    • 6.2 模糊
    • 6.3 Sobel算子
  • 七、结论
  • 练习

一、说明

关于FrameBuffer的使用,是OpenGL的高级使用方式,而这种新诞生的功能有些未知的技术需要逐步熟悉,本篇就是针对它的图像处理功能展开陈述。

二、帧缓冲区

在前面的章节中,我们了解了 OpenGL 提供的不同类型的缓冲区:颜色、深度和模板缓冲区。这些缓冲区像任何其他 OpenGL 对象一样占用视频内存,但到目前为止,除了在创建 OpenGL 上下文时指定像素格式之外,我们几乎无法控制它们。这种缓冲区组合称为默认帧缓冲区,正如您所见,帧缓冲区是内存中可以渲染的区域。如果您想获取渲染结果并对其进行一些附加操作(例如许多现代游戏中的后处理),该怎么办?

在本章中,我们将讨论帧缓冲区对象,这是创建额外的帧缓冲区以进行渲染的一种方法。帧缓冲区的伟大之处在于它们允许您将场景直接渲染到纹理,然后可以在其他渲染操作中使用该纹理。在讨论了帧缓冲区对象的工作原理之后,我将向您展示如何使用它们对上一章的场景进行后处理。

三、创建新的帧缓冲区

您需要的第一件事是一个帧缓冲区对象来管理新的帧缓冲区。

GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);

此时您还不能使用此帧缓冲区,因为它还不完整。在以下情况下,帧缓冲区通常是完整的:

至少已附加一个缓冲区(例如颜色、深度、模板)
必须至少有一种颜色附件(OpenGL 4.1 及更早版本)
所有附件均已完成(例如,纹理附件需要保留内存)
所有附件必须具有相同数量的多重样本
您可以随时通过调用glCheckFramebufferStatus并检查它是否返回来检查帧缓冲区是否完整GL_FRAMEBUFFER_COMPLETE。其他返回值请参阅参考资料。您不必执行此检查,但验证通常是一件好事,就像检查着色器是否成功编译一样。

现在,让我们绑定帧缓冲区来使用它。

glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

第一个参数指定帧缓冲区应附加到的目标。OpenGL 在这里区分了GL_DRAW_FRAMEBUFFER和GL_READ_FRAMEBUFFER。绑定到 read 的帧缓冲区用于 的调用glReadPixels,但由于这种区别在普通应用程序中相当罕见,因此您可以使用 将您的操作应用于两者GL_FRAMEBUFFER。

glDeleteFramebuffers(1, &frameBuffer);

完成后别忘了清理。

四、附属装饰

仅当已分配内存来存储结果时,您的帧缓冲区才能用作渲染目标。这是通过为每个缓冲区附加图像(颜色、深度、模板或深度和模板的组合)来完成的。有两种对象可以用作图像:纹理对象和渲染缓冲区对象。前者的优点是它们可以直接在着色器中使用,如前面的章节所示,但根据您的实现,渲染缓冲区对象可能会作为渲染目标进行更优化。

4.1 纹理图像

我们希望能够渲染一个场景,然后在另一个渲染操作中使用颜色缓冲区中的结果,因此纹理在这种情况下是理想的选择。创建纹理作为新帧缓冲区颜色缓冲区的图像与创建任何纹理一样简单。

GLuint texColorBuffer;
glGenTextures(1, &texColorBuffer);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);

glTexImage2D(
    GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL
);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

该纹理与您之前见过的纹理之间的区别在于NULLdata 参数的值。这是有道理的,因为这次将通过渲染操作动态创建数据。由于这是颜色缓冲区的图像,因此format和internalformat参数受到更多限制。该format参数通常仅限于 或GL_RGB以及GL_RGBA颜色internalformat格式。

我在这里选择了默认的 RGB 内部格式,但您可以尝试更奇特的格式,例如GL_RGB10如果您想要 10 位颜色精度。我的应用程序的分辨率为 800 x 600 像素,因此我使这个新的颜色缓冲区与该分辨率相匹配。分辨率不必与默认帧缓冲区相匹配,但glViewport如果您决定改变,请不要忘记调用。

剩下的一件事是将图像附加到帧缓冲区。

glFramebufferTexture2D(
    GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0 );

第二个参数意味着您可以有多个颜色附件。片段着色器可以通过使用我们之前使用的函数将out变量链接到附件来向其中任何一个输出不同的数据。glBindFragDataLocation我们现在将坚持使用一种输出。最后一个参数指定图像应附加到的 mipmap 级别。 Mipmapping 没有任何用处,因为在使用颜色缓冲区图像进行后期处理时,它将以其原始大小进行渲染。

4.2 渲染缓冲区对象图像

由于我们使用深度和模板缓冲区来渲染可爱的旋转立方体,因此我们也必须创建它们。 OpenGL 允许您将它们组合成一张图像,因此我们必须再创建一张图像才能使用帧缓冲区。尽管我们可以通过创建另一个纹理来做到这一点,但将这些缓冲区存储在渲染缓冲区对象中会更有效,因为我们只对读取着色器中的颜色缓冲区感兴趣。

GLuint rboDepthStencil;
glGenRenderbuffers(1, &rboDepthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);

创建渲染缓冲区对象与创建纹理非常相似,不同之处在于该对象被设计用作图像而不是像纹理那样的通用数据缓冲区。我在这里选择了GL_DEPTH24_STENCIL8内部格式,它适合分别保存 24 位和 8 位精度的深度缓冲区和模板缓冲区。

glFramebufferRenderbuffer(
    GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil  );

连接起来也很容易。您可以稍后通过调用删除该对象,就像删除任何其他对象一样glDeleteRenderbuffers。

五、使用帧缓冲区

选择帧缓冲区作为渲染目标非常容易,实际上只需一次调用即可完成。

glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
在此调用之后,所有渲染操作都将其结果存储在新创建的帧缓冲区的附件中。要切换回屏幕上可见的默认帧缓冲区,只需传递0.

glBindFramebuffer(GL_FRAMEBUFFER, 0);
请注意,虽然只有默认的帧缓冲区在屏幕上可见,但您可以读取当前与调用绑定的任何帧缓冲区,glReadPixels只要它不仅绑定到GL_DRAW_FRAMEBUFFER.

5.1 后期处理

在当今的游戏中,后处理效果似乎几乎与屏幕上渲染的实际场景一样重要,并且实际上可以使用不同的技术来实现一些令人惊叹的结果。实时图形中的后处理效果通常在片段着色器中实现,并将渲染的场景作为纹理形式的输入。帧缓冲区对象允许我们使用纹理来包含颜色缓冲区,因此我们可以使用它们为后处理效果准备输入。

要使用着色器为先前渲染到纹理的场景创建后处理效果,通常将其渲染为屏幕填充的 2D 矩形。这样,应用了效果的原始场景就会以其原始大小填充屏幕,就好像它首先被渲染到默认帧缓冲区一样。

当然,您可以利用帧缓冲区发挥创意,通过从不同角度多次渲染场景并将其显示在监视器或最终图像中的其他对象上,使用它们在游戏世界中执行从门户到摄像机的任何操作。这些用途更加具体,因此我将它们作为练习留给您。

5.2 更改代码

不幸的是,在这里逐步介绍对代码的更改有点困难,特别是如果您偏离了此处的示例代码。现在您已经了解了帧缓冲区是如何创建和绑定的,并且只要小心一点,您应该能够做到这一点。让我们全局地完成这里的步骤。

首先尝试创建帧缓冲区并检查它是否完整。尝试将其绑定为渲染目标,您会看到屏幕变黑,因为场景不再渲染到默认帧缓冲区。尝试更改场景的清晰颜色并读取它以glReadPixels检查场景是否正确渲染到新的帧缓冲区。
接下来,尝试创建一个新的着色器程序、顶点数组对象和顶点缓冲区对象,以 2D(而不是 3D)渲染事物。切换回默认帧缓冲区非常有用,这样可以轻松查看结果。您的 2D 着色器不需要变换矩阵。尝试以这种方式在 3D 旋转立方体场景前面渲染一个矩形。
最后,尝试将 3D 场景渲染到您创建的帧缓冲区,并将矩形渲染到默认帧缓冲区。现在尝试使用矩形中帧缓冲区的纹理来渲染场景。
我选择仅使用 2 个位置坐标和 2 个纹理坐标进行 2D 渲染。我的 2D 着色器如下所示:

#version 150 core
in vec2 position;
in vec2 texcoord;
out vec2 Texcoord;
void main()
{
    Texcoord = texcoord;
    gl_Position = vec4(position, 0.0, 1.0);
}
#version 150 core
in vec2 Texcoord;
out vec4 outColor;
uniform sampler2D texFramebuffer;
void main()
{
    outColor = texture(texFramebuffer, Texcoord);
}

使用此着色器,程序的输出应该与您了解帧缓冲区之前相同。渲染一帧大致如下所示:

// Bind our framebuffer and draw 3D scene (spinning cube)
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glBindVertexArray(vaoCube);
glEnable(GL_DEPTH_TEST);
glUseProgram(sceneShaderProgram);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texKitten);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texPuppy);

// Draw cube scene here

// Bind default framebuffer and draw contents of our framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindVertexArray(vaoQuad);
glDisable(GL_DEPTH_TEST);
glUseProgram(screenShaderProgram);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);

glDrawArrays(GL_TRIANGLES, 0, 6);

3D 和 2D 绘图操作都有自己的顶点数组(立方体与四边形)、着色器程序(3D 与 2D 后处理)和纹理。您可以看到,绑定颜色缓冲区纹理与绑定常规纹理一样简单。请注意,glBindTexture更改 OpenGL 状态之类的调用相对昂贵,因此请尝试将它们保持在最低限度。

我认为,无论我在这里解释程序的总体结构有多好,你们中的一些人只是喜欢看一些新的示例代码,也许还可以运行diff它以及上一章中的代码。

六、后期处理效果

我现在将讨论各种有趣的后处理效果、它们的工作原理以及它们的外观。

6.1 色彩处理

反转颜色是图像处理程序中常见的一个选项,但您也可以使用着色器自己完成!

由于颜色值是从0.0到 1.0的浮点值,因此反转通道就像计算一样简单1.0 - channel。如果对每个通道(红色、绿色、蓝色)执行此操作,您将得到反转的颜色。在片段着色器中,可以这样完成。

outColor = vec4(1.0, 1.0, 1.0, 1.0) - texture(texFramebuffer, Texcoord);

这也会影响 alpha 通道,但这并不重要,因为 alpha 混合默认情况下是禁用的。

在这里插入图片描述

通过计算每个通道的平均强度可以简单地实现颜色灰度化。

outColor = texture(texFramebuffer, Texcoord);
float avg = (outColor.r + outColor.g + outColor.b) / 3.0;
outColor = vec4(avg, avg, avg, 1.0);

这工作得很好,但人类对绿色最敏感,对蓝色最不敏感,因此更好的转换可以使用加权通道。

outColor = texture(texFramebuffer, Texcoord);
float avg = 0.2126 * outColor.r + 0.7152 * outColor.g + 0.0722 * outColor.b;
outColor = vec4(avg, avg, avg, 1.0);

6.2 模糊

有两种众所周知的模糊技术:框模糊和高斯模糊。后者会产生更高质量的结果,但前者更容易实现,并且仍然相当接近高斯模糊。

在这里插入图片描述

模糊是通过对像素周围的像素进行采样并计算平均颜色来完成的。

const float blurSizeH = 1.0 / 300.0;
const float blurSizeV = 1.0 / 200.0;
void main()
{
    vec4 sum = vec4(0.0);
    for (int x = -4; x <= 4; x++)
        for (int y = -4; y <= 4; y++)
            sum += texture(
                texFramebuffer,
                vec2(Texcoord.x + x * blurSizeH, Texcoord.y + y * blurSizeV)
            ) / 81.0;
    outColor = sum;
}

您可以看到总共采集了 81 个样本。您可以更改 X 轴和 Y 轴上的样本量来控制模糊量。这些blurSize变量用于确定每个样本之间的距离。较高的样本数和较低的样本距离会产生更好的近似值,但也会迅速降低性能,因此请尝试找到一个良好的平衡点。

6.3 Sobel算子

Sobel算子常用于边缘检测算法中,我们来看看它长什么样。

片段着色器如下所示:

vec4 top         = texture(texFramebuffer, vec2(Texcoord.x, Texcoord.y + 1.0 / 200.0));
vec4 bottom      = texture(texFramebuffer, vec2(Texcoord.x, Texcoord.y - 1.0 / 200.0));
vec4 left        = texture(texFramebuffer, vec2(Texcoord.x - 1.0 / 300.0, Texcoord.y));
vec4 right       = texture(texFramebuffer, vec2(Texcoord.x + 1.0 / 300.0, Texcoord.y));
vec4 topLeft     = texture(texFramebuffer, vec2(Texcoord.x - 1.0 / 300.0, Texcoord.y + 1.0 / 200.0));
vec4 topRight    = texture(texFramebuffer, vec2(Texcoord.x + 1.0 / 300.0, Texcoord.y + 1.0 / 200.0));
vec4 bottomLeft  = texture(texFramebuffer, vec2(Texcoord.x - 1.0 / 300.0, Texcoord.y - 1.0 / 200.0));
vec4 bottomRight = texture(texFramebuffer, vec2(Texcoord.x + 1.0 / 300.0, Texcoord.y - 1.0 / 200.0));
vec4 sx = -topLeft - 2 * left - bottomLeft + topRight   + 2 * right  + bottomRight;
vec4 sy = -topLeft - 2 * top  - topRight   + bottomLeft + 2 * bottom + bottomRight;
vec4 sobel = sqrt(sx * sx + sy * sy);
outColor = sobel;

就像模糊着色器一样,以有趣的方式采集并组合一些样本。您可以在其他地方阅读有关技术细节的更多信息。

七、结论

着色器的一个很酷的事情是,由于显卡具有巨大的并行处理能力,您可以实时地按像素操作图像。毫不奇怪,像 Photoshop 这样的新版本软件使用显卡来加速图像处理操作!还有许多更复杂的效果,例如 HDR、运动模糊和 SSAO(屏幕空间环境光遮挡),但这些效果比单个着色器涉及的工作量要多一些,因此它们超出了本章的范围。

练习

尝试通过添加另一个帧缓冲区来实现两次通过的高斯模糊效果。 (解决方案)
尝试在 3D 场景中添加一个面板,从不同角度显示该场景。 (解决方案)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/644293.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

九、图形化脚本

多年来&#xff0c; shell脚本一直都被认为是枯燥乏味的。但如果你准备在图形化环境中运行脚本时&#xff0c;就未必如此了。有很多与脚本用户交互的方式并不依赖read和echo语句。 9.1 创建文本菜单 创建交互式shell脚本最常用的方法是使用菜单。提供各种选项可以帮助脚本用户…

从浮点数定义到FP8: AI模型中不同的数据类型

背景&#xff1a;AI模型中不同的数据类型对硬件算力和内存的需求是不同的&#xff0c;为了提高模型在硬件平台的吞吐量&#xff0c;减少数据通信带宽需求&#xff0c;往往倾向于将高位宽数据运算转向较低位宽的数据运算。本文通过重新回顾计算机中整数和浮点数的定义&#xff0…

从0开始带你成为Kafka消息中间件高手---第二讲

从0开始带你成为Kafka消息中间件高手—第二讲 那么在消费数据的时候&#xff0c;需要从磁盘文件里读取数据后通过网络发送出去&#xff0c;这个时候怎么提升性能呢&#xff1f; 首先就是利用了page cache技术&#xff0c;之前说过&#xff0c;kafka写入数据到磁盘文件的时候&…

Go微服务: Grpc服务注册在Consul的示例(非Go-Micro)

概述 现在&#xff0c;我们使用consul客户端的api来把GRPC服务实现注册到consul上&#xff0c;非Go-Micro的形式其实&#xff0c;consul官方提供了对应的接口调用来实现&#xff0c;golang中的consul/api包对其进行了封装我们使用consul/api来进行展示 目录结构 gitee.com/g…

微信小程序上线必备:SSL证书申请以及安装

一、认识ssl证书 1、ssl证书是什么&#xff1f; SSL证书&#xff0c;全称Secure Socket Layer Certificate&#xff0c;是一种数字证书&#xff0c;它遵循SSL&#xff08;现在通常指TLS&#xff0c;Transport Layer Security&#xff09;协议标准&#xff0c;用于在客户端&…

C++ vector类

目录 0.前言 1.vector介绍 2.vector使用 2.1 构造函数(Constructor) 2.1.1. 默认构造函数 (Default Constructor) 2.1.2 填充构造函数 (Fill Constructor) 2.1.3 范围构造函数 (Range Constructor) 2.1.4 拷贝构造函数 (Copy Constructor) 2.2 迭代器(Iterator) 2.2.…

封装了一个iOS水平方向瀑布流布局

首先查看效果图 是支持多分区的 思路就是和竖直方向上的瀑布流布局是一样的&#xff0c; 只不过这里记录的列是水平方向的&#xff0c;同时要记录下 当前最小的x 所在的列&#xff0c;其实原理和竖直方向上的是相同的 &#xff0c;下面贴出代码 父类layout中的代码 // // …

08-Fortran基础--Fortran内置函数分类总结

08-Fortran基础--Fortran内置函数分类总结 0 引言1 Fortran内置函数1.1 常用到数学函数1.2 字符串函数&#xff1a;1.3 数组函数&#xff1a;1.4 数值查询函数1.5 文件操作函数&#xff1a; 2 结语 0 引言 Fortran是一种很古老的编程语言&#xff0c;但它仍然广泛使用于科学计算…

使用LoRA进行高效微调:基本原理

Using LoRA for efficient fine-tuning: Fundamental principles — ROCm Blogs (amd.com) [2106.09685] LoRA: Low-Rank Adaptation of Large Language Models (arxiv.org) Parametrizations Tutorial — PyTorch Tutorials 2.3.0cu121 documentation 大型语言模型&#xf…

基于地理坐标的高阶几何编辑工具算法(7)——矩形绘制

文章目录 工具步骤应用场景示意图算法原理工具步骤 点击矩形绘制工具,点击三个点完成矩形绘制。 应用场景 用于在地图上快速绘制任意方向的矩形。 示意图 算法原理 点第一个点确定矩形的一个角点P1,也作为平移后的坐标原点,生成平移矩阵。点第二个点P2,确定矩形的一条边…

Studio 3T 2024.3 (macOS, Linux, Windows) - MongoDB 的专业 GUI、IDE 和 客户端,支持自然语言查询

Studio 3T 2024.3 (macOS, Linux, Windows) - MongoDB 的专业 GUI、IDE 和 客户端&#xff0c;支持自然语言查询 The professional GUI, IDE and client for MongoDB 请访问原文链接&#xff1a;https://sysin.org/blog/studio-3t/&#xff0c;查看最新版。原创作品&#xff…

CentOS 7.9 邮箱部署——Postfix+Dovecot详细

PostfixDovecot 文章目录 PostfixDovecot资源列表基础环境一、部署DNS二、部署postfix和dovecot2.1、配置postfix2.2、配置dovecot2.3、创建邮件用户 三、发送邮件测试3.1、windows安装poxmail3.2、登录邮箱3.3、发送接收邮件 四、搭建SSL认证加密4.1、生成私钥4.2、生成公钥4.…

状态转换图

根据本章开头讲的结构化分析的第3条准则,在需求分析过程中应该建立起软件系统的行为模型。状态转换图(简称为状态图)通过描绘系统的状态及引起系统状态转换的事件,来表示系统的行为。此外,状态图还指明了作为特定事件的结果系统将做哪些动作(例如,处理数据)。因此,状态图提供了…

iMX6ULL 嵌入式linux开发 | 4G无线广播终端实现方案介绍

现有的有线广播&#xff0c;如村上的大喇叭&#xff0c;需要布线&#xff0c;施工麻烦。借助现有的4G网络&#xff0c;传输音频流完全没问题&#xff0c;4G网络结合流媒体技术和MQTT消息传递实现设备间的同步推拉流。这种方案可以避免有线布线的麻烦&#xff0c;同时实现4G无线…

基于springboot+vue的智慧外贸平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Elasticsearch 分析器(内置分析器,自定义分析器,IK分析器)

Elasticsearch 分析器&#xff08;内置分析器&#xff0c;自定义分析器&#xff0c;IK分析器&#xff09; 内置分析器使用分析器自定义分析器中文分析器&#xff08;IK分析器&#xff09;安装使用添加词典 内置分析器 官网&#xff1a;https://www.elastic.co/guide/en/elasti…

如何确保大模型 RAG 生成的信息是基于可靠的数据源?

在不断发展的人工智能 (AI) 领域中&#xff0c;检索增强生成 (RAG) 已成为一种强大的技术。 RAG 弥合了大型语言模型 (LLM) 与外部知识源之间的差距&#xff0c;使 AI 系统能够提供更全面和信息丰富的响应。然而&#xff0c;一个关键因素有时会缺失——透明性。 我们如何能够…

翻译《The Old New Thing》- What‘s the deal with the EM_SETHILITE message?

Whats the deal with the EM_SETHILITE message? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071025-00/?p24693 Raymond Chen 2007年10月25日 简要 文章讨论了EM_SETHILITE和EM_GETHILITE消息在文档中显示为“未实现”的原因。这些…

Redis开发实战

单机部署安装 服务端下载&#xff0c;安装&#xff0c;启动去官网下载最新的版本&#xff1a;http://redis.io/download &#xff0c;这里用的是3.0.2解压后&#xff0c;进入解压好的文件夹redis的安装非常简单&#xff0c;因为已经有现成的Makefile文件&#xff0c;所以直接先…

NASA数据集——阿尔法喷气式大气实验甲醛(HCHO)数据

Alpha Jet Atmospheric eXperiment Formaldehyde Data 简介 阿尔法喷气式大气实验甲醛数据 阿尔法喷气式大气实验&#xff08;AJAX&#xff09;是美国国家航空航天局艾姆斯研究中心与 H211, L.L.C. 公司的合作项目&#xff0c;旨在促进对加利福尼亚、内华达和太平洋沿岸地区的…