【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 场景中添加一个面板,从不同角度显示该场景。 (解决方案)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

横截面分位数回归

一、分位数回归简介 分位数回归&#xff08;英语&#xff1a;Quantile regression&#xff09;是回归分析的方法之一。最早由Roger Koenker和Gilbert Bassett于1978年提出。一般地&#xff0c;传统的回归分析研究自变量与因变量的条件期望之间的关系&#xff0c;相应得到的回归…

AI时代的服装设计师--AIGC

AI时代的服装设计师--AIGC AIGCAIGC设计能替代真正的设计师吗森马T恤设计AIGC优势、优化 本文记录于去年参加的一次森马T恤设计活动的感受。 AIGC 可以说&#xff0c;近期以来&#xff0c;随着ChatGPT的不断发展&#xff0c;从ChatGPT-3到ChatGPT-4的飞速发展&#xff0c;AIGC…

Windows和Linux系统部署Docker(2)

目录 一、Linux系统部署docker 前置环境&#xff1a; 1.安装需要的软件包&#xff0c; yum-util 提供yum-config-manager功能 2.添加阿里云 docker-ce 仓库 3.安装docker软件包 4.启动 docker并设置开机自启 5.查看版本&#xff1a; 二、windows系统部署docker 1.查看…

Type ‘null‘ is not assignable to type ‘T‘. - ArkTSCheck

设置泛型将参数配置为 null 时抛出了如下异常: Type null is not assignable to type T. T could be instantiated with an arbitrary type which could be unrelated to null. <ArkTSCheck> 解决办法 在 null 后面添加 ! 即可,以表示该值不会为 null data: T null! 以…

Qt 基于FFmpeg的视频转换器 - 转GIF动图

Qt 基于FFmpeg的视频转换器 - 转GIF动图 引言一、设计思路二、核心源码三、参考链接 引言 gif格式的动图可以通过连续播放一系列图像或视频片段来展示动态效果&#xff0c;使信息更加生动形象&#xff0c;可以很方便的嵌入到网页或者ppt中。上图展示了视频的前几帧转为gif动图的…

基于Paraformer的alpha-token强制对齐

1. 基本原理 CIF 作为Parafoemr的核心模块&#xff0c;用于预测字数和生成声学向量&#xff0c;从而实现了单轮非自回归解码。其中字数的预测主要通过encoder输出系数alpha的累计得分&#xff0c;满足通关阈值β1.0即可产生一个token&#xff0c;其中alpha曲线在一定程度上呈现…

CSS:浮动

▐ 文档流&#xff1a; 由于网页默认是一个二维平面&#xff0c;当我们在网页中一行行摆放标签时&#xff0c;块标签会独占一行&#xff0c;行标签则只占自身大小&#xff0c;这种情况下要实现网页布局就很麻烦了&#xff0c;所以我们就需要通过一些方法来改变这种默认的布局方…

centos7离线安装pthon3.8

centos7离线安装pthon3.8 因服务器无外网环境&#xff0c;所以事先需要把所有离线的依赖都准备好。 安装前的准备 先在有外网环境的机器上准备依赖 安装 centos-release-scl 第三方yum源 yum install centos-release-scl安装 yum 依赖下载插件 yum install yum-plugin-do…

GO语言 gin框架 简述

原文地址 基本路由 Go语言中文文档 一、简介 Gin是一个golang的轻量级web框架&#xff0c;性能不错&#xff0c;API友好。 Gin支持Restful风格的API&#xff0c;可以直接从URL路径上接收api参数或者URL参数&#xff0c;也可是使用json或者表单 数据绑定的方式接收参数。 Gin响…

【传知代码】BERT论文解读及情感分类实战-论文复现

文章目录 概述原理介绍BERT模型架构任务1 Masked LM&#xff08;MLM&#xff09;任务2 Next Sentence Prediction (NSP)模型输入下游任务微调GLUE数据集SQuAD v1.1 和 v2.0NER 情感分类实战IMDB影评情感数据集数据集构建模型构建 核心代码超参数设置训练结果注意事项 小结 本文…

AIOps在线评测基准首阶段建设完成,面向社区发布真实运维数据!

