帧缓冲对象-实践过程问题及解决
来源:互联网 发布:unity3d 官方文档 编辑:程序博客网 时间:2024/06/06 02:41
自从面试腾讯实习生结束后,一直在学习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 corein vec2 fTexcoord;uniform sampler2D screenTexture;out vec4 color;void main(){ color = vec4(texture2D(screenTexture, fTexcoord));}
渲染深度缓冲的结果:
可以看到,全屏红色,深浅虽然正确,但是,这里并不是期望中的黑白色.一番折腾后,在以前写代码时,看别人的生成阴影的代码里,是取纹理采样结果的r分量,于是把片段着色器代码修改成如下:
#version 330 corein 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)踏踏实实,不仅要看书,更要去实践,动手写代码,这样不仅能增强记忆,在写代码的过程中还能发现很多细节问题,这些问题往往可以导致整个程序运行失败,此外,通过代码实践->发现问题->解决问题的良性循环,能够增强对整个知识的理解!
- 帧缓冲对象-实践过程问题及解决
- java.sql.SQLException: 关闭的连接问题及hibernate clob缓冲流存储的解决过程记录
- 解决real缓冲的问题
- 双缓冲解决抖动问题
- ASP.NET 缓冲: 技术及最佳实践
- 帧缓冲对象FBO
- IIS 5.1安装问题及解决过程
- 使用PowerMock过程中的问题及解决
- ANDROID编译过程的问题及解决
- Ubuntu学习过程中的问题及解决
- centos7配置LAMP过程问题及解决
- hadoop,hbase安装过程中遇到问题及解决过程!
- python 字典对象赋值之deepcopy遭遇的问题及解决过程(lxml惹的祸)
- 解决js跨域问题的思路及实践
- 双缓冲解决绘制图象闪烁问题
- MFC双缓冲解决闪烁问题
- GDI+双缓冲解决图像闪烁问题
- MFC双缓冲解决闪烁问题
- php里String中双引号""和单引号''的区别
- get_distance
- Cannot refer to an instance field arg while explicitly invoking a constructor
- MySQL · 性能优化 · MySQL常见SQL错误用法
- 迭代器使用
- 帧缓冲对象-实践过程问题及解决
- node.js常用命令
- 谈谈JAVA AIO
- org.xml.sax.SAXException: Scanner State 24 not Recognized
- SDIO总线协议
- CSDN
- select模型详解
- ATM自动存取款系统(第二版)
- markdown教程