查看: 5704|回复: 1

OSG原创教程:最长的一帧(24)

[复制链接]

该用户从未签到

发表于 2008-9-16 13:52:12 | 显示全部楼层 |阅读模式
当前位置:osgUtil/RenderStage.cpp第1014行,osgUtil::RenderStage::draw ()
首先我们要解决的问题是RenderStage::runCameraSetUp和RenderStage::copyTexture这两个函数的工作内容。事实上也就是对“悬疑列表”中“什么是渲染目标实现方式”这个搁置了十数日的话题作一番讨论。

我们第一次接触到Camera::getRenderTargetImplementation函数是在“第八日”中,讲解Viewer::eventTraversal函数时(Viewer.cpp,623行),不过事件遍历的代码中并没有体现出“渲染目标”或者“Render Target”的任何作用来。事实上,这个函数帮助我们实现了一个场景渲染过程中可能非常重要的功能,即纹理烘焙(Render To Texture,RTT),或者称之为“渲染到纹理”。RTT技术意味着我们可以将后台实时绘制得到的场景图像直接作为另一个场景中对象的纹理,从而实现更加丰富的场景表达效果。

在OpenGL的较早版本中,RTT技术的实现主要是通过从帧缓存(Frame Buffer)中取得数据并传递给纹理对象来实现的;而随着硬件水平的发展,现在我们有了帧缓存对象(Frame Buffer Object),像素缓存(Pixel Buffer)等多种绘制平台的选择。

RTT实现的基本步骤为:(1)首先创建一个“渲染纹理”(Render Texture),例如FBO对象,像素缓存对象等;(2)设置它为图形设备的渲染目标(Render Target);(3)将“渲染纹理”绑定到一个纹理或图片对象上;(4)此时图形设备的渲染将在后台进行,其结果将直接体现在所绑定的纹理对象上。

“渲染目标”的设定即通过Camera::getRenderTargetImplementation函数实现。其可用的传入参数中,FRAME_BUFFER表示帧缓存,可以适用于较广泛的硬件平台上;而FRAME_BUFFER_OBJECT表示FBO对象,它可以用来实现离屏渲染(Offscreen Rendering)的工作,其渲染结果不会体现在图形窗口中。

设置渲染目标和绑定纹理的方法十分简单,例如:
  1. osg::Texture2D* texture = new osg::Texture2D;
  2. camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER );
  3. camera->attach( osg::Camera::COLOR_BUFFER, texture );
复制代码
即可将纹理对象texture与场景绘制的帧缓存绑定在一起。我们既可以将texture的内容保存成图片,作为场景的截图;也可以将纹理绑定到某个物体上,实现纹理烘焙的效果。这里osgprerender是一个很好的例子,使用附加参数--fb,--fbo,--pbuffer,--window等可以充分了解不同渲染目标实现的过程及其差异。

绑定到摄像机的实际纹理或者图片,在Camera类中均使用Camera::Attachment结构体来保存。而RenderStage::runCameraSetUp则反复遍历名为Camera::BufferAttachmentMap的映射表,检索并设置那些与颜色缓存(COLOR_BUFFER),深度缓存(DEPTH_BUFFER)等相对应的Attachment对象;RenderStage::copyTexture则负责针对FRAME_BUFFER渲染目标,拷贝场景图像到Attachment对象中。

下面我们就深入RenderStage::drawInner函数,它正是整个场景绘制的重点部分。

当前位置:osgUtil/RenderStage.cpp第780行,osgUtil::RenderStage::drawInner ()
此函数的工作首先是FBO对象的初始化,这里将使用FBOExtensions::isSupported和FrameBufferObject::hasMultipleRenderingTargets函数来判断显示卡是否支持FBO以及MRT(多重渲染目标)扩展,并使用FrameBufferObject::apply来调用实际的FBO执行函数。OpenGL为多重渲染目标的支持提供了多达十六个颜色缓存,在OSG中它们均表示为Camera::COLOR_BUFFERi,最后一个i取值为0到15。

如果没有启用FBO支持或者没有使用MRT的话,此时作为渲染树根节点的渲染台(RenderStage)还将负责使用glDrawBuffer和glReadBuffer分别设置场景绘制缓存和读取缓存的值(用户层次上则使用Camera类的成员函数setDrawBuffer 和setReadBuffer来实现)。当两个缓存的值均设置为GL_BACK时,场景的绘制将在后台缓存完成,并可以使用SwapBuffer动作交换前后双缓存的数据,避免场景绘制是产生闪烁。这也是OSG为场景缺省摄像机自动设置的特性(通过指定GraphicsContext::Traits::doubleBuffer的值,我们也可以在创建新的图形窗口时设置是否使用双缓存的特性)。

