在之前的章节,所有的物体都是中规中矩的显示的,只考虑了光照对物体的影响,那假设想要显示特殊的效果该怎么操作呢?例如马赛克风、将所有的物体都显示为黑白色,就像上世纪80年代的灰白电视一样,又或者说将整个场景渲染到一张泛黄的纸上以体现出年代感……
当然是修改着色器,事实上,很多地方都是这么做的,不过有些情况下,场景中的物体和对应的着色器都不少,若是想要整个场景(视口)体现出某个效果,就需要借助别的方法了
接下来,就是想办法往帧缓冲里添加附件,一个附件就是一个内存地址,这个内存地址里面包含一个为帧缓冲准备的缓冲,它可以是个图像,当创建一个附件的时候我们有两种方式可以采用:纹理或渲染缓冲(renderbuffer)对象
三、纹理附件
先创建一个帧缓冲的纹理:
GLuint getAttachmentTexture()
{
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 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);
glBindTexture(GL_TEXTURE_2D, 0);
return textureID;
}
这里和之前的定义纹理有三点不同:
- 纹理的大小为屏幕的大小(测试是800 x 600)
- 不需要传递data参数,只分配内存而不去填充,纹理填充会在渲染到帧缓冲的时候去做
- 不再关心环绕方式或者Mipmap
创建完纹理后,紧接着就是把它附加到帧缓冲上:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
glFramebufferTexture2D 函数参数:
- target:帧缓冲类型的目标,在前一章提到过
- attachment:附加的附件的类型,目前附加的是一个颜色附件
- textarget:你希望附加的纹理类型,目前总是GL_TEXTURE_2D
- texture:附加的实际纹理,用上面生成的texture
- level:Mipmap level
扩展:如果想要附加深度缓冲和模板缓冲,就需要在生成纹理时是用 GL_DEPTH24_STENCIL8 的纹理格式和内部类型,这样的纹理每32位数值就包含了24位的深度信息和8位的模板信息,不过这里暂时不需要考虑,大致用法如下:
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
四、缓冲对象附件(RBO)
纹理是一个帧缓冲的可行附件类型,除此之外,OpenGL还有渲染缓冲对象(Renderbuffer objects),渲染缓存是为离线渲染而新引进的,它容许将一个场景直接渲染到一个渲染缓存对象中,而不是渲染到纹理对象中
RBO并不能单独使用,必须配合FBO,与opengl缓冲区对应,RBO可以存放颜色、深度、模板数据
和纹理图像一样,渲染缓冲对象也是一个缓冲,它可以是一堆字节、整数、像素或者其他东西。渲染缓冲对象的一大优点是,它以OpenGL原生渲染格式储存它的数据,因此在离屏渲染到帧缓冲的时候,这些数据时已经被优化过的了,这样的话,写入或把它们的数据简单地到其他缓冲的时候非常快,之前提到的函数glfwSwapBuffers交换缓冲区,同样以渲染缓冲对象实现
glGenRenderbuffers(1, &RBO);
glBindRenderbuffer(GL_RENDERBUFFER, RBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
由于渲染缓冲对象通常是只写的,它们经常作为深度和模板附件来使用,因为大多数时候并不需要从深度和模板缓冲中读取数据
- glRenderbufferStorage:创建一个深度和模板渲染缓冲对象,第2个参数指定渲染缓冲区的颜色可渲染、深度可渲染或模板可渲染格式,后面两个参数为指定缓冲区的宽和高
- glFramebufferRenderbuffer:和glFramebufferTexture2D目的一样,将渲染缓冲区附加到当前帧缓冲区上
到现在帧缓冲就做好了:绑定了颜色缓冲,也有了深度和模板缓冲、如果遗漏了某个缓冲,那么对应的测试就不会工作,因为当前绑定的帧缓冲里没有对应的缓冲
好了,现在回到上篇的目的:
定义一个帧缓冲并且启用,然后走之前正常的渲染逻辑,这个时候,本应该在屏幕中展示的图象就被渲染到了自己定义的帧缓冲中,接下来,我们再想办法将帧缓冲中的数据转变为纹理,并且对这张纹理在默认帧缓冲中进行渲染显示
在此,目的就变成了:绑定为上面自己定义的帧缓冲,像往常那样渲染场景,之后绑定回默认帧缓冲,简单的绘制一个全屏四边形,用自己的帧缓冲的颜色缓冲作为他的纹理