OpenGL 编程指南(第八版)学习笔记——4颜色 像素和帧缓存
缓存及其用途
- 所有图形程序的共同目标,在屏幕上绘制图像(或者绘制到屏幕的一处缓存中)。
- 帧缓存(通常就是屏幕)是由矩形的像素数组组成的,每个像素都可以在图像对应的点上显示一块方形的颜色值。
- 颜色缓存只是记录像素信息的多个缓存中的一个。实际上,一个像素可能会关联多个颜色缓存,也就是渲染缓存(renderbuffer)。
- OpenGL系统中通常包含以下几种类型的缓存:
- 一个或多个可用的颜色缓存(color buffer)
- 深度缓存(depth buffer)
- 模板缓存(stencil buffer)
- 默认帧缓存总是会包含一个双重缓存(double-buffered)机制的颜色缓存。
颜色缓存
-
双缓存机制:直接在窗口中显示的前置缓存(front-buffer),以及用来渲染新图像的后备缓存(back-buffer)。当执行交换缓存操作,如
glutSwapBuffers()
。注:只有默认帧缓存的主颜色缓存可以使用双重缓存的特性。
GL还可以支持立体显示,即每个颜色缓存都会划分出左颜色缓存,右颜色缓存。
深度缓存
- 深度缓存为每个像素保存一个深度值,它可以用来判断3位空间中物体的可见性。
- 这里的深度! 是物体与观察者眼睛的距离,因此深度值较大的像素会被深度值较小的像素覆盖。
- 深度缓存的特性也可以通过 “深度测试”(z-buffer)来改变。
模板缓存
- 模板缓存用来限制屏幕特定区域的绘制。
- 相当于一个ROI。或者说是一个带有孔洞花纹的硬纸板。
缓存的清除
- 通常在渲染完一帧后,最常见的操作就是清除缓存。
- 设置不同缓存的清除值
void glClearColor(GLclampf red,GLclamf green,GLclampf bule,GLclampf alpha);
void glClearDepth(GLclampd depth);
void glClearDepthf(GLclampf depth);
void glClearStencil(GLint s);
// GLclampf和GLclampd类型就是截断后的GLfloat和GLdouble,需要截断到0.0-1.0区间。
- 在设置清除值之后,可以使用
glClear
来执行清除操作
void glClear(GLbitfild mask);
// 清除特定的缓存。mask的值是以下几种枚举量之间按位或操作的结果。
缓存的掩码
- 在GL向颜色、深度或者模板缓存中写入数据之前,可以对数据执行一次掩码操作。
// 设置用于写入不同缓存的掩码
// 先略过。
颜色与OpenGL
- 片元着色器负责设置每个片元的颜色。
- 有多种方式来完成这个操作。
- 片元着色器可以不借助任何”外部”数据。比如:设置完成某个常量颜色值
- 每个输入的顶点都会提供一个附加的颜色数据。
- 颜色的补充 —— 不是具体的颜色值 —— 可以通过片元着色器中计算来生成颜色值。
- 外部数据,例如数字图像等,也可以在片元着色器中引用,用于查找颜色值。这些数据保存在
纹理贴图
当中,并且需要用到纹理映射技术(texture mapping)。
颜色表达与OpenGL
- OpenGL是如何使用颜色值的:
- GL内部使用一个浮点数来组成一个像素的颜色。
归一化数值
(normalized value)
顶点颜色
- 颜色数据也必须保存到
顶点缓存对象VBO
光栅化
- 光栅化的主要职责就是判断空间的哪个部分被几何体(点,线,三角形)覆盖。
- 光栅化相当于一个片元生命的开始。
多重采样
- 多重采样是一种对几何图元的边缘进行平滑的技术。通常叫做 反走样。
- 多重采样的工作方式就是对每个像素的几何图元进行多次采样。此时,每个像素点不会只保存单个颜色值,而是记录多个样本值。
帧缓存对象
- 通过帧缓存对象(frame buffer object),我们可以创建自己的帧缓存,并且将他们绑定到渲染缓存(render buffer),将数据拷贝的消耗最小化,同时对性能进行优化。
- 帧缓存对象对下面几种技术非常有意义:
- 离屏渲染技术;
- 纹理贴图的更新;
- 缓存乒乓技术(这是GPGPU当中用到的一种数据传输的方式);
- 窗口系统的缓存 vs 自己创建的帧缓存
- 分配一个程序创建的帧缓存对象:
void glGenFramebuffers(GLsizei n,GLuint* ids);
// 分配n个未使用的帧缓存对象的名称,并且存储在ids中。
- 绑定帧缓存对象:
void glBindFramebuffer(GLenum target,GLuint framebuffer);
// 设置一个可读或者可写的帧缓存。
// target为 GL_DRAW_FRAMEBUFFER 绘制时的目标帧缓存。
// target为 GL_READ_FRAMEBUFFER 读取操作的数据源
// target为 GL_FRAMEBUFFER 可读读写
// 若 framebuffer为0! 表示将目标绑定到默认的窗口系统帧缓存,或者设置为一个glGenFramebuffers()所生产的帧缓存对象。
- 释放程序分配的帧缓存
void glDeleteFramebuffers(GLsizei n,const GLuint *ids);
- 判断是否是程序分配的帧缓存对象:
GLboolean glIsFramebuffer(GLuint framebuffer);
- 设置帧缓存对象的参数。前提是帧缓存对象还没有进行关联,否则这些参数需要通过帧缓存附件来设置。
void glFramebufferParameteri(GLenum target,GLenum panme,GLint param);
- 帧缓存对象创建之后,还有很多工作要完成。还需要指定一处空间用作绘制操作或者读取操作,这处空间称作
帧缓存附件(framebuffer attachment)
。
渲染缓存
- 渲染缓存(renderbuffer)是OpenGL所管理的一处高效的内存区域,它可以存储格式化的图像数据。渲染缓存中的数据只有关联到一个帧缓存对象之后才有意义。
- 创建新的渲染缓存时可以调用
glGenRenderBuffers()
函数。
void glGenRenderbuffers(GLsizei n,GLuint* ids);
- 帧缓存对象经过绑定之后才可以修改自己的状态。
void glBindRenderbuffer(GLenum target,GLuint renderbuffer);
创建渲染缓存的存储空间
glBindRenderbuffer
第一次调用后,只是创建了一个所有状态均为默认值的渲染缓存,这时候它还没有分配存储空间来存储图像信息。- 分配存储空间并且设置图像格式。
glRenderbufferStorage
或者glRenderbufferStorageMultisample
void glRenderbufferStorage(GLenum target,GLenum internalformat,GLsizei width,GLsizei height);
void glRenderbufferStorageMultisample(GLenum target,GLsizei samples,GLenum internalformat,GLsizei width,GLsizei height);
-
示例:创建一个256x256的RGBA颜色渲染缓存
glGenRenderbuffers(1,&color); glBindRenderbuffer(GL_RENDERBUFFER,color); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA,256,256);
帧缓存附件
- 当开始渲染时,可以将渲染的结果保存到以下几个地方:
- 如果使用多重渲染目标(multiple render target),则创建图像到颜色缓存,设置是多个颜色缓存
- 将遮挡信息保存到深度缓存
- 将逐像素的渲染掩码保存到模板缓存
- 这些缓存类型都表示了一种帧缓存的附件。
附件名称 | 描述 |
---|---|
GL_COLOR_ATTACHMENTi | 第i个颜色缓存。i从0开始 |
GL_DEPTH_ATTACHMENT | 深度缓存 |
GL_STENCIL_ATTACHMENT | 模板缓存 |
GL_DEPTH_STENCIL_ATTACHMENT | 特殊的附件类型。用于保存压缩后的深度-模板缓存 |
- 将渲染缓存关联到帧缓存对象的情形:
- 使用
glFramebufferRenderbuffer
void glFramebufferRenderbuffer(GLenum target,GLenum attachment,GLenum renderbuffertarget,GLuint renderbuffer);
// 将渲染缓存renderbuffer关联到当前绑定的帧缓存对象的附件attachment上。
- 帧缓存的完整性:
- 调用
glCheckFramebufferStatus()
函数来检查
- 调用
选择颜色缓存来进行读写操作
glDrawBuffer()
或者glDrawBuffers()
来选择要写入的缓存glReadBuffer()
来选择读取用的缓存,并作为glReadPixels()、glCopyTexImage*()和glCopyTexSubImage*()等函数的数据源
像素数据的读取与拷贝
- 当渲染工作结束之后,我们可以需要获取渲染后的图像作他用。这种情形下可以使用
glReadPixels()
函数读取帧缓存中的像素。 - 然后将像素返回到应用程序中。可以分配一处内存空间来保存返回的像素。
拷贝像素矩阵
- 如果需要在一块缓存的不同区域进行拷贝,或者在不同的帧缓存之间进行拷贝。
- 可以使用
glBItFramebuffer
函数。
void glBlitFramebuffer(GLint srcX0,GLint srcY0,GLint srcX1,GLint stcY1,GLint dstX0,GLint dstY0,GLint dstX1,GLint dstY1,GLbitfield buffers,GLenum filter);