第二步是我们马上要重点研究的,RenderBin::draw函数。它负责从根节点开始遍历渲染树,并执行各个渲染叶(RenderLeaf)以及上层状态节点(StateGraph)所包含的内容。
完成场景的实际绘制工作之后,OSG将检测并显示出场景绘制当中遇到的错误。有的时候我们会在控制台看到“Warning: detected OpenGL error … after RenderBin::draw()”的字样,这事实上就是在RenderBin::draw函数绘制时产生的错误。通常是因为显示卡对OpenGL高版本的某些函数或枚举量不支持而造成的。

后面依然是有关FBO的操作,包括使用glBlitFramebufferEXT进行解算,将结果复制到关联的纹理以及图片对象中,并结束FBO的调用。

需要特别注意的是:如果希望使用FBO来实现纹理烘焙或者场景截图的话,不可以将场景主摄像机的setRenderTargetImplementation直接设置为相应的枚举量,那样将无法正常地看到场景(因为主摄像机对应的渲染台已经将场景绘制的结果绑定到FBO上了)。正确的作法是在场景树中增加一个Camera节点,设置“渲染目标实现方式”为FBO方式;并通过Camera::setRenderOrder设定它的渲染顺序,设置为PRE_RENDER可以保证这个摄像机在主场景之前执行绘制(它创建了一个“前序渲染台”,存入RenderStage::_preRenderList列表),从而实现“渲染到纹理”的效果。参见osgprerender例子以及第二十二日所述CullVisitor::apply(Camera&)函数的内容。

当前位置:osgUtil/RenderBin.cpp第387行,osgUtil::RenderBin::drawImplementation ()
RenderBin::draw函数的工作就是调用RenderBin::drawImplementation函数,当然用户也可用自定义的绘制回调(RenderBin::setDrawCallback)代替drawImplementation来完成这一绘制工作,当然前提是我们知道在场景绘制的过程中都需要完成什么。

我们曾经在第十七日的内容中列出了osg::State类的几点重要功能:(1)保存OpenGL的所有状态、模式、属性参数、顶点和索引数据;(2)提供了对OpenGL状态堆栈的处理机制,对即将进入渲染管线的数据进行优化;(3)允许用户直接查询各种OpenGL状态的当前值。

这里所述的第二点,对于OpenGL渲染状态堆栈的处理,实际上就是对于OSG状态树(StateGraph)的遍历处理。而各种OpenGL模式的开关设定(也就是我们熟悉的glEnable和glDisable)实际上是通过State::applyMode函数完成;顶点坐标,法线坐标以及各种顶点和索引数组的设置(即glVertexPointer,glNormalPointer等)也是由State类的相关函数,如setVertexPointer等实现的;各种渲染属性的OpenGL处理函数繁多而复杂,此时State类将使用applyAttribute函数,进而调用不同渲染属性对象的StateAttribute::apply(State&)函数,实现多种多样的渲染特性。

由此可见,osg::State类是OSG与OpenGL的主要接口,场景状态树的遍历者和整合者,也是各种渲染状态,以及顶点值的处理途径。但是我们早已知道,OSG的顶点坐标和索引信息是由osg::Geometry类负责保存的,那么负责将Geometry对象的数据传递给State对象的,就是渲染树的叶节点RenderLeaf了。它通过执行自己所包含的Drawable几何体对象的Drawable::draw函数,实现几何体的实际绘制;而在Geometry类的绘制过程中,则将自己记录的数据信息传递给State对象,由它负责完成顶点的载入和处理工作。

而渲染树在其中的作用,就是抽取每个渲染树节点(RenderBin)中的渲染叶(RenderLeaf)对象,交由osg::State整合它在状态树中继承的全部渲染状态,并将几何体数据传递给OpenGL管线,完成绘制的工作。

也许您对于这堆突如其来的结论颇为糊涂,没有关系,具体的代码我们将在下一日加以解析。

解读成果:
RenderStage::draw,RenderStage::drawInner。
悬疑列表:
Operation对象在线程中的应用时机是什么?

该用户从未签到

发表于 2008-9-28 11:30:57 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表