本文正在参与「金石计划」
FBO离屏烘托
到目前为止,咱们现已运用过了几个buffer:可写入色彩值的色彩缓冲区,能够写入和测验深度的深度缓冲区以及模板缓冲区。这些缓冲区被存放在GPU的某内存中,这块内存便是一个帧缓冲。
之前咱们运用的都是屏幕供给的默许缓冲区,默许缓冲区制作后的色彩和深度等都会呈现在屏幕上。
OpenGL还供给了一个更加灵敏的API,能够让开发者自定义帧缓冲区,然后写入到对应的纹路或者Render缓冲区目标中(RBO):这便是咱们要说的离屏烘托。
顾名思义,离屏烘托便是不会制作在屏幕上的制作操作,而是在GPU后台进行制作,并写入到纹路或者RBO中。
制作后的纹路或者RBO又能够用来制作到屏幕上,相当于做了一个中间层的转化。
什么是RBO?
RBO(Render Buffer Object)即烘托缓冲区目标,是一个由应用程序分配的 2D 图画缓冲区。烘托缓冲区能够用于分配和存储深度或者模板值,能够用作 FBO 中的深度或者模板附着。
为什么要运用FBO?
首先,在默许状况下,OpenGL通过制作到窗口的默许帧缓冲区,完成纹路的烘托,但是这种状况下只能适配纹路尺度小余帧缓冲区的状况,假如纹路太大就没法正常制作。
其次:咱们知道GPU的图画处理才能是很强壮的,在制作的过程中,也不是所有的场景都需要制作到屏幕上的,如图画的前期处理,缩放,滤镜等耗时操作都能够运用FBO来做预处理,这样能够大大提高GPU在后台的利用率。
FBO如何运用?
FBO的运用过程一般有以下几个:
-
1.创立并初始化FBO:
- 1.1 : 创立2D纹路用于fbo的色彩依靠:glGenTextures->glBindTexture
- 1.2:创立FBO:glGenFramebuffers
- 1.3:绑定FBO:glBindFramebuffer(GL_FRAMEBUFFER,fbo_id);
- 1.4 : 绑定FBO纹路:glBindTexture->glFramebufferTexture2D->glTexImage2D
- 1.5 : 检测FBO的完整性:glCheckFramebufferStatus
- 1.6:解绑纹路:glBindTexture,解绑FBO:glBindFramebuffer
//1.1:创立2D纹路用于fbo的色彩依靠 glGenTextures(1,&m_FboTextureId); glBindTexture(GL_TEXTURE_2D,m_FboTextureId); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, GL_NONE); // 1.2:创立FBO:glGenFramebuffers glGenFramebuffers(1,&m_FboId); // 1.3:绑定FBO:glBindFramebuffer(GL_FRAMEBUFFER,fbo_id); glBindFramebuffer(GL_FRAMEBUFFER,m_FboId); // 1.4: 绑定FBO纹路:glBindTexture->glFramebufferTexture2D->glTexImage2D glBindTexture(GL_TEXTURE_2D,m_FboTextureId); glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,m_FboTextureId,0); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,imageWidth,imageHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,nullptr); // 1.5: 检测FBO的完整性:glCheckFramebufferStatus if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) { LOGCATE("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE"); return ; } // 1.6:解绑纹路:glBindTexture,解绑FBO:glBindFramebuffer glBindTexture(GL_TEXTURE_2D,GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
-
2.运用FBO目标创立的纹路或者RBO目标进行烘托:
- 2.1:离屏烘托
- 2.1.1:绑定FBO并运用离屏烘托的着色器目标:glBindFramebuffer->glUseProgram(fboShaderObj);
- 2.1.2:绑定VAO并设置纹路目标:glBindVertexArray->glUniform1i
- 2.1.3:开端离屏烘托纹路:glDrawElements
- 2.1.4:解绑VAO:glBindVertexArray->glBindFramebuffer
- 2.1.5:解绑FBO:glBindFramebuffer(GL_FRAMEBUFFER, 0);
//2.1:离屏烘托 { glPixelStorei(GL_UNPACK_ALIGNMENT,1); glViewport(0,0,imageWidth,imageHeight); //2.1.1:绑定FBO并运用离屏烘托的着色器目标:glBindFramebuffer,glUseProgram(fboShaderObj); glBindFramebuffer(GL_FRAMEBUFFER,m_FboId); glUseProgram(fbo_ProgramObj); //2.1.2:绑定VAO并设置纹路目标:glBindVertexArray->glUniform1i glBindVertexArray(m_fboVAO); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,textureID); glUniform1i(glGetUniformLocation(fbo_ProgramObj,"s_TextureMap"),0); //2.1.3:开端离屏烘托纹路:glDrawElements glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (const void *)0); //2.1.4:解绑VAO:glBindVertexArray->glBindFramebuffer glBindTexture(GL_TEXTURE_2D,GL_NONE); glBindVertexArray(GL_NONE); //2.1.5:解绑FBO:glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER,0); }
离屏烘托会得到一个新的纹路目标,能够供给给一般烘托运用。
留意:在离屏烘托的时候咱们将烘托的视口(glViewport)设置为了原纹路贴图的宽高,然后将离屏烘托极点数据设置为1为单位,这样就能够烘托出一个和原纹路贴图相同尺度的纹路目标。
- 2.2:一般烘托
- 2.2.1:运用一般着色器目标:glUseProgram(commonShaderObj);
- 2.2.2:绑定VAO并设置纹路。运用2.1中离屏烘托的FboTextureId纹路id
- 2.2.3:开端制作:glDrawElements
- 2.2.4:解绑VAO
//2.2:一般烘托 { glViewport(0, 0, mScreenWidth, mScreenHeight); //2.2.1:运用一般着色器目标:glUseProgram(commonShaderObj); glUseProgram(programObj); //2.2.2:绑定VAO并设置纹路。运用2.1中离屏烘托的FboTextureId纹路id glBindVertexArray(VAO); glUniform1i(glGetUniformLocation(programObj, "s_TextureMap"), 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_FboTextureId); //2.2.3:开端制作:glDrawElements glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0); // 2.2.4:解绑VAO glBindTexture(GL_TEXTURE_2D,GL_NONE); glBindVertexArray(GL_NONE); }
-
极点数据:
//0.一般烘托极点数据
GLfloat vertices[] = {
//极点 //纹路坐标
-0.5f, -0.5f, 0.0f,0.0f, 0.0f,//V0
0.5f,-0.5f, 0.0f,1.0f,0.0f,//V1
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,//V2
-0.5f, 0.5f, 0.0f,0.0f, 1.0f//V3
};
//0.离屏烘托极点数据:设置以1位单位距离
GLfloat fbo_vertices[] = {
//极点 //纹路坐标
-1.0f, -1.0f, 0.0f,0.0f, 0.0f,//V0
1.0f,-1.0f, 0.0f,1.0f,0.0f,//V1
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,//V2
-1.0f, 1.0f, 0.0f,0.0f, 1.0f//V3
};
//确认三角形索引
GLint indices[] = {
0,1,2,
0,2,3
};
这里小余对离屏烘托运用不同的极点数据,配合前面说的视口改变操作,得到一个新的和原纹路贴图尺度相同的纹路目标。
- 着色器程序:
//1.创立着色器程序,此处将着色器程序创立封装到一个工具类中
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 vPosition; \n"
"layout(location = 1) in vec2 texCords; \n"
"out vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
" v_texCoord = texCords; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 v_texCoord; \n"
"out vec4 fragColor; \n"
"uniform sampler2D s_TextureMap; \n"
"void main() \n"
"{ \n"
" fragColor = texture(s_TextureMap,v_texCoord); \n"
"} \n";
// 用于离屏烘托的片段着色器脚本,取每个像素的灰度值
char fbo_fShaderStr[] =
"#version 300 es\n"
"precision mediump float;\n"
"in vec2 v_texCoord;\n"
"layout(location = 0) out vec4 outColor;\n"
"uniform sampler2D s_TextureMap;\n"
"void main()\n"
"{\n"
" vec4 tempColor = texture(s_TextureMap, v_texCoord);\n"
" // RGB转YUV:\n"
" // Y = 0.299 * R + 0.587 * G + 0.114*B\n"
" // U = - 0.1687 * R - 0.3313 * G + 0.5* B + 128 注: +128 的意义是让UV的规模处于整数区间(0-255)\n"
" // V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128 注: +128 的意义是让UV的规模处于整数区间(0-255)\n"
" // 下面的luminance 只取了YUV的Y,所以是黑白的照片"
" float luminance = tempColor.r * 0.299 + tempColor.g * 0.587 + tempColor.b * 0.114;\n"
" outColor = vec4(vec3(luminance), tempColor.a);\n"
"}"; // 输出灰度图
这里要显示灰度图,涉及到RGB和YUV的转化:
RGB转YUV:
Y = 0.299 * R + 0.587 * G + 0.114*B"
U = - 0.1687 * R - 0.3313 * G + 0.5* B + 128 注: +128 的意义是让UV的规模处于整数区间(0-255)
V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128 注: +128 的意义是让UV的规模处于整数区间(0-255)
效果图:
能够看到正确显示出了灰度值,阐明咱们离屏烘托操作是成功了的。
总结
离屏烘托这块可能我们第一次学的话比较难看懂,只要了解:离屏烘托能够在GPU后台对纹路做一些预处理操作,记住是预处理,且这中间能够通过屡次离屏烘托,然后最终发送到进行屏幕制作的默许帧缓冲中。
离屏烘托能够做的东西很多:只要是对图片进行处理的,比如图片缩放,裁剪,旋转,模糊,滤镜等等:
以后工作中做的大部分是对图片的处理操作,也便是FBO。所以要入门图画处理,FBO也是一个必会技能。