整个帧缓冲区操作 Whole Framebuffer Operations
本节描述控制或影响整个帧缓冲区的操作。
选择缓冲区写入 Selecting Buffers for Writing
控制将每个片段颜色值写入的颜色缓冲区
void glDrawBuffer( enum buf );
void glNamedFramebufferDrawBuffer( uint framebuffer, enum buf );
- DrawBuffer 帧缓冲对象是绑定到 DRAW_FRAMEBUFFER 的帧缓冲对象。
- NamedFramebufferDrawBuffer 帧缓冲对象可以是零或帧缓冲对象的名称。如果帧缓冲对象为零,则受影响的是默认绘制帧缓冲区。
如果受影响的是默认帧缓冲区,则 buf
- NONE
- FRONT_LEFT
- FRONT_RIGHT
- BACK_LEFT
- BACK_RIGHT
- FRONT
- BACK
- LEFT
- RIGHT
- FRONT_AND_BACK
如果受影响的是帧缓冲对象,则 buf
- NONE:No buffer
- COLOR_ATTACHMENTi:帧缓冲区上特定的附着点上的图像
指定帧缓冲对象(Framebuffer Object, FBO)的绘制缓冲区,从而确定渲染过程中片段颜色写入的位置
void glNamedFramebufferDrawBuffers( uint framebuffer, sizei n, const enum *bufs );
framebuffer
: 指定帧缓冲对象的名称。如果值为0,则表示影响默认帧缓冲。n
: 表示数组bufs
中缓冲区的数量。bufs
: 一个指向枚举值数组的指针,这些值分别对应于每个片段颜色将被写入的缓冲区。- 对于影响默认帧缓冲的情况,则为 { NONE、FRONT_LEFT、FRONT_RIGHT、BACK_LEFT、BACK_RIGHT } 或者特殊值
BACK
。当使用BACK
时,n
必须为1,并且颜色值在单缓冲环境下写入左缓冲,在双缓冲环境下写入后置左缓冲。 - 如果影响的是帧缓冲对象,则为 { NONE、COLOR_ATTACHMENTi }。
- 对于影响默认帧缓冲的情况,则为 { NONE、FRONT_LEFT、FRONT_RIGHT、BACK_LEFT、BACK_RIGHT } 或者特殊值
在两种情况下,所定义的绘制缓冲区与相应的片段颜色一一对应。当超过 n
个片段颜色时,剩余的颜色对应的绘制缓冲区设置为 NONE
。
实现支持的最大绘制缓冲区数量取决于硬件和驱动程序,可以通过调用 GetIntegerv
函数查询 MAX_DRAW_BUFFERS
参数来获取支持的最大数量。
另外,除了 NONE
外,数组 bufs
指向的任何缓冲区不得重复出现。
如果片段着色器写入用户定义的输出变量,则 DrawBuffers 指定一组绘制缓冲区,这些变量定义的多种输出颜色中的每一种都会单独写入其中。 如果片段着色器未写入用户定义的输出变量,则着色器执行后的片段颜色值是未定义的,并且每个片段颜色可能不同。 如果写入了一些(但不是全部)用户定义的输出变量,则与未写入的变量对应的片段颜色的值同样未定义。
写入用户定义的输出变量的顺序未定义。 如果同一图像附加到帧缓冲区对象的多个附加点,并且将不同的值写入到绑定到这些附件的输出,则附加图像中的结果值是未定义的。 类似地,在任何其他每片段操作期间都会导致未定义的行为,其中多重附加图像可能会被多个输出写入,例如在混合期间。
DrawBuffer
和 DrawBuffers
函数是用来指示OpenGL在后续的像素颜色值写入操作中影响哪些缓冲区的。当这些函数作用于帧缓冲对象(Framebuffer Object, FBO)时,如果设置的绘制缓冲区选择了一个尚未附加图像的附件,那么相应的片段颜色将不会被写入。
指定 NONE
作为片段颜色的绘制缓冲区意味着阻止该片段颜色的写入。在单眼(monoscopic)渲染场景下,只包含左眼视图的缓冲区;而在双眼(stereoscopic)渲染场景下,则同时包含左右眼视图的缓冲区。同样地,在单缓冲(single buffered)上下文中,仅包括前缓冲(front buffer),而在双缓冲(double buffered)上下文中,则包含前后两个缓冲。
默认帧缓冲(default framebuffer)的初始状态是这样的:
- 如果存在后缓冲(back buffer),则片段颜色0的绘制缓冲区设为
BACK
; - 若无后缓冲,则设为
FRONT
; - 如果当前上下文未关联任何默认帧缓冲,则设为
NONE
。
对于帧缓冲对象(FBO),初始状态下片段颜色0的绘制缓冲区设置为 COLOR_ATTACHMENT0
。对于默认帧缓冲和帧缓冲对象而言,除片段颜色0以外的其他片段颜色的初始绘制缓冲区状态均为 NONE
。
可以通过调用 GetIntegerv
函数并设置参数 pname
为 DRAW_BUFFERi
来查询当前绑定为绘图目标的帧缓冲所选中的第i个片段颜色的绘制缓冲区。其中,DRAW_BUFFER
等价于 DRAW_BUFFER0
,通常用于查询第一个颜色附件的绘制缓冲区设置。
缓冲区更新的精细控制 Fine Control of Buffer Updates
控制颜色缓冲区写入掩码
void glColorMask( boolean r, boolean g, boolean b, boolean a );
void glColorMaski( uint buf, boolean r, boolean g, boolean b, boolean a );
- 参数 r、g、b 和 a 分别代表红色、绿色、蓝色和阿尔法通道是否允许写入。
要查询特定索引i
的绘制缓冲区的颜色写入掩码(决定哪些颜色分量可以被写入),可以调用glGetBooleani_v
函数,并设置target
为GL_COLOR_WRITEMASK
以及指定index
i
。若要查询默认绘制缓冲区(通常是索引0)的颜色写入掩码,则可通过glGetBooleanv
函数,其中pname
设为GL_COLOR_WRITEMASK
。
启用或禁用深度缓冲区对深度值的写入
void glDepthMask( boolean mask );
控制向模板缓冲区特定位的写入
void glStencilMask( uint mask );
void glStencilMaskSeparate( enum face, uint mask );
mask
的最低有效位(数量等于模板缓冲区的位数)组成一个整数掩码。掩码中的每一位对应模板缓冲区的一位。face
可以是GL_FRONT
、GL_BACK
或GL_FRONT_AND_BACK
,表示所影响的是前面板、后面板还是同时影响两者。
在渲染过程中,由前向基本体生成的片段使用前面板的模板掩码,后向基本体生成的片段则使用后面板的模板掩码(详情参见17.3.3节)。
清除操作(Clear operation)在清除模板缓冲区时始终使用前面板的模板写入掩码。
当 SAMPLE_BUFFERS 的值为 1 时,ColorMask、DepthMask 和 StencilMask 或 StencilMaskSeparate 控制多重采样缓冲区中值的修改。 颜色遮罩对颜色缓冲区的修改没有影响。如果完全禁用颜色遮罩,则仍必须组合颜色样本值(如上所述),并将结果用于替换 DrawBuffer 启用的缓冲区的颜色值。
清除缓冲区 Clearing the Buffers
清空特定缓冲区
void glClear( bitfield buf );
COLOR_BUFFER_BIT
: 清除值通过 glClearColor() 设置DEPTH_BUFFER_BIT
: 清除值通过 glClearDepth() 设置STENCIL_BUFFER_BIT
: 清除值通过 glClearStencil() 设置
void glClearColor( float r, float g, float b, float a );
void glClearDepth( double d );
void glClearDepthf( float d );
void glClearStencil( int s ); // `s`会被掩码处理。
初始化状态下,颜色缓冲区、深度缓冲区以及模板缓冲区各自的清除值如下:
- RGBA颜色缓冲区的清除值为 (0.0, 0.0, 0.0, 0.0)。
- 深度缓冲区的清除值为 1.0。
- 模板缓冲区的清除索引为 0。
清除帧缓冲对象的不同部分,包括颜色缓冲、深度缓冲和模板缓冲
void glClearBuffer{if ui}v( enum buffer, int drawbuffer, const T *value );
void glClearNamedFramebuffer{if ui}v( uint framebuffer, enum buffer, int drawbuffer, const T *value );
- 清除颜色缓冲:
- 当指定 buffer 为 COLOR 时,通过 drawbuffer 参数来选择绘制缓冲区。清除值由 value 指向的四元素向量(R, G, B, A)决定。
glClearBufferfv(buffer, drawbuffer, value);
glClearBufferiv(buffer, drawbuffer, value);
glClearBufferuiv(buffer, drawbuffer, value);
- 清除深度缓冲:
- 当 buffer 设为 DEPTH,drawbuffer 必须是 0,表示对整个深度缓冲执行清除操作。
glClearBufferfv(GL_DEPTH, 0, value);
- 清除模板缓冲:
- 当 buffer 等于 STENCIL 时,drawbuffer 是 0,针对整个模板缓冲执行清除。
- 对于 ClearBuffer* 函数,其作用于当前绑定的绘图帧缓冲;而对于
ClearNamedFramebuffer*
函数,可以明确指定帧缓冲对象的名字,甚至可以选择默认的绘图帧缓冲(通过传入0)。 glClearBufferiv(GL_STENCIL, 0, value);
同时清除帧缓冲对象的深度和模板缓冲区
void glClearBufferfi( enum buffer, int drawbuffer, float depth, int stencil );
void glClearNamedFramebufferfi( uint framebuffer, enum buffer, int drawbuffer, float depth, int stencil );
buffer
: 必须设置为 GL_DEPTH_STENCIL,表示将同时清除深度和模板组件。drawbuffer
: 必须设为零,因为此处默认清空整个深度-模板缓冲。depth
: 深度缓冲区要清除到的值;此值若有必要会进行裁剪,类似于使用 glClearDepth 时的操作。stencil
: 模板缓冲区要清除到的值;应用与 glClearStencil 相同的掩码规则。framebuffer
:明确指定帧缓冲对象的名字,甚至可以选择默认的绘图帧缓冲(通过传入0)。
清除多重样本缓冲区 Clearing the Multisample Buffer
清除操作会根据指定的清除掩码对多重采样缓冲中的颜色、深度或模板样本进行相应的清除:
- 当设置了
COLOR_BUFFER_BIT
并且绘图缓冲模式不是NONE
时,会清除多重采样的颜色样本。 - 若设置了
DEPTH_BUFFER_BIT
或STENCIL_BUFFER_BIT
,则会分别清除深度和模板样本。
Clear*Buffer*
命令不仅适用于单个常规缓冲区的清除,也同样适用于与之关联的多重采样缓冲。
此外,遮罩(Masking)和裁剪区域(Scissoring)的规定同样适用于对多重采样缓冲的清除操作,这意味着只有符合这些限制条件的缓冲样本才会被实际清除。
使帧缓冲区内容无效 Invalidating Framebuffer Contents
void glInvalidateSubFramebuffer( enum target, sizei numAttachments, const enum *attachments, int x, int y, sizei width, sizei height );
void glInvalidateNamedFramebufferSubData( uint framebuffer, sizei numAttachments, const enum *attachments, int x, int y, sizei width, sizei height );
target
: 指定要操作的目标帧缓冲;必须是DRAW_FRAMEBUFFER、READ_FRAMEBUFFER之一,或者直接使用FRAMEBUFFER,它等同于DRAW_FRAMEBUFFER。framebuffer
: 帧缓冲对象的ID。如果设置为零,则影响默认的绘制帧缓冲。numAttachments
: 表示attachments数组中附件数量的参数。attachments
: 包含了要无效化的附件类型,如GL_COLOR_ATTACHMENT0
、GL_DEPTH_ATTACHMENT
或GL_STENCIL_ATTACHMENT
等。x
,y
: 要失效矩形左下角的坐标。width
,height
: 矩形的宽度和高度。
void glInvalidateFramebuffer( enum target, sizei numAttachments, const enum *attachments );
// 等价于
glInvalidateSubFramebuffer(target, numAttachments, attachments, 0, 0, vw, vh);
void glInvalidateNamedFramebufferData( uint framebuffer, sizei numAttachments, const enum *attachments );
// 等价于
glInvalidateNamedFramebufferSubData(framebuffer, numAttachments, attachments, 0, 0, vw, vh);
其中 vw
和 vh
分别等于通过查询 MAX_VIEWPORT_DIMS
获得的最大视口宽度和高度。