opengl使用帧缓冲区及模板缓存记录
程序员文章站
2023-12-24 21:27:39
...
opengl使用帧缓冲区及模板缓存记录
这篇文章用于记录学习帧缓存区过程中的一些知识点,由于大部分文章所涉及的知识点都是相似的,所以只能自己在摸索的过程中记录一二,目前只是初学,可能还有很多地方理解出现偏差,请各位大佬批评指正。
-
创建帧缓存区
glGenBuffers(1,&fbo);
创建帧缓存区的步骤比较简单,只需在创建好后绑定到合适的绑定点上。
接下来需要创建深度、模板和颜色缓冲区,用于添加到帧缓存区
glGenRenderbuffers(1,&depthbuffer);//创建深度、模板缓存区(即选择GL_DEPTH24_STENCIL8参数 glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer); glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH24_STENCIL8,800,600); glGenBuffers(2,colorbuffer);//创建两个颜色缓存区,用于绘制两种图像 glBindRenderbuffer(GL_RENDERBUFFER,colorbuffer[0]); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8,800,600); glBindRenderbuffer(GL_RENDERBUFFER,colorbuffer[1]); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8,800,600);
在创建完成之后需要将这些缓存区添加到帧缓存区当中。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);//绑定fbo为帧缓冲区 glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_RENDERBUFFER,depthbuffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,colorbuffer[0]);//绑定在颜色缓冲区0 glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_RENDERBUFFER,colorbuffer[1]);//绑定在颜色缓冲区1
这样就完成了帧缓存区的创建
-
创建shader
顶点着色器和普通的没有区别,只能片段着色器会多一个输出,因为有两个颜色缓冲区。片段着色器代码如下:
#version 130 in vec2 texcoord; out vec4 vFragColor; out vec4 oStraightColor; uniform sampler2D grid;//纹理 uniform int flag;//用于模板缓存的标志位 void main(void) { if(flag==1) { vFragColor = vec4(vec3(texture(grid, texcoord).r), 1.0); oStraightColor = vec4(1.0); } else { vFragColor = vec4(0.04, 0.28, 0.26, 1.0); oStraightColor = vec4(0.04, 0.28, 0.26, 1.0); } }
在创建完shader之后还需要将片段着色器的输出
vFragColor
和oStraightColor
与颜色缓冲区进行绑定,即glBindFragDataLocation(diffuseLightShader, 0, "vFragColor");//绑定在0号颜色缓冲区 glBindFragDataLocation(diffuseLightShader, 1, "oStraightColor");//绑定在1号颜色缓冲区
到这里就完成了所有初始化的操作,接下来可以开始绘制。
-
渲染主循环
首先需要设置使用帧缓存区,否则会按照默认直接绘制到屏幕上。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glDrawBuffers(2,fboBuffs);//fboBuffs是GL_COLOR_ATTACHMENT0和GL_COLOR_ATTACHMENT1的数组头指针。
然后按照正常的绘制操作完成绘制,即
glUniformMatrix4fv(locMVP,1,GL_FALSE,transformPipeline.GetModelViewProjectionMatrix()); glUniform1i(glGetUniformLocation(diffuseLightShader,"grid"),0); glUniform1i(glGetUniformLocation(diffuseLightShader,"flag"),1);//flag=1表示绘制原始纹理,而不是纯色 glBindVertexArray(vao); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
到这一步为止屏幕上是没有输出的,因为都绘制到缓冲区fbo上了。
接下来就是把缓冲区的内容输出到屏幕上,
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);//设置输出缓冲区为0,即屏幕 glDrawBuffers(1, windowBuff); glViewport(0, 0, 800, 600); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);//设置从fbo读取数据用于输出 //上述部分实现了设置从fbo读取图像数据输出到屏幕,接下来将各个颜色缓冲区输出。 glReadBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(0, 0, 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST ); glReadBuffer(GL_COLOR_ATTACHMENT1); glBlitFramebuffer(0, 0, 800, 600, (int)(800 *(0.8)), (int)(600*(0.8)), 800, 600, GL_COLOR_BUFFER_BIT, GL_LINEAR );
现在就能在屏幕上看见绘制的内容了。
-
加入模板测试绘制边框
我们现在只是使用了之前创建的两个颜色缓冲区,接下来使用模板缓存绘制边框。关于模板测试的用法即原理可以参考StencilTest 。
在调用
glDrawArrays
之前需要启用模板测试,并清空缓存区。glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
然后在第一次绘制的之前添加以下代码,将需要绘制的部分模板值设为1
glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilMask(0xFF);
在第一次绘制完成后,将所绘制的图像放大
modelViewMatrix.Scale(1.05f,1.05f,1.05f)
然后再次设置模板测试,实现只绘制边界
glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilMask(0x00); glDisable(GL_DEPTH_TEST);
然后调用绘制程序,此时将
flag
设为0,只输出纯色,最终实现效果如下: -
后记
- 在绘制边框的时候需要使用与第一次绘制相同的着色器,通过flag调节着色器的输出。本来采用单独的一个着色器输出纯色,但是会导致无法输出到两个颜色缓冲区,暂时还没有学习到相关方法。
- 在使用帧缓存区时,需要输出到两个颜色缓存区,但是将gl的版本设置为3.0以上会出现输出到两个缓存区的结果一样(或者只使用了一个缓冲区),无法实现缩略图的效果。官方文档在3.0版本对FBO进行了更新,但是以现有水平难以理解文档内容。gl版本设置为3.0以下时可以实现上述效果。
-
完整代码
// DiffuseLight.cpp // OpenGL SuperBible // Demonstrates simple diffuse lighting // Program by Richard S. Wright Jr. #include <GLTools.h> // OpenGL toolkit #include <GLMatrixStack.h> #include <GLFrame.h> #include <GLFrustum.h> #include <GLGeometryTransform.h> #include <StopWatch.h> #include <math.h> #ifdef __APPLE__ #include <glut/glut.h> #else #define FREEGLUT_STATIC #include <GL/glut.h> #include <GL/freeglut_ext.h> #endif GLFrame viewFrame; GLFrustum viewFrustum; GLTriangleBatch sphereBatch; GLMatrixStack modelViewMatrix; GLMatrixStack projectionMatrix; GLGeometryTransform transformPipeline; GLShaderManager shaderManager; GLuint diffuseLightShader; // The diffuse light shader GLint locMVP; // The location of the ModelViewProjection matrix uniform GLuint singlecolorShader; static GLuint fbo,texture; static GLuint depthbuffer, colorbuffer[2],stencilbuffer; static const GLenum windowBuff[] = { GL_BACK_LEFT }; static const GLenum fboBuffs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; unsigned int vertexBuffer, vao ; float lastX = 400; float lastY = 300; float yaw = 0; float pitch = 0; float front[3] = {0,0,-1}; bool first_mouse = true; // This function does any needed initialization on the rendering // context. void SetupRC(void) { // Background glClearColor(0.2f, 0.3f, 0.2f, 1.0f ); glEnable(GL_DEPTH_TEST); //glEnable(GL_CULL_FACE); viewFrame.MoveForward(4.0f); glGenBuffers(1,&fbo); glGenRenderbuffers(1,&depthbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer); glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH24_STENCIL8,800,600); glGenBuffers(2,colorbuffer); glBindRenderbuffer(GL_RENDERBUFFER,colorbuffer[0]); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8,800,600); glBindRenderbuffer(GL_RENDERBUFFER,colorbuffer[1]); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8,800,600); glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_RENDERBUFFER,depthbuffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,colorbuffer[0]); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_RENDERBUFFER,colorbuffer[1]); diffuseLightShader = shaderManager.LoadShaderPairWithAttributes("Perspective.vs", "Perspective.fs", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord"); glBindFragDataLocation(diffuseLightShader, 0, "vFragColor"); glBindFragDataLocation(diffuseLightShader, 1, "oStraightColor"); locMVP = glGetUniformLocation(diffuseLightShader, "mvpMatrix"); singlecolorShader = shaderManager.LoadShaderPairWithAttributes("Perspective.vs", "Perspective2.fs", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord"); // Geometry for a simple quad static const GLfloat quad[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, }; static const GLfloat texcoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; // A checkerboard texture static const GLubyte texture_data[] = { 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0 }; // Greate a vertex array object and a vertex buffer for the quad // including position and texture coordinates glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(quad) + sizeof(texcoords), NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quad), quad); glBufferSubData(GL_ARRAY_BUFFER, sizeof(quad), sizeof(texcoords), texcoords); glVertexAttribPointer(GLT_ATTRIBUTE_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, NULL); glVertexAttribPointer(GLT_ATTRIBUTE_TEXTURE0, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid *)sizeof(quad)); glEnableVertexAttribArray(GLT_ATTRIBUTE_VERTEX); glEnableVertexAttribArray(GLT_ATTRIBUTE_TEXTURE0); // Create the checkerboard texture glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 16, 16, 0, GL_RED, GL_UNSIGNED_BYTE, texture_data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } // Cleanup void ShutdownRC(void) { } // Called to draw scene void RenderScene(void) { static CStopWatch rotTimer; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glDrawBuffers(2,fboBuffs); glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClearColor(0.5f, 0.5f, 0.5f, 1.0f ); M3DMatrix44f mCamera; viewFrame.GetCameraMatrix(mCamera); modelViewMatrix.PushMatrix(viewFrame); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f); glUseProgram(diffuseLightShader); glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilMask(0xFF); glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniform1i(glGetUniformLocation(diffuseLightShader,"grid"),0); glUniform1i(glGetUniformLocation(diffuseLightShader,"flag"),1); glBindVertexArray(vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized glDrawArrays(GL_TRIANGLE_FAN, 0, 4); modelViewMatrix.Scale(1.05f,1.05f,1.05f); glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilMask(0x00); glDisable(GL_DEPTH_TEST); glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniform1i(glGetUniformLocation(diffuseLightShader,"flag"),0); glBindVertexArray(vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glStencilMask(0xFF); glStencilFunc(GL_ALWAYS, 0, 0xFF); glEnable(GL_DEPTH_TEST); modelViewMatrix.PopMatrix(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDrawBuffers(1, windowBuff); glViewport(0, 0, 800, 600); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glReadBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(0, 0, 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST ); glReadBuffer(GL_COLOR_ATTACHMENT1); glBlitFramebuffer(0, 0, 800, 600, (int)(800 *(0.8)), (int)(600*(0.8)), 800, 600, GL_COLOR_BUFFER_BIT, GL_LINEAR ); glutSwapBuffers(); glutPostRedisplay(); } void ChangeSize(int w, int h) { // Prevent a divide by zero if(h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); } void Keyboard(unsigned char key, int x, int y) { float delta = -0.1f; switch (key) { // Space toggles perspective correction case 'w': viewFrame.MoveForward(-delta); break; case 's': viewFrame.MoveForward(delta); break; case 'a': viewFrame.MoveRight(-delta); break; case 'd': viewFrame.MoveRight(delta); break; case 27: glutLeaveMainLoop(); break; default: break; }; } void MouseFunc(int x,int y) { if (first_mouse) { first_mouse = false; lastX = x; lastY = y; } float xoffset = x - lastX; float yoffset = y - lastY; lastX = x; lastY = y; float sensitivity = 0.3f; sensitivity = m3dDegToRad(sensitivity); xoffset *= sensitivity; yoffset *= sensitivity; viewFrame.RotateWorld(-xoffset,0.0f,1.0f,0.0f); //viewFrame.RotateWorld(-yoffset,1.0f,0.0f,0.0f); } void EntryFunc(int state) { if(state == GLUT_LEFT) { first_mouse = true; } } /// // Main entry point for GLUT based programs int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); /* 1, 3 */ glutInitContextVersion(1, 3); glutInitContextProfile (GLUT_CORE_PROFILE); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("Simple Diffuse Lighting"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); glutKeyboardFunc(Keyboard); glutPassiveMotionFunc(MouseFunc); glutEntryFunc(EntryFunc); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); ShutdownRC(); return 0; }
推荐阅读