渲染缓冲区对象 RBO

渲染缓冲区对象是包含图像的 OpenGL 对象。它们是专门与帧缓冲对象一起创建和使用的。它们经过优化,可用作渲染目标,而纹理可能不是,当您不需要从生成的图像中采样(即在传递后着色器中)时,它们是合乎逻辑的选择。如果需要重新采样(例如在第二个着色器通道中读回深度时),请改用纹理。渲染缓冲区对象还本机支持多重采样 (MSAA)。

内容

创造

渲染缓冲区对象是标准的 OpenGL 对象。因此,它们具有通常的glGenRenderbuffers/glDeleteRenderbuffers创建和销毁函数。它们还具有通常的glBindRenderbuffer函数来绑定它们。它需要一个目标参数,但唯一可行的目标是GL_RENDERBUFFER。

纹理对象类似,渲染缓冲区在初始化时为空。在将它们绑定到帧缓冲区对象之前,必须为渲染缓冲区分配存储。为此,只需调用:

 void glRenderbufferStorage(GLenum target​, GLenum internalformat​, GLsizei width​, GLsizei height​);

目标必须是GL_RENDERBUFFER,与将渲染缓冲区对象绑定到的目标相同。内部格式应该是用于图像的内部格式。关于图像格式的文章详细阐述了这些格式的含义。宽度和高度是渲染缓冲区的宽度和高度。

如果要创建多重采样渲染缓冲区,请使用略有不同的函数:

 void glRenderbufferStorageMultisample(GLenum target​, GLsizei samples​, GLenum internalformat​, GLsizei width​, GLsizei height​);

除了 samples 参数外,这的工作方式与原始版本完全相同。事实上,如果您为样本传递 0,它的工作原理与原始版本完全相同。samples 参数是缓冲区中的样本数。它必须小于GL_MAX_SAMPLES。

与 glTexImage2D 类似,在已经调用此函数的渲染缓冲区上调用此函数将导致它释放与上一个调用关联的任何资源并分配新存储。

注意:强烈建议您不要这样做。如果需要新的渲染缓冲区,只需删除旧对象并创建一个新对象即可。重新创建具有相同对象名称的渲染缓冲区可能会导致完整性问题,尤其是在它当时附加到另一个对象时。

使用

您可能已经注意到,与 glTexImage2D 不同,这些创建函数中没有用于实际初始化数据的参数。渲染缓冲区的内容是未定义的(它可以保存旧数据,或清零,或任何内容)也没有像glTexImage2D那样将数据上传到渲染缓冲区的功能。

不,使用渲染缓冲区对象的唯一方法是将其附加到帧缓冲区对象。将该 FBO 绑定到上下文并相应地设置绘制或读取缓冲区后,可以使用像素传输操作对其进行读取和写入。当然,您也可以渲染到它。标准的glClear函数及其好友(如glClearBuffer)也将清除适当的缓冲区。

像素传输

像素传输操作是从未格式化的内存缓冲区中获取像素数据并将其复制到由图像格式控制的 OpenGL 拥有的存储中的操作。反之亦然:将像素数据从基于图像格式的存储复制到未格式化的内存。有许多函数会影响像素传输操作的处理方式;其中许多与如何解释内存缓冲区中的信息有关。

术语

像素传输可以从用户内存到 OpenGL 内存,也可以从 OpenGL 内存传输到用户内存(用户内存可以是客户端内存或缓冲区对象)。用户存储器中的像素数据被称为打包。因此,传输到 OpenGL 内存称为解包操作,从 OpenGL 内存传输称为打包操作。

像素传输启动

有许多 OpenGL 函数可以启动像素传输操作。

像素包操作(即:从OpenGL到用户的传输):

像素解包操作(即:从用户转移到OpenGL):

  • glTexImage*:为绑定纹理对象的 mipmap 级别分配可变存储,并选择性地将像素数据写入该 mipmap 级别
  • glTexSubImage*:将用户的像素数据写入绑定纹理对象的给定mipmap的某些部分。

