欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

opengl使用帧缓冲区及模板缓存记录

程序员文章站 2023-12-24 21:27:39
...

opengl使用帧缓冲区及模板缓存记录

这篇文章用于记录学习帧缓存区过程中的一些知识点,由于大部分文章所涉及的知识点都是相似的,所以只能自己在摸索的过程中记录一二,目前只是初学,可能还有很多地方理解出现偏差,请各位大佬批评指正。

  1. 创建帧缓存区

    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
    

    这样就完成了帧缓存区的创建

  2. 创建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之后还需要将片段着色器的输出vFragColoroStraightColor与颜色缓冲区进行绑定,即

    glBindFragDataLocation(diffuseLightShader, 0, "vFragColor");//绑定在0号颜色缓冲区
    glBindFragDataLocation(diffuseLightShader, 1, "oStraightColor");//绑定在1号颜色缓冲区
    

    到这里就完成了所有初始化的操作,接下来可以开始绘制。

  3. 渲染主循环

    首先需要设置使用帧缓存区,否则会按照默认直接绘制到屏幕上。

    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 );	
    

    现在就能在屏幕上看见绘制的内容了。

  4. 加入模板测试绘制边框

    我们现在只是使用了之前创建的两个颜色缓冲区,接下来使用模板缓存绘制边框。关于模板测试的用法即原理可以参考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,只输出纯色,最终实现效果如下:
    opengl使用帧缓冲区及模板缓存记录

  5. 后记

    • 在绘制边框的时候需要使用与第一次绘制相同的着色器,通过flag调节着色器的输出。本来采用单独的一个着色器输出纯色,但是会导致无法输出到两个颜色缓冲区,暂时还没有学习到相关方法。
    • 在使用帧缓存区时,需要输出到两个颜色缓存区,但是将gl的版本设置为3.0以上会出现输出到两个缓存区的结果一样(或者只使用了一个缓冲区),无法实现缩略图的效果。官方文档在3.0版本对FBO进行了更新,但是以现有水平难以理解文档内容。gl版本设置为3.0以下时可以实现上述效果。
  6. 完整代码

    // 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;
    }
    
相关标签: OpenGL opengl c++

上一篇:

下一篇: