精华内容
下载资源
问答
  • 咱们能在屏幕上看到东西,这是默认的帧缓冲的信息输出,这个帧缓冲里面包括,颜色缓冲,深度缓冲,模板缓冲等。 而一个默认的帧缓冲是不够用的,不能灵活的处理一些想要的效果。 所以咱们要创建新的帧缓冲,创建一个...

    用最通俗的话来说:

    咱们能在屏幕上看到东西,这是默认的帧缓冲的信息输出,这个帧缓冲里面包括,颜色缓冲,深度缓冲,模板缓冲等。

    而一个默认的帧缓冲是不够用的,不能灵活的处理一些想要的效果。

    所以咱们要创建新的帧缓冲,创建一个普通的帧缓冲,需要至少一个颜色附件、一个缓冲,这个颜色附件也就是一张纹理,在绑定这个帧缓冲之后,渲染的东西会全部存储到这张纹理中,可以把这个纹理理解成一个普通的颜色/深度或模板缓冲一样。

    而深度和模板的信息需要一个叫做渲染缓冲对象的东西来存储,这就是前面面说到的一个缓冲,这个是在颜色附件之后引入的,存储的是opengl数据原生格式,速度很快。但是渲染缓冲是只写的,不能读取(使用纹理访问),但我们还是可以用glReadPixels来读取(一个缓冲读取到另一个缓冲)。通常进行深度模板测试的时候用这个附件,因为速度快,也不需要读取。


    屏幕后效处理

    咱们在得到这张纹理后,进行一些比如模糊,滤镜等一些处理,最后在转换到默认帧缓冲,拿一张和屏幕大小的面片渲染咱们处理后的这张纹理,这些处理后就最终得到了咱们所看到的处理后的画面。

    还有更复杂的一些后处理,还需要一些深度法线等信息,这时候一个颜色附件就不够用了,法线信息需要存储到新的颜色附件中,这个时候需要输出到多个纹理(附件)中,这也叫做MRT,不要忘记告诉GPU渲染到哪些附件中。

    延迟渲染&前向渲染

    延迟渲染也是利用帧缓冲去完成的,不过都叫G-Buffer。

    延迟渲染主要有两个阶段:

    第一阶段,我们先渲染场景一次,获取场景内物体的几何信息,位置,法线,颜色等信息,并存储再帧缓冲的纹理附件中,如下:



    第二阶段,进行光照处理,恢复到默认帧缓冲,渲染一个屏幕大小的面片,并使用第一阶段的信息对每个面片进行光照处理。

    整体如下:


    利用帧缓冲进行延迟渲染细节步骤如下:

    1.创建帧缓冲,然后绑定三个颜色附件,这三个颜色附件分别用来存储场景中物体的位置向量,法线向量,纹理颜色。外加一个深度渲染缓冲,进行深度写入(留作后用)

    2.利用最简单的shader渲染场景,并把上述所需要的信息写入四个附件中。

    3.恢复到默认帧缓冲,创建屏幕大小面片

    4.利用上述三个颜色附件所存储的信息,再加上场景中所有光源的颜色位置信息,及摄像机信息,在一个shader中进行复杂的光照处理

    5.输出到屏幕

    这就是基本的延迟渲染(未经管线优化)。


    相比较前向渲染,延迟渲染有很多优点,它不需要再重复的对很多片段再进行光照处理,因为这些片段都是进行深度测试之后的最顶层的片段。但是延迟渲染也有缺点,比如,不能混合,因为得到的都是最顶层的一个片段,不存在其他片段,所以无法进行混合操作。另一个缺点是迫使整个延迟渲染的场景使用同一种光照算法。

    所以为了解决上述问题,通常把渲染器分割成两个部分,一个部分是延迟渲染,一部分是专门为了混合或者其他不适合延迟渲染管线效果而设计的前向渲染部分。

    如下:

    再延迟渲染的第4步之后,再添加上一个作,用glReadPixels把G-Buffer中的第四个附件(渲染缓冲对象中的深度信息)copy到默认帧缓冲中,这下就有深度信息了,然后再渲染前向渲染的所有东西,最后输出到屏幕。


    下面是我结合延迟渲染和前向渲染的一个demo:

    小方块(灯源)和天空盒都是使用前向渲染,人物是使用延迟渲染














    展开全文
  • (3)Learn OpenGL中关于帧缓冲那部分,只讲了如何渲染颜色帧缓冲,没有讲怎么去渲染深度缓冲,正好编程指南 > >有讲,新代码如下: void Test::init1() { glGenFramebuffers( 1 , &_frameBuffer); ...

    自从面试腾讯实习生结束后,一直在学习OpenGL,以前断断续续学过,但是主要的参考材料是 < < OpenGL 编程指南(第八版) > >(俗称红宝书)以及网络上零零碎碎的博客,去年在创业公司实习时,也学过,但是学习的效果一直都比较差,学到最后,只能知道渲染管线,顶点数组,光照等等,着色器只会最简单的,可以说,毫无竞争力.总结下来,主要是参考的资料不对,红宝书固然好,但不适合初学者,初学者还是需要一步步,动手实践好每个实例来学习,不然很容易变成空中楼阁.上周看到了Learn OpenGL这个教材,如获至宝,至少从现在的学习效果来看,还是蛮不错的.
    废话说了好多,昨天学到帧缓冲(Frame Buffer)这一块,按照教材来,但是一直没有效果,现在回过头来看,造成的原因是:自己的封装造成写的程序和教程存在一些出入.本以为流程差不多效果就可以显示的,但是却没有,有些细节致使结果错误.LearnOpenGL帧缓冲
    下面总结这些错误及对应的解决方法:
    (1)先看如下代码:

    void Test::init()
    {
        glGenFramebuffers(1, &_frameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
        _textureColorBuffer = generateAttachmentTexture(false, false);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
            _textureColorBuffer, 0);
    
        glGenRenderbuffers(1, &_rbo);
        glBindRenderbuffer(GL_RENDERBUFFER, _rbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCREEN_WIDTH, SCREEN_HEIGHT);
        //glBindRenderbuffer(GL_RENDERBUFFER, 0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glCheckError();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo);
        glCheckError();
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cout << "ERROR::FRAMEBUFFER-FRAMEBUFFER is not complete!" << std::endl;
        }
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }

    报错:
    这里写图片描述
    这里INVALID_OPERATION即OpenGL中用glErrors()获取的错误状态GL_INVALID_OPERATION,我用自定义glCheckError函数封装了该函数,出现该错误的原因:在输入代码时,我将glBindRenderbuffer(GL_RENDERBUFFER, 0)写成glBindFramebuffer(GL_FRAMEBUFFER, 0),这导致在接下来的glFramebufferRenderbuffer前,将帧缓冲对象(FBO)解绑,在接下去将渲染缓冲对象(RBO)绑定到帧缓冲时,没有可用的帧缓冲可用,所以出现GL_INVALID_OPERATION.粗心导致的错误!!!但这个错误让我对代码中出现的API有了更清晰的了解(小白伤不起);
    (2)如下代码:

    void Test::draw()
    {
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    
        //glEnable(GL_DEPTH_TEST);
        //glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
        //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        Shape* shape = new Sphere();
        shape->setScale(glm::vec3(3.0f));
        shape->setRotate(45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
        shape->setTexture2D(ResourceManager::getInstance().getTexture2D("earth"));
        shape->setShaderProgram(ResourceManager::getInstance().getShaderProgram("basic"));
        shape->draw();
    
        Shape* cube = new Cube();
        cube->setTranslate(glm::vec3(0, 0, 15));
        cube->setRotate(45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
        cube->setShaderProgram(ResourceManager::getInstance().getShaderProgram("basic"));
        cube->draw();
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glDisable(GL_DEPTH_TEST);
    
        Shape* plane = new Cube();
        Texture2D texture2D(_textureColorBuffer);
        plane->setTexture2D(texture2D);
        plane->setShaderProgram(ResourceManager::getInstance().getShaderProgram("framebuffer"));
        plane->draw();
    
        glEnable(GL_DEPTH_TEST);
    
        delete shape;
        delete cube;
        delete plane;
    }

    几何图形和渲染过程我做了封装,看函数名字可以理解渲染过程,渲染结果:
    这里写图片描述
    黑屏…glClearColor和glClear,在draw函数调用前已经使用,如下:

    bool Application::initRender()
    {
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    
        int width, height;
        glfwGetFramebufferSize(_window, &width, &height);
        glViewport(0, 0, width, height);
    
        ResourceManager::getInstance().initResource();
    
        glCheckError();
    
        _camera = Camera((float)width / (float)height);
        _camera.bindUniformBuffer(CameraUniformBindPoint);
    
        return true;
    }
    
    void Application::exec()
    {
        glEnable(GL_DEPTH_TEST);    //开启深度测试
        glEnable(GL_STENCIL_TEST);  //开启模板测试
    
        while (!glfwWindowShouldClose(_window))
        {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
            static Timer timer;
            glfwPollEvents();
            processInput(timer.calcInvertal());
            draw();
            glfwSwapBuffers(_window);
            sleep();
        }
        exit();
    }

    所以在Test::draw()函数中开始并没有注释的部分,当时以为这没有问题的(现在证明太naive~),于是在Test::init()函数中找问题,在做了一些尝试后,发现注释了glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo);后,可以渲染结果了,如下图:
    这里写图片描述,可以发现,图像中的结果明显是因为没有开启深度测试(当时并没有想到)导致的,然后移动摄像机,结果如下图:
    这里写图片描述,这种结果,明显是渲染开始没有清空GL_COLOR_BUFFER_BIT导致的,可是,当时我认为我已经在Application::initRender和Application::exec()中调用了,肯定不是上述的开启深度测试和清空颜色缓冲(事后被打脸,啪啪啪),于是陷入僵局了.
    一直无果后,1点多钟师兄说要回寝室,就暂时放下了.
    今天(话说今天是我浙120周年校庆,生日快乐!)早上来到实验室后,还是要面对昨晚未解决的问题,我一直肯定不是开启深度测试和清空颜色缓冲的问题,最后身边有一本< < OpenGL ES 3.0 编程指南 > >,也有讲述帧缓冲的部分,代码大致相同,但在对应Test::init中的代码,该书的代码最后没有glBindFramebuffer(GL_FRAMEBUFFER, 0)这行代码,我尝试注释了这行代码,结果…竟然可以了,如下图:
    这里写图片描述
    可是移动摄像机,还是有问题:
    这里写图片描述
    此时,想到注释glBindFramebuffer(GL_FRAMEBUFFER, 0)后,Application::exec函数里的操作可以应用到帧缓冲中,此时才明白,窗口缓冲和自定义帧缓冲是两个不同的缓冲,在glBindFramebuffer(GL_FRAMEBUFFER, 0)后,Application::exec()里的初始化操作是针对窗口缓冲的,而不是自定义的缓冲,绑定后自定义帧缓冲后,也必须有开启深度测试,清空颜色缓冲等操作,即上述Test::draw()中被注释的代码.修改后终于正常了.现在想到,当时注释glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo)这行代码前无用的原因是,没有针对自定义缓冲执行清空颜色缓冲和深度缓冲的操作.
    (3)Learn OpenGL中关于帧缓冲那部分,只讲了如何渲染颜色帧缓冲,没有讲怎么去渲染深度缓冲,正好< < OpenGL ES 3.0 编程指南 > >有讲,新代码如下:

    void Test::init1()
    {
        glGenFramebuffers(1, &_frameBuffer);
        glGenTextures(2, textures);
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCREEN_WIDTH, SCREEN_HEIGHT, 0,
            GL_RGB, GL_UNSIGNED_SHORT_5_6_5, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(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, 0);
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glCheckError();
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, SCREEN_WIDTH,
            SCREEN_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
        glCheckError();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glBindTexture(GL_TEXTURE_2D, 0);
    
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[1], 0);
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cout << "ERROR::FRAMEBUFFER-FRAMEBUFFER is not complete!" << std::endl;
        }
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    
    void Test::draw()
    {
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    
        glEnable(GL_DEPTH_TEST);
        glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        Shape* shape = new Sphere();
        shape->setScale(glm::vec3(3.0f));
        shape->setRotate(45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
        shape->setTexture2D(ResourceManager::getInstance().getTexture2D("earth"));
        shape->setShaderProgram(ResourceManager::getInstance().getShaderProgram("basic"));
        shape->draw();
    
        Shape* cube = new Cube();
        cube->setTranslate(glm::vec3(0, 0, 15));
        cube->setRotate(45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
        cube->setShaderProgram(ResourceManager::getInstance().getShaderProgram("basic"));
        cube->draw();
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glDisable(GL_DEPTH_TEST);
    
        Shape* plane = new Cube();
        //Texture2D texture2D(textures[1]);
        Texture2D texture2D(_textureColorBuffer);
        plane->setTexture2D(texture2D);
        plane->setShaderProgram(ResourceManager::getInstance().getShaderProgram("framebuffer"));
        plane->draw();
    
        glEnable(GL_DEPTH_TEST);
    
        delete shape;
        delete cube;
        delete plane;
    }

    变化的是Test::init(),Test::draw()修改的部分主要是注释的那行,Test::init()用两个纹理作为存放缓冲的内容,(1)(2)中的Test::init()是一个纹理和一个渲染缓冲,原着色器代码如下:

    #version 330 core
    
    in vec2 fTexcoord;
    
    uniform sampler2D screenTexture;
    
    out vec4 color;
    
    void main()
    {
        color = vec4(texture2D(screenTexture, fTexcoord));
    }

    渲染深度缓冲的结果:
    这里写图片描述
    可以看到,全屏红色,深浅虽然正确,但是,这里并不是期望中的黑白色.一番折腾后,在以前写代码时,看别人的生成阴影的代码里,是取纹理采样结果的r分量,于是把片段着色器代码修改成如下:

    #version 330 core
    
    in vec2 fTexcoord;
    
    uniform sampler2D screenTexture;
    
    out vec4 color;
    
    void main()
    {
    //  color = vec4(texture2D(screenTexture, fTexcoord));
        color = vec4(texture2D(screenTexture, fTexcoord).r);
    }

    结果:
    这里写图片描述
    终于正确了!!!
    这样做的原因(猜测),深度纹理的internalFormat和type分别是GL_DEPTH_COMPONENT32F,GL_FLOAT,而不是GL_RGB和GL_UNSIGNED_SHORT_5_6_5这种颜色纹理的类型,所以在采样时,texture2D的结果是(R,G,B,A),深度数据全部存放在R变量里的,G,B,A都是0.0f,所以结果会泛红.这个问题的解决完全是因为之前看到的那份代码,不然又要走很多弯路,不过之前一直不懂为啥取r分量,现在终于明白了!

    三个问题,解决的同时让自己对OpenGL的理解更进一步,对API的使用也有了进步,但还是要吐槽一句,OpenGL这种设计,确实有点反人类(可能是我太弱鸡了).

    最后,解决问题过程的一些感想:
    (1)细心,细心,细心,第一个问题就是粗心造成的!
    (2)分析问题的能力挺弱鸡的,第二个问题,如果能够认真分析,然后针对性去找代码问题,可能就不会耗费那么久的时间了,侧面来说,这也是自己对OpenGL熟练造成的;
    (3)我记得去年在**实习时,做的入职作业,弄得很差,阴影效果,我花了两周才弄出来.这样的原因,一是查找资料的能力太弱,当时找到的都是固定管线的资料,一起实习的伙伴做的好的就是根据LearnOpenGL这本教程来的;二是,眼高手低,当时红宝书上有阴影实现的代码轮廓,自己在没有搞明白纹理,帧缓冲的情况就去写,结果是浪费了大量时间,如果开始就扎扎实实,从简单地学起,或许可以缩短大量时间,还能完全理解背后的原理;
    (4)踏踏实实,不仅要看书,更要去实践,动手写代码,这样不仅能增强记忆,在写代码的过程中还能发现很多细节问题,这些问题往往可以导致整个程序运行失败,此外,通过代码实践->发现问题->解决问题的良性循环,能够增强对整个知识的理解!

    展开全文
  • 深度缓冲是一个每次片段着色器渲染的时候都要比较当前片段的深度和之前片段的深度信息,从而决定哪个片段该被渲染在当前换面。 模板缓冲是一个我们确定的一个缓冲,我们可以通过在每次片段渲染的时候,把对应片段的...

    深度缓冲是一个每次片段着色器渲染的时候都要比较当前片段的深度和之前片段的深度信息,从而决定哪个片段该被渲染在当前换面。

    模板缓冲是一个我们确定的一个缓冲,我们可以通过在每次片段渲染的时候,把对应片段的模板缓冲改掉,这样渲染完成一个物体之后,对应物体的片段部分模板缓冲已经被改变了,然后我们可以重新渲染一次该物体,缩放一下,这样通过一定的模板测试,就可以剔除之前对应的片段,给物体的边缘做新的渲染。

    之前讲的深度缓冲和模板缓冲都是帧缓冲的一种。帧缓冲可以替换目前对应的帧缓冲。帧缓冲可以绑定纹理附件和深度——模板附件。

    纹理缓冲主要是用来像颜色缓冲一样的,把当前场景记录下来?

    然后深度——模板缓冲(附加在渲染缓冲对象里)一般是只读信息,不去采样。

    当把一个纹理附加到帧缓冲的时候,所有的渲染指令将会写入到这个纹理中,就想它是一个普通的颜色/深度或模板缓冲一样。使用纹理的优点是,所有渲染操作的结果将会被储存在一个纹理图像中,我们之后可以在着色器中很方便地使用它。

    我们然后切换回默认的帧缓冲,把之前的帧缓冲纹理  当作一个普通的纹理图片来处理,就可以轻易改变对应场景的风格(就相当于把场景存储在一个照片里,我们只需要加载照片,然后改变照片像素的风格!)

    展开全文
  • OpenGL之帧缓冲

    千次阅读 2019-04-17 13:37:02
    本文主要包括帧缓冲的基本概念、函数使用,以及引出离屏渲染和后处理的介绍。 文章目录概要帧缓冲附件函数纹理附件渲染缓冲对象附件附件类型的选择后处理离屏渲染离屏渲染与双缓冲来源 概要 所谓帧缓冲是做什么用的...

    本文主要包括帧缓冲的基本概念、函数使用,以及引出离屏渲染和后处理的介绍。

    概要

    所谓帧缓冲是做什么用的,就是用来存储渲染数据的地方,可以理解为显存。几何数据(顶点坐标、纹理坐标等)和纹理经过一系列渲染管道最终计算出屏幕上的所有像素点,它们需要一个地方来存放,这个地方就是帧缓冲。帧缓冲中的数据会被显示器读取来刷新显示。

    OpenGL可以同时存在多个帧缓冲,创建窗口时OpenGL会自动创建一个默认帧缓冲,专门用于该窗口的渲染显示。其它的需要用户创建自定义帧缓冲。

    帧缓冲中包括有颜色缓冲区、深度缓冲区、模板缓冲区这三类缓冲区。

    我用下图介绍一下帧缓冲的用处,以深度缓冲为例。
    在由三维映射至二维空间时,Z坐标将会被转换为深度值写入深度缓冲中,测试和混合阶段执行后,像素点的颜色值会被写入帧缓冲的颜色附件中,最终帧缓冲会显示到显示器上。
    在这里插入图片描述

    帧缓冲

    附件

    要注意,帧缓冲并不包含缓冲数据的内存,它只是包含颜色缓冲区、深度缓冲区、模板缓冲区的附加点。这些对象才是真正的缓冲,它们被称之为附件。绑定到帧缓冲的这个过程叫做“附加”。

    例如下图中,帧缓冲中包含有多个附加点可将真正的缓冲区附加上去。

    在这里插入图片描述

    OpenGL定义,一个帧缓冲对象包括有

    1. 多个颜色缓冲附件,
    2. 一个深度缓冲附件,
    3. 一个模板缓冲附件。

    可以通过一个结构体来理解它(仅用于理解):

    struct FrameBuffer
    {
        int[] color;
        int depth;
        int stencil;
    };
    

    注意到颜色缓冲区可以是一个列表,这是由OpenGL定义的,允许用户同一时间将颜色缓冲区渲染到多个目标(multiple render targets,MRT)。

    MRT是什么意思呢?
    比方说,你可以附加两个颜色附件,然后在这两个附件上渲染出整个场景。可能一个是完整版场景,一个低采样版场景,分别作不同用途,可能其中带有遮罩或者用于其它后处理。

    此外OpenGL定义,一个完整的帧缓冲对象至少要有一个颜色附件,否则将无法被使用。

    函数


    创建帧缓冲和创建VBO时非常类似地。

    void glGenFramebuffers(GLsizei n, GLuint* ids)
    void glDeleteFramebuffers(GLsizei n, const GLuint* ids)
    

    glGenFramebuffers第一个参数是创建帧缓冲的数目,第二参数是存储帧缓冲区对象的ID。


    绑定帧缓冲到OpenGL目标上:

    void glBindFramebuffer(GLenum target, GLuint id)
    

    将这个帧缓冲绑定到一个目标上,将它指定为激活的帧缓冲,这个目标会影响OpenGL对帧缓冲r的使用,也可以看做目标=类型。

    • GL_FRAMEBUFFER:所有的读取和写入帧缓冲的操作将会影响当前绑定的帧缓冲
    • GL_READ_FRAMEBUFFER:所有的读取操作会从这个帧缓冲中读取
    • GL_Draw_FRAMEBUFFER):被用作渲染、清除等写入操作的目标

    附件有两种类型,分别是纹理附件和渲染缓冲对象(Render Buffer Object,RBO)附件。先说纹理附件。

    纹理附件

    用一个纹理对象来作为一个附件,这称之为纹理附件。所有渲染操作的结果将会被储存在这个纹理图像中。

    纹理可能有立方体纹理,1D、2D、3D纹理等类型,此外还可以使用Mipmap。

    在RBO出来之前,是只能使用纹理作为附件的,但RBO并不意味着纹理附件就“过时”了,纹理附件的优点在于纹理是一种标准格式,在片段着色器中我们可以快速读取到纹理上的像素。(读取比RBO快)


    纹理附件的创建和一般的纹理相同。

    glGenTextures
    glBindTexture
    glTexImage2D
    glTexParameteri
    

    附加纹理到帧缓冲:

    glFramebufferTexture2D(GLenum target, 
                        GLenum attachmentPoint, 
                        GLenum textureTarget, 
                        GLuint textureId, 
                        GLint level)
    
    • target:帧缓冲的目标(即我们前面说到的帧缓冲类型)
    • attachmentPoint:附件的类型。颜色、深度或是模板缓冲附件
      GL_COLOR_ATTACHMENT0, …, GL_COLOR_ATTACHMENT_n_), GL_DEPTH_ATTACHMENT, 和 GL_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL_ATTACHMENT
    • textureTarget:附件的纹理类型,大部分时候都是GL_TEXTURE_2D
    • textureId:要附加的纹理对象
    • level:多级渐远纹理的级别。一般保留为0

    渲染缓冲对象附件

    渲染缓冲对象(Render Buffer Object,RBO)是后来者,这是为了解决帧缓冲渲染到纹理时,纹理使用标准格式,所以速度可能较慢。

    而RBO使用OpenGL原生的格式存储像素值,因此它针对屏幕外渲染进行了优化。换句话,绘制到RBO比绘制到纹理要快得多,即写入数据/复制数据会更快。

    而它的缺点也在于像素用的是原生的、与实现相关的格式,因此从RBO读取比从纹理读取要困难得多。

    RBO与纹理附件最大的两个区别在于:

    1. RBO没有Mipmap,所以它只是一个图像
    2. 不能使用纹理访问或其它方式来采样(尽管可以使用 glReadPixels 函数来读取,只是不能在着色器中读取)

    在深度测试和模板测试中,我们只关心测试结果,通常我们不需要从深度或模板中采样,所以对于深度缓冲对象和模板缓冲对象我们一般采用RBO。


    RBO同一般缓冲区的用法差不多

    void glGenRenderbuffers(GLsizei n, GLuint* ids)
    
    void glDeleteRenderbuffers(GLsizei n, const Gluint* ids)
    
    void glBindRenderbuffer(GLenum target, GLuint id)
    

    当创建RBO时有一点特殊,它没有任何数据存储空间,必须要为它分配内存:

    void glRenderbufferStorage(GLenum  target,
                               GLenum  internalFormat,
                               GLsizei width,
                               GLsizei height)
    
    • target:RBO的类型,只有一种,必须是GL_RENDERBUFFER
    • internalFormat:内部格式,用于颜色的(GL_RGBA4/GL_RGB5_A1/GL_RGB565等)、用于深度的(GL_DEPTH_COMPONENT16)或用于模板格式(GL_STENCIL_INDEX8),还可以用于深度+模板的格式(GL_DEPTH24_STENCIL8)
    • width:RBO的宽
    • height:RBO的高

    附加RBO到帧缓冲上:

    void glFramebufferRenderbuffer(GLenum target,
                                   GLenum attachmentPoint,
                                   GLenum renderbufferTarget,
                                   GLuint renderbufferId)
    
    • target:RBO的类型,只有一种,必须是GL_RENDERBUFFER
    • attachmentPoint:附加点格式,GL_COLOR_ATTACHMENT0, …, GL_COLOR_ATTACHMENT_n_), GL_DEPTH_ATTACHMENT,和 GL_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL_ATTACHMENT
    • renderbufferTarget:当第四个参数不为0时,这个值必须是GL_RENDERBUFFER。否则随便。
    • renderbufferId:要附加的RBO对象

    附件类型的选择

    通常的规则是,如果不需要从一个缓冲中采样数据,那么使用RBO。例如深度缓冲和模板缓冲。
    如果你需要从缓冲中采样颜色或深度值等数据,那么你应该选择纹理附件。一般来说后处理都会需要这种。

    后处理

    在顶点着色器对每一个像素点,进行各种计算处理,这叫后处理。
    例如下图简单的处理,对纹理中每个像素点进行反相处理。
    在这里插入图片描述

    离屏渲染

    在默认帧缓冲中绘制对象,这叫当前屏幕渲染(On-screen Rendering)。

    把渲染计算结果放在非默认帧缓冲中,这叫离屏渲染(Off-screen Rendering)。
    一般要将自定义缓帧缓冲的数据渲染出来,还需要切回默认帧缓冲,取出自定义帧缓冲的颜色附件,再将其渲染。

    例如下图中,将自定义帧缓冲中的颜色附件拿出来,单独绘制在一个只有屏幕1/4大小的长方形上。
    在这里插入图片描述

    对于普通的当前屏幕渲染来说,这一整套绘制流水线就是一个Rendering Pass,而离屏渲染就存在多个Rendering Pass了。

    离屏渲染性能消耗是比较大的,主要在于两点:

    1. 更多的Rendering Pass,GPU工作量增大
    2. Rendering Pass之间的环境切换相当耗时,从自定义缓冲区转换到系统帧缓冲区。

    那为什么还要用它呢?
    大部分后处理都需要用到离屏渲染,这在游戏中是非常常见的功能。
    像是遮罩
    在这里插入图片描述
    阴影
    在这里插入图片描述
    bloom泛光
    在这里插入图片描述
    light shafts体积光,像上次分享会提到的第二种渲染体积光的方式:后处理中的径向模糊。
    在这里插入图片描述
    亦或是outline描边
    在这里插入图片描述
    补充一句:OpenGL中模板测试可以实现描边的效果,但是具有很多的限制,效果不理想。
    常见的做法是用两个Pass。
    第一个 Pass ,沿法线挤压,稍大一点,剔除正面,就可以看到它的背面
    第二个 Pass ,正常渲染,剔除背面,遮挡上一个 Pass 的中间部分

    使用纹理附件作为颜色附件,这在后处理中有一个好处,可以在这个像素点取一小块区域采样,来进行复杂的效果例如核效果、高斯模糊等。

    离屏渲染与双缓冲

    离屏渲染要求用户必须自定义创建一个帧缓冲并与默认帧缓冲配合使用。这和OpenGL中的双缓冲机制有什么关系呢?

    用一张图来表示双缓冲机制。如图,在每一帧中我们的渲染的结果会保存在后缓冲中,在每帧结束前需要交换前后缓冲来刷新显示。也就是说,系统默认帧缓冲就是后缓冲。

    离屏渲染需要创建一个新的帧缓冲,也并不影响双缓冲机制。

    在这里插入图片描述

    所以答案是双缓冲机制与离屏渲染并没有什么关系。

    来源

    1. https://learnopengl-cn.github.io/04 Advanced OpenGL/05 Framebuffers/
    2. http://www.songho.ca/opengl/gl_fbo.html
    3. https://stackoverflow.com/questions/9850803/glsl-renderbuffer-really-required
    4. https://stackoverflow.com/questions/2213030/whats-the-concept-of-and-differences-between-framebuffer-and-renderbuffer-in-op
    展开全文
  • OpenGL之帧缓冲详解

    2020-05-17 18:44:25
    简介 OpenGlES中的物体绘制并不是直接在屏幕上进行的,而是预先在帧缓冲区内进行绘制的,每绘制完一帧再将绘制的结果交换到屏幕上,所以...在启用深度测试的情况下,新片元想进入帧缓冲时需要将自己的深度值与帧缓冲.
  • 深度缓冲详解(DepthBuffer)

    万次阅读 2018-05-16 11:42:02
    深度缓冲区与帧缓冲区相对应,用于记录上面每个像素的深度值,通过深度缓冲区,我们可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确。 2. 何为深度 深度其实就是该象素点在3d世界中距离摄象机的距离...
  • webgl 中渲染到纹理需要用到帧缓冲区,但是同时想使用深度缓冲区和模板缓冲区需要的步骤如下,直接上代码 var width = 800; var height = 600; //创建帧缓冲区对象 var frameBuffer = gl.createFramebuffer();
  • 帧缓冲

    2020-02-10 17:48:28
    opengl 中默认会为我们创建一个帧缓冲,以后的颜色深度模板等信息会保存在里面。 一 什么是帧缓冲 以下摘抄自著名的wiki: Aframebuffer(frame buffer, or sometimesframestore) is a portion ofrandom-access ...
  • framebuffer_write 通过直接修改帧缓冲区中的像素信息将文本写入iOS屏幕的工具 用法: fb_write 结果示例: 博客文章-https:
  • 适用于Unity的Oldskool帧缓冲区反馈效果 如何使用 导入到您的项目中 将反馈脚本添加到相机对象 启动播放模式。 随意调整性能。 反馈脚本将优先级最低(最大深度值)的前一帧的内容传输到当前帧。 您可以调整在传输...
  • opengl帧缓冲

    2019-01-04 17:59:36
    OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓冲。 我们目前所做的所有操作都是在默认帧缓冲的渲染缓冲上进行的。默认的帧缓冲是在你创建窗口的时候生成和...
  • 到目前为止,我们已经使用了很多屏幕缓冲了:用于写入颜色值的颜色缓冲、用于写入深度信息的深度缓冲和允许我们根据一些...这些缓冲结合起来叫做帧缓冲(Framebuffer),它被储存在内存中。OpenGL允许我们定义我们...
  • 帧缓冲区对象 FrameBuffers Object(FBO) 一 概念 =============================================================== 迄今为止,我们使用了好几种屏幕缓冲区:颜色缓冲区,深度缓冲区,模板缓冲区。 这些缓冲区...
  • [转]OpenGL核心技术之帧缓冲

    万次阅读 2017-02-12 15:03:48
    如果你遗漏了比如深度缓冲,所有深度测试就不会工作,因为当前绑定的帧缓冲里没有深度缓冲。 所以,为把场景绘制到一个单独的纹理,我们必须以下面步骤来做: 使用新的绑定为激活帧缓冲帧缓冲,像往常那样...
  • 渲染到深度纹理帧缓冲对象FBO渲染管线的最后一个阶段是到帧缓冲区。前面学习的好多知识所做的渲染操作都是在默认的帧缓冲中进行的,这个默认的帧缓冲是我们创建一个Surface时自动创建和配置好的,这篇博客就创建我们...
  • 帧缓冲

    千次阅读 2014-07-04 11:03:32
    1.缓存包括颜色、scissor、alpha、stencil、depth这些缓存,所以缓存不是一片缓存,而是所有这些缓存的组合,缓存它对应了屏幕上的每一个pixel(不是真正的pixel,而是一个fragment所对应的位置)的各种这些...
  • OpenGL 帧缓冲

    千次阅读 2017-02-24 23:20:31
    概念:帧缓冲可以理解为着色器渲染之后将要显示在窗口上的所有颜色信息,深度信息和模版信息的数据集合,这些数据都保存在内存中,最后经由显示器显示在窗口中。窗口都有一个默认的帧缓冲,来存放最终要显示的所有...
  • OpenGl L17帧缓冲

    2021-11-05 18:58:17
    把这些缓冲结合起来叫做帧缓冲,OpenGl允许我们定义自己的帧缓冲。 我们目前的操作都是在默认帧缓冲的渲染缓冲上进行的(在创建窗口的时候生成和配置的),而有了我们的帧缓冲之后,就可以有更多方式来渲染了。 1.创建...
  • OpenGL学习之帧缓冲——FBO

    千次阅读 2018-03-12 19:21:41
    实际上,GLFW为我们做了完了这件事,渲染得到的信息存储到了系统默认帧缓冲(window-system-provided framebuffer)当中了。帧缓冲——framebuffer是OpenGL管线最后“一站”,它存储一系列的2D序列,最后在...
  • Vulkan帧缓冲

    2019-12-31 16:52:35
    我们在创建渲染流程对象时指定使用的附着需要绑定在帧缓冲对象上使用。帧缓冲对象引用了用于表示附着的 VkImageView 对象。对于我们的程序,我们只使用了一个颜色附着。但这并不意味着我们只需要使用一张图像,每个...
  • OpenGL中的缓冲只是一个管理特定内存块的对象,没有其它更多的功能了。 首先我们明确一个原理或者道理,不论是什么缓冲,...FBO 也就是帧缓冲 FrameBufferObject ,可以用来做很多事,核心上来说,它干了什么...
  • 深度缓冲区:与帧缓冲区对应,用于记录上面每个像素的深度值,通过深度缓冲区,我们可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确。(注意区分深度测试和背面剔除)模板缓冲区:与深度缓冲区类似,通过...
  • OpenGL中关于帧缓冲区的理解

    千次阅读 2017-10-18 15:56:40
    《OpenGL超级宝典》中对帧缓冲区的解释比较简单,文中说一个OpenGL窗口的表面被称作“帧缓冲区...帧缓冲通常包括:颜色缓冲,深度缓冲,模板缓冲和累积缓冲。这些缓冲区可能是在一块内存区域,也可能单独分开,看硬件。
  • 帧缓冲 默认和自定义的 一些区别

    千次阅读 2018-01-18 22:49:31
    转载自: ...OpenGL管道的最终呈现目标称为帧缓冲区(Framebuffer)。Framebuffer是OpenGL使用的2D数组或存储库的集合:彩色缓冲区,深度缓冲区,模板缓冲区和累积缓冲区。
  • OpenGL 帧缓冲Framebuffers

    2021-03-19 17:25:56
    OpenGL帧缓冲Framebuffers帧缓冲Framebuffers简介创建一个帧缓冲纹理附件渲染缓冲对象附件渲染到纹理后期处理...OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓
  • OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓冲。 我们目前所做的所有操作都是在默认帧缓冲的渲染缓冲上进行的。默认的帧缓冲是在你创建窗口的时候生成和...
  • LearnOpenGL16——帧缓冲

    2021-02-18 14:52:04
    转自 ...帧缓存的概念 到目前位置,已经介绍了多种缓存(数据) 用于写入颜色值的颜色缓冲 用于写入深度信息的深度缓冲 允许我们根据一些条件丢弃特定片段...这些缓冲结合起来叫做帧缓冲(Framebuffer) 帧缓存的作用 用于

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,033
精华内容 6,413
关键字:

帧缓冲深度