本文根据必示科技算法研究员、产品总监聂晓辉博士在2024 CCF国际AIOps挑战赛线下宣讲会上的演讲整理成文。 2024年1月份OpenAIOps社区成立&#xff0c;随着越来越多的社区成员加入&#xff0c;各项工作在有条不紊的推进中。在线评测基准系统&#xff08;AIOps Live Benchmark&a…

积鼎CFDPro水文水动力模型,专为中小流域洪水“四预”研发的流体仿真技术

水动力模型与水文模型是水利工程与水文学研究中不可或缺的两大工具。水动力模型着重于流体运动的动力学机制&#xff0c;通过一系列方程组捕捉水流的时空变化&#xff0c;而概念性水文模型则侧重于流域尺度的水文循环过程&#xff0c;利用物理概念与经验关系进行近似模拟。两者…

Windows系统部署YOLOv5 v6.1版本的训练与推理环境保姆级教程

文章目录 一 概述二 依赖环境(prerequisites)2.1 硬件环境2.2 软件环境 三 环境安装3.1 创建并激活虚拟环境3.2 安装Pytorch与torchvision3.3 校验Pytorch安装3.4 下载 YOLOv5 v6.1 源码3.5 安装 YOLOv5 依赖3.6 下载预训练模型3.7 安装其他依赖3.8 测试环境安装3.9 测试训练流…

jupyter notebook更改位置

1.找到jupyer的配置文件 一般在c盘用户的.jupter文件夹下 2. 用记事本打开这个配置文件&#xff0c;定位到c.NotebookApp.notebook_dir /path_to_your_directory 替换你的位置 3.找到jupyer图标的位置&#xff0c;打开属性 添加要存放的位置在目标文件的末尾&#xff0c;重新…

一个全面了解Xilinx FPGA IP核的窗口:《Xilinx系列FPGA芯片IP核详解》(可下载)

随着摩尔定律的逐渐放缓&#xff0c;传统的芯片设计方法面临着越来越多的挑战。而FPGA以其并行处理能力和可编程性&#xff0c;为解决复杂问题提供了新的途径。它允许设计者在同一个芯片上实现多种不同的功能模块&#xff0c;极大地提高了资源的利用率和系统的综合性能。 FPGA…

HCIA-ARP

ARP的由来 ARP这一种协议它会是在我们HCIA中第一个需要完全掌握的一个协议&#xff0c;不然对于数据通讯来说大家都会一直觉得很绕圈 协议栈&#xff0c;网线&#xff0c;网卡&#xff0c;它们组成了我们最小的数据通信的小脉络注&#xff1a;可以了解ARP攻击&#xff08;冒充访…

文献解读-群体基因组第二期|《中国人群中PAX2新生突变的检测及表型分析:一项单中心研究》

关键词&#xff1a;应用遗传流行病学&#xff1b;群体测序&#xff1b;群体基因组&#xff1b;基因组变异检测&#xff1b; 文献简介 标题&#xff08;英文&#xff09;&#xff1a;Detection of De Novo PAX2 Variants and Phenotypes in Chinese Population: A Single-Cente…

new CCDIKSolver( OOI.kira, iks ); // 创建逆运动学求解器

demo案例 new CCDIKSolver(OOI.kira, iks); 在使用某个特定的库或框架来创建一个逆运动学&#xff08;Inverse Kinematics, IK&#xff09;求解器实例。逆运动学在机器人学、动画和计算机图形学等领域中非常重要&#xff0c;它用于根据期望的末端执行器&#xff08;如机器人的…

建设现代智能工业-智能化、数字化、自动化节能减排

建设现代智能工业-智能化节能减排 遵循“一体化”能源管理(Integrated Energy Management)的设计宗旨&#xff0c;集成城市各领域(如工业.交通、建筑等&#xff09;的能源生产和消费信息&#xff0c;面向城市政府、企业、公众三类实体&#xff0c;提供“一体化”的综合能源管理…

uniapp h5项目切换导航栏及动态渲染按钮颜色

1.效果图 2.html,动态渲染按钮样式---三元判断 <!-- 切换栏 --><view class"statusList"><block v-for"(item,index) in list" :key"index"><view class"swiper-tab-list" :class"current item.id?activ…