还有用于压缩图像格式的特殊像素传输命令。这些在技术上不是像素传输操作,因为它们只执行将内存复制到压缩纹理/从压缩纹理复制内存。但此处列出了它们,因为它们可以使用像素缓冲区进行读取和写入。

下面的讨论将忽略压缩纹理函数,因为所讨论的内容都与它们无关。

像素传输参数

除压缩纹理函数外,所有启动像素传输的函数均采用 3 个参数:

 GLenum format​, GLenum type​, void *data​

数据指针是客户端内存指针或缓冲区对象的偏移量。此操作的开关基于缓冲区对象是否绑定到 GL_PIXEL_PACK/UNPACK_BUFFER 绑定,具体取决于像素传输是打包操作还是解包操作。为了便于讨论,我们称之为“客户端内存”,无论它是否引用缓冲区对象。

格式和类型参数描述单个像素的格式。这些是指纹理对象的内部格式(在 glTexImage* 调用的情况下)。它们也没有完全描述客户端内存中的像素格式(有关详细信息,请参阅#Pixel传输参数)。

像素格式

客户端数据的像素可以是颜色值、深度值、组合深度/模具值,也可以只是模具值。颜色值最多可以有四个分量:R、G、B 和 A。深度和模具值只有一个分量。组合深度/模具值有两个组成部分。

像素传递函数的格式参数定义以下内容:

  • 从/写入数据的基本类型:颜色、深度、模具或深度/模具。这必须与正在从/写入的映像的映像格式匹配。
  • 每个像素中各个组件的顺序。
  • 对于颜色值,在读取/写入时是否应将数据转换为浮点值/从浮点值转换数据。有关更多详细信息,请参阅下文。

如果仅传输深度值,则使用GL_DEPTH_COMPONENT。如果仅传输模具值,则使用GL_STENCIL_INDEX。如果要传输组合的深度/模具值,则使用GL_DEPTH_STENCIL。后者只能与显式使用深度/模具格式的纹理一起使用,或者在具有显式深度/模具格式的帧缓冲区对象的帧缓冲像素读取操作中使用。

对于颜色格式,有更多可能性。GL_REDGL_GREEN 和 GL_BLUE 表示传输这些特定组件的数据(GL_ALPHA不能使用)。GL_RG按该顺序表示两个分量,即 R 和 G。GL_RGBGL_BGR代表这三个组成部分,GL_BGR顺序相反。GL_RGBAGL_BGRA代表这些组成部分;后者颠倒了前三个组件的顺序。这些是唯一支持的颜色格式(请注意,有一些解决方法)。

上述所有格式说明符都隐式表示像素是浮点值。对于整数像素类型,使用浮点格式意味着将假定像素是规范化整数。因此,它们将被解释为规范化值。

如果要将积分数据传输为积分图像格式,则必须在像素格式后加上“_INTEGER”。这表示客户端像素数据表示整数,而不是(可能规范化的)浮点数。对于完整的图像格式,应仅使用“_INTEGER”格式后缀。

注意:format 参数的许多合法值看起来很像图像格式他们不是!不要混淆两者。虽然在许多情况下,它们必须在某种程度上匹配,但它们做完全不同的事情。如果始终对纹理使用大小图像格式,则它们将永远不匹配,因为 format 参数不能具有大小。

像素类型

像素传递函数的类型参数定义由格式定义的每个组件占用多少位。有两种类型的类型值:将每个组件指定为单独字节值的值,或将多个组件打包到单个值中的值。

例如,GL_RGBA格式GL_UNSIGNED_BYTE类型相结合意味着每个像素将占用 4 个无符号字节。第一个字节是 R,第二个字节是 G,依此类推。GL_UNSIGNED_BYTE作为类型是每组件类型;每个组件都有此大小。

可能的每组件格式使用 OpenGL 类型的枚举器

  • GL_(UNSIGNED_)字节:1 字节
  • GL_(UNSIGNED_)短:2 字节
  • GL_(UNSIGNED_)国际: 4 字节
  • GL_HALF_FLOAT:2 字节
  • GL_FLOAT:4 字节

但是,有一些有用的像素数据的打包排列,其中每个组件都打包到非字节长度值中。一个常见的例子是 16 位 RGB 颜色,其中红色和蓝色分量占 5 位,绿色分量为 6。

为了在 OpenGL 中指定此类数据,我们使用打包类型值。打包类型字段指定如下:

 GL_[base type​]_[size1​]_[size2​]_[size3​]_[size4​](_REV​)
 

括号表示可选值。

类型是完全打包的值的 OpenGL 类型枚举器名称。这些值始终是无符号整数类型,其大小足以容纳整个颜色。因此,5-6-5 RGB 颜色将存储到 16 位无符号整数中,这意味着使用 UNSIGNED_SHORT

大小值按该顺序表示组件的大小(以位为单位)。我们希望第一个组件为 5,第二个组件为 6,第三个组件为 5。由于没有第四个分量,因此没有 size4 值。

因此,表示 5-6-5 颜色的类型是GL_UNSIGNED_SHORT_5_6_5

默认情况下,组件的布局从 msb(最高有效位)到 lsb(最低有效位)。但是,如果类型末尾有一个_REV,则组件顺序将颠倒,并且它们从 lsb 到 msb 进行布局。在“REV”模式下,第一个组件(与前 5 个组件匹配的组件)将进入格式指定的最后一个组件。

因此,如果我们GL_RGB格式GL_UNSIGNED_SHORT_5_6_5_REV类型相结合,则蓝色分量将进入颜色值的前 5 位。请注意,这在功能上与带GL_UNSIGNED_SHORT_5_6_5GL_BGR相同。

除 2 种特殊情况外,格式中的组件数必须与打包类型提供的组件数匹配。一些打包类型甚至对组件排序或格式可以使用的组件类型施加了限制。

OpenGL定义了可能的大小。它们是:

  • 3_3_2 (2_3_3_REV):无符号字节。仅与GL_RGB一起使用。
  • 5_6_5 (5_6_5_REV):无符号短裤。仅与GL_RGB一起使用。
  • 4_4_4_4 (4_4_4_4_REV):无符号短裤。
  • 5_5_5_1 (1_5_5_5_REV):无符号短裤。
  • 8_8_8_8 (8_8_8_8_REV):无符号整数。
  • 10_10_10_2 (2_10_10_10_REV):无符号整数。
  • 24_8(无_REV):无符号整数。仅与GL_DEPTH_STENCIL一起使用。
  • 10F_11F_11F_REV(无非 REV):无符号整数。这些表示浮点数,只能与GL_RGB一起使用。这只能用于具有GL_R11F_G11F_B10F图像格式的图像。
  • 5_9_9_9_REV(无非 REV):无符号整数。仅与GL_RGB一起使用;最后一个组件(5.它是 REV)不直接映射到颜色值。它是一个共享指数。仅将其用于具有GL_RGB9_E5图像格式的图像。

有一个非常特殊的打包类型字段。这是GL_FLOAT_32_UNSIGNED_INT_24_8_REV。这只能与使用 GL_DEPTH32F_STENCIL8 图像格式的图像一起使用。它表示两个 32 位值。第一个值是 32 位浮点深度值。第二个将 32 位整数值分解为 24 位未使用的空间,后跟 8 位模板。

像素传输参数

格式类型参数仅描述单个数据像素的表示形式。客户端内存中数据的布局由各种全局参数控制,这些参数由 glPixelStore[if] 函数设置,并结合客户端的字节序

打包和解包操作(分别从 OpenGL 内存读取和写入 OpenGL 内存)使用不同的参数集来控制像素数据的布局。所有打包(读取)参数都以 GL_PACK 开头,而所有解包(写入)参数都以 GL_UNPACK 开头。这两种操作具有相同的参数,具有相同的含义,但它们只影响该特定类型的操作。

GL_PACK_ALIGNMENT参数不会影响任何上传到 OpenGL 内存的上传(解包)。

像素布局

像素数据的布局如下。

数据按“行”排列。每行表示像素传输中的水平跨度,具体取决于传输操作中的宽度参数。行中的每个像素都与其他像素直接相邻。所以像素之间没有空间。

如果格式为 GL_RGB并且类型为 GL_UNSIGNED_BYTE,则像素的大小为 3。宽度为 16 像素意味着单行像素的总字节长度为 48 字节。如果格式GL_RGBA,则像素大小将为 4,行的大小将为 64。

如果像素传输操作是二维或更高的,则会出现高度行数,其中高度是像素传递函数的参数。第一行是 OpenGL 空间中图像的底部。下一行是上面的行,依此类推。

但是,与像素不同,行不一定在内存中直接连续。每行上的字节必须以特定对齐方式开始。此对齐方式由用户使用 GL_PACK/UNPACK_ALIGNMENT 参数定义。此值可以是 1、2、4 或 8。

例如,如果格式为 GL_RGB类型为 GL_UNSIGNED_BYTE宽度为 9,则每行的长度为 27 字节。如果对齐方式为 8,则表示第二行在第一行之后 32 个字节开始。如果对齐方式为 1,则第二行从第一行之后的 27 个字节开始。

如果像素传输操作是三维的,则存在深度以及宽度高度。这不会改变布局;它只改变有多少行。不是高度行,而是高度 * 深度行。

字节序问题

客户端像素数据(即“打包”数据)始终按客户端字节排序。因此,在小端计算机上,无符号整数在客户端内存中以小端顺序表示。在大端计算机上,无符号整数以大端顺序表示。

如果你想改变这一点,你可以使用glPixelStore将GL_PACK/UNPACK_SWAP_BYTES设置为GL_TRUE。这将导致 OpenGL 对类型值执行字节交换,从平台的本机字节序顺序到 OpenGL 预期的顺序。

为了阐明如何根据格式类型GL_PACK/UNPACK_SWAP_BYTES和客户端字节顺序确定客户端内存布局,下面是一些使用 RGBA 8888 像素数据的示例:

客户端内存布局示例
顺序(在客户端内存中)格式类型SWAP_BYTES客户端字节顺序
吉巴吉巴UNSIGNED_BYTE<任何><任何>
吉巴吉巴UINT_8_8_8_8_REV小端序
BGRABGRAUNSIGNED_BYTE<任何><任何>
BGRABGRAUINT_8_8_8_8_REV小端序
ABGR吉巴UINT_8_8_8_8小端序
阿格布BGRAUINT_8_8_8_8小端序
ABGR吉巴UINT_8_8_8_8_REV小端序
ABGR吉巴UINT_8_8_8_8_REV大端序

子图像选择

待办事项:此部分需要填写。

格式转换

用户指定的像素必须在用户指定的格式(具有格式和类型)和由图像的图像格式控制的内部表示之间转换。

客户端内存中的像素要么采用某种形式的浮点表示形式,要么采用整数值。浮点形式包括规范化整数,无论是有符号的还是无符号的。如果 format 参数未指定“_INTEGER”后缀,则假定所有整数值都是规范化整数。如果 format 参数指定“_INTEGER”,但类型为浮点类型(GL_FLOATGL_HALF_FLOAT 或类似类型),则会产生错误并且像素传输失败。此外,如果指定了“_INTEGER”但图像格式不是整数,则传输将失败。

将数据传输到图像时,像素值将转换为浮点值或整数值。如果图像格式已规范化,则写入的值将固定为 [0, 1] 并规范化。如果图像格式是积分格式,则逐字复制积分输入值。

对于写入客户端数据,此过程是相反的。

注意:如果OpenGL实现可以侥幸逃脱,它将不会进行转换。因此,如果将规范化整数上传到规范化整数内部格式,则实现不会打扰转换,因为它将是无操作。始终尝试将给定格式与图像格式匹配。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值