zoukankan      html  css  js  c++  java
  • Ogre2.1 Hlms与渲染流程

      在我前面三篇说明Ogre2.x的文章里,第一篇大致说了下Hlms,第二篇说了下和OpenGL结合比较紧的渲染,本文用来说下Hlms如何影响渲染流程中,因为有些概念已经在前面二文里说过了,本文就不再提,最后这篇也算是对Ogre2.1一个渲染流程的详细讲解。

    每桢场景流程

      一 所有场景更新,场景更新包含的内容如下。

    • 场景动画 _applySceneAnimations
    • 所有节点数据 UPDATE_ALL_TRANSFORMS
    • 骨骼动画 UPDATE_ALL_ANIMATIONS
    • 所有模型的AABB。(ObjectData)UPDATE_ALL_BOUNDS
    • 所有光源的AABB。 UPDATE_ALL_BOUNDS
    • 生成光源列表。BUILD_LIGHT_LIST

      如上这些部分Ogre1.x需要每次针对摄像机(RW与RTT)都重新来计算一次,在Ogre2.1中就每桢计算一次,去掉无效的更新与计算,并且这上面每步都能高效利用多线程,具体过程请看我上篇 Ogre2.0 全新功能打造新3D引擎

      二 场景对应workSpace->合成器节点(PassScene)->RenderTarget,因此下面这些和具体摄像机有关,其中主要是Cull当前模型进渲染通道。

    • 更新当前场景所有模型(ObjectData)UPDATE_ALL_LODS
    • 设置当前场景阴影节点。
    • 当前场景当前摄像机的Cull(CullPhase01). CULL_FRUSTUM,如果模型可见,添加到渲染通道中。

      这部分更新主要与当前摄像机有关,在RW中,数据保存给第四步

      三 RW(正常渲染)开始渲染阴影节点,而RTT(光源贴图)直接跳过本段走第四步。

    • 得到投射阴影的光源与对应的RTT.(PSSM中方向光源可以对应大于1个的RTT,一般用3个RTT).
    • 渲染每个RTT.(在之前交换正常Cull模型列表,所有阴影RTT渲染完成后再交换回来)。

      整个过程差不多如CompositorShadowNode上面的注释中的一段。

    a normal rendering flow with shadow map looks like this:
    
    normal->_cullPhase01();
    
    saveCulledObjects( normal->getSceneManager() );
    
        shadowNode->setupShadowCamera( normal->getVisibleBoundsInfo() );
    
        shadowNode->_cullPhase01();
    
        shadowNode->_renderPhase02();
    
    restoreCulledObjects( normal->getSceneManager() );
    
    normal->_renderPhase02();
    a normal rendering

    四 渲染承接上面Cull的数据(第二步)开始渲染(RenderPhase02)

    • mAutoParamDataSource信息填充 这个我记的是Ogre1.x中的着色器参数更新与RTSS需要的,Ogre2.1中应该不需要这个了。
    • Forward3D的生成,主要过程参见前篇文章,全局多光源。
    • 渲染通道开始渲染,参见RenderQueue。

      这部分主要就是渲染通道中的数据,渲染通道相对于Ogre1.x来说改动主要集中在线程通道与hash排序,这样在渲染中,能自动InstanceBatch并且减少渲染切换状态。

    HLMS如何插入渲染流程  

      HLMS在这个过程中做了啥,首先我们先看下相关概念。

      在前面 Ogre2.0 全新功能打造新3D引擎 中简单介绍了下HLMS,其中有Macroblocks块,Blendblocks块,以及Datablock块,相当于一个模块化的Material. 

      Hlms后台有很多相关如(PBS,Unit)模版,对应在OgreMain中的Hlms类包含很多方法是用来解析hlms语法规则的函数,相关hlms语法见前链接中Ogre2.1中的移值手册,如@property, @end, @foreach, @counter, @value, @set[add,sub,mul,div,mod,min,max], @piece等。这些语法与常用的着色器算法构成Hlms的着色器模板代码。

      RenderableCache:上面有个Renderable,但是不直接和Renderabl有关联,其实是这样的,里面二个字段,一个HlmsPropertyVec保存着材质属性的值组合(如有无切线,uv坐标个数,法线贴图,光照贴图等等),还一个字段PiecesMap,简单点来说,HlmsPropertyVec里保存属性与对应int的值,int用来表示bool,个数啥的,对应hlms语法就是如@property,@foreach,@counter等,而PiecesMap用来保存属性与对应string的值,简单来说,就是上面hlms语法@piece,也可以说是代码片断,具体情况请看前面所说的Ogre2.1移植文档,和Renderable的隐藏关系就是HLMS要根据Renderable的HlmsDatablock生成HlmsPropertyVec和PiecesMap,所以具体来说,应该是HlmsDatablock的Cache。

      HlmsCache前面讲解Ogre2.1高效渲染时有说,RenderableCache保存了生成着色器代码的模板变量,而HlmsCache保存根据RenderableCache生成的色器代码,其中hash不仅仅是Renderable的渲染属性组合,还包含了当前Hlms类型的渲染属性组合。

      一 Ogre初始化,拿到HLMS资源时。

      和Ogre1.x一样的是,在创建初始化窗口后,就开始加载资源,不同的是我们先需要像HlmsManager注册了几种Hlms类型,如PBS,Unit等。然后加载资源文件时,遇到如hlms Rocks pbs{}这种,首先开始解析其中的HlmsMacroblock,HlmsBlendlock这些数据,然后组成HlmsDatablock,这个可以这么理解,原来材质是Material->Technique->pass,现在除开着色器代码部分,如深度测试啥的全整合成一个HlmsDatablock,然后是生成HlmsPBS对应下的HlmsPbsDatablock的hash,然后添加到HlmsManager.

      二 创建场景,添加模型进场景时。

      和Ogre1.x类似的是,创建Entiy(Renderable),设置Material,在这变成创建Item(Renderable),然后设置HlmsDatablock,不同原来的直接把Renderable下关联Material完事,在Ogre2.1中,Hlms需要把当前Renderable放入Hlms调用calculateHashFor计算,这个过程大致可以看做首先是根据HlmsDatablock里所有渲染设置添加到Hlms里的mSetProperties,然后根据mSetProperties调用addRenderableCache生成上面所说的RenderableCache并添加到Hlms的列表中,并返回当前Hlms类型(假设是PBS)与当前RenderableCache在列表中位置组成的hash值,然后如上面生成阴影RTT时对应的RenderableCache与hash值,并返回正常渲染与阴影RTT的二个hash.

      三 每桢渲染时。

      这段因为和 Ogre2.1结合OpenGL3+高效渲染 里有多重复的逻辑,在这就没多说明,如果这里看不明白,请转到这篇里有针对这个地方更为详细的说明。

      首先摄像机执行cull,如上面的cullPhase01这步,检查当前模型是否可见,如果可见,添加进渲染通道中,在通道中包装成QueuedRenderable[见上面链接],这里复制其中一段,下面说明,检索所有可见的Renderable.根据Renderable的材质(在这是HlmsDatablock,非Ogre1.x中的pass)生成分段数hash(用于排序,其中先材质,再mesh),并把相关Renderable,分段数hash,对应的MovableObject包装成QueuedRenderable添加到线程渲染通道中,合并所有当前线程渲染通道到当前通道中.

      如前面渲染流程中的renderPhase02这步,在渲染具体模型之前,针对每个Hlms类型(PBS,Unit等)初始化一个HlmsCache,具体包含如是否是使用前向渲染,如果使用,则把当前Forward3D里的属性填充到对应HlmsCache中的HlmsPropertyVec中,还有如光源个数,方向光,聚光灯,点光源的个数分别写入HlmsPropertyVec中。

      然后开始渲染通道中的具体模型QueuedRenderable,与上面的HlmsCache(Hlms类型设置)根据Hlms::createShaderCacheEntry生成当前Renderable的HlmsCache。具体来说,根据renderableHash[见上面链接]找到对应的RenderableCache,把这个RenderableCache与HlmsCache(Hlms类型设置)里的HlmsPropertyVec组合起来,以及相应的PiecesMap放入对应模板(如PBS着色器模板)生成对应的着色器代码,所有着色器代码放入最终的HlmsCache,并根据相应hash[第二步中生成的hash]放入Hlms列表中,当后面有相同的hash进来后,直接取对应的HlmsCache,而不需要再生成一次着色器代码。

    Ogre中的CommandList

      最后,我们来看下RenderQueue::renderGL3做了啥,帮助我们理清内部commandlist显示组成,我们例子还是选定上链接中的这个例子,渲染模式用PBS,并去掉灯光与阴影影响。

      简单来说,有一个4*4个模型,其中一条对角线上全是球形,余下全是立方体,其中偶数行使用材质Rocks,奇数行使用Marble.调用glDraw…(DrawCall)的次数只需要二次或四次,看硬件支持情况,如何做到的了,在Ogre2.1中,把如上16个模型添加进渲染通道时,会根据材质,模型等生成排序ID,如上顺序大致为Rocks[sphere0-0,sphere2-2,cube0-1,cube0-2,cube0-3,cube2-1…], Marble[sphere1-1,sphere3-3,cube1-2,cube1-3…].那么他们CommandList顺序以及各Command含义。

      二者所用HLMS材质,Rocks与Marble。

    hlms Rocks pbs
    
    {
    
        roughness    0.4
    
        fresnel        1.33
    
        
    
        diffuse_map        Rocks_Diffuse.tga
    
        normal_map        Rocks_Normal.tga
    
        roughness_map    Rocks_Spec.tga
    
        specular_map    Rocks_Diffuse.tga
    
    }
    
     
    
    hlms Marble pbs
    
    {
    
        roughness    1.0
    
        detail_map0                MRAMOR6X6.jpg
    
        detail_offset_scale0     0 0 5 5
    
        roughness_map            MRAMOR-bump.jpg
    
    }
    Hlms 材质

      一般来说,CommandList列表如下顺序,特殊情况在这不讨论,渲染模式用PBS,并去掉灯光与阴影影响。

      CB_SET_MACROBLOCK 设定包含逐片断处理中的深度检查,还有剔除模型,显示模式。

      CB_SET_BLENDBLOCK 设定如逐片断处理中的Alpha混合操作。

      CB_SET_HLMS_BLOCK 对应上面的HlmsCache,绑定顶点,细分,几何,片断着色器。

    • 如下每个模型进入fillBuffersFor,首先如果从别的渲染模式切换成PBS渲染模式,理解为每一桢就好,因为每桢是渲染完一个模式再开始渲染另一种。从如下的CB_SET_CONSTANT_BUFFER_VS 到CB_SET_TEXTURE的所有命令一般只会在第一个模型进入fillBuffersFor调用,其余模型只是更新worldMatBuf里的数据。

      CB_SET_CONSTANT_BUFFER_VS U0设定PassBuffer,在顶点与着色器代码中如下。摄像机矩阵P,视图矩阵V。 

    //Uniforms that change per pass
    layout(binding = 0) uniform PassBuffer
    {
        //Vertex shader (common to both receiver and casters)
        mat4 viewProj;
        //Vertex shader
        mat4 view;    
        //-------------------------------------------------------------------------
        //Pixel shader
        mat3 invViewMatCubemap;
        vec4 ambientUpperHemi;
        vec4 ambientLowerHemi;
        vec4 ambientHemisphereDir;
        Light lights[1];    
    } pass;
    View Code

       CB_SET_CONSTANT_BUFFER_PS U0设定PassBuffer,代码同上。

      CB_SET_TEXTURE_BUFFER_VS T0 增加一个TBO,用来保存模型矩阵。 

    layout(binding = 0) uniform samplerBuffer worldMatBuf;
    worldMatBuf
    • 设定worldMatBuf,简单来说,存足够多的模型矩阵,一般来说,每一桢,worldMatBuf只需要一次设置,后续DrawCall共用这个TBO,通过baseInstance与divisor定位每个DrawCll里的每个instance的每个模型。
    • baseInstance:如上例子16个模型,模型的baseInstance也就是从0-15,其中Rocks材质时,前二个球同一实例,第一个球baseInstance为0,后面一个球只需要参数的Instance加1为2,然后6个立方体同一实例,重新添加一个参数,第一个立方体的baseInstance从2开始,后面的立方体只需要更新Instance参数为2到7。然后是Marble材质,如上前二个球同一实例,baseInstace从8开始,后面的球还是Instance为2,然后是立方体的baseInstace从10开始,后面的立方体只需要更新Instance参数为2到7。

      CB_SET_CONSTANT_BUFFER_PS U1 设定MaterialBuffer,每个模型的PBS渲染属性,如Fresnel(菲涅尔系数), roughness(粗糙度)等,这个UBO保存了一个PBS的渲染属性结合的列表,结合类ConstBufferPoolUser来看相应源码。 

    //Uniforms that change per Item/Entity, but change very infrequently
    struct Material
    {
        /* kD is already divided by PI to make it energy conserving.
          (formula is finalDiffuse = NdotL * surfaceDiffuse / PI)
        */
        vec4 kD; //kD.w is alpha_test_threshold
        vec4 kS; //kS.w is roughness
        //Fresnel coefficient, may be per colour component (vec3) or scalar (float)
        //F0.w is transparency
        vec4 F0;
        vec4 normalWeights;
        vec4 cDetailWeights;
        vec4 detailOffsetScaleD[4];
        vec4 detailOffsetScaleN[4];
    
        uvec4 indices0_3;
        //uintBitsToFloat( indices4_7.w ) contains mNormalMapWeight.
        uvec4 indices4_7;
    };
    
    layout(binding = 1) uniform MaterialBuf
    {
        Material m[273];
    } materialArray;
    MaterialBuf

       CB_SET_CONSTANT_BUFFER_VS U2 InstanceBuffer,常见指示MaterialBuffer中的索引。

    //Uniforms that change per Item/Entity
    layout(binding = 2) uniform InstanceBuffer
    {
        //.x =
        //The lower 9 bits contain the material's start index.
        //The higher 23 bits contain the world matrix start index.
        //
        //.y =
        //shadowConstantBias. Send the bias directly to avoid an
        //unnecessary indirection during the shadow mapping pass.
        //Must be loaded with uintBitsToFloat
        uvec4 worldMaterialIdx[4096];
    } instance;
    InstanceBuffer

      CB_SET_CONSTANT_BUFFER_PS U2 InstanceBuffer,代码如上。每个模型会更新InstanceBuffer这里面的数据,一般在PS中更新。

      CB_SET_TEXTURE_BUFFER_VS T0 因为要添加当前模型矩阵的数据,检查是否需要重新申请一个TBO,每桢第一次进来需要调用这个。

    • 当材质变化,一般来说,这些要重新设置

      CB_SET_TEXTURE 设定纹理如材质上面的Rocks_Diffuse.tga

      CB_SET_TEXTURE 设定纹理如材质上面的Rocks_Normal.tga

      CB_SET_TEXTURE 设定纹理如材质上面的Rocks_Spec.tga

      CB_TEXTURE_DISABLE_FROM 设定当前激活纹理为0

      CB_SET_VAO 顶点数据,位置,法线,索引等。

      CB_SET_INDIRECT_BUFFER DrawCall参数,如上例子中,有8个模型用一个材质,那么这个参数记录了这8个模型的DrawCall参数。

    • 在下面,一次绘制上面的Rocks材质下的八个模型

      CB_DRAW_CALL_INDEXED_EMULATED 嗯,我这台电脑不支持Indirect draw,所以选择这个DrawCall函数,如本例中,Rocks材质有八个模型,其中二个是球,6个是立方体,如果支持Indirect draw,此命令为CB_DRAW_CALL_INDEXED,里面只包含一次DrawCall,而不技能Indirect下的CB_DRAW_CALL_INDEXED_EMULATED,里面包含了二次DrawCall.

    • 开始绘制Marble材质下的八个模型。

      CB_SET_HLMS_BLOCK 对应上面的HlmsCache,绑定顶点,细分,几何,片断着色器。

      CB_SET_TEXTURE 设定纹理如材质上面的MRAMOR6X6.jpg

      CB_SET_TEXTURE 设定纹理如材质上面的MRAMOR-bump.jpg

      CB_TEXTURE_DISABLE_FROM 设定当前激活纹理为0

      CB_SET_VAO 顶点数据,位置,法线,索引等。

      CB_SET_INDIRECT_BUFFER DrawCall参数

    • 同上面,一次绘制上面的Marble材质下的八个模型 

      CB_DRAW_CALL_INDEXED_EMULATED

      简单来说,每桢进入PBS渲染,只有第一个模型调用fillBuffersFor来初始化相应UBO与TBO,后面的模型调用fillBuffersFor一般来说只是更新了存入模型矩阵的TBO里的数据以及保存材质索引的UBO中,当材质不同切换时,一般也只是重新绑定纹理,VBO,DrawCall参数,相应的状态切换如MACROBLOCK,BLENDBLOCK在这个例子只没有发生切换。

      新的合成器看了下,虽然引入一些新的概念,但是基本的Pass概念一样,中间也没有用到Hlms,还是用的老材质做特效,这部分用Hlms也没意义,一是老的材质里在这直接写相应着色器代码要方便写,二是Hlms针对模型的DrawCall整合功能这里没用,这里特效是针对RTT与RW的像素修改融合。比较重要的如PassQuad与PassScene还是一样,一个RTT,一个RW,新增如PassDepthCopy可以用一些pre-depth的特效。其余都是一些组合,也就不重新来仔细说了,有兴趣的同学可以看我写的Ogre1.9的合成器解析。  

  • 相关阅读:
    PAT (Advanced Level) 1060. Are They Equal (25)
    PAT (Advanced Level) 1059. Prime Factors (25)
    PAT (Advanced Level) 1058. A+B in Hogwarts (20)
    PAT (Advanced Level) 1057. Stack (30)
    PAT (Advanced Level) 1056. Mice and Rice (25)
    PAT (Advanced Level) 1055. The World's Richest (25)
    PAT (Advanced Level) 1054. The Dominant Color (20)
    PAT (Advanced Level) 1053. Path of Equal Weight (30)
    PAT (Advanced Level) 1052. Linked List Sorting (25)
    PAT (Advanced Level) 1051. Pop Sequence (25)
  • 原文地址:https://www.cnblogs.com/zhouxin/p/5028940.html
Copyright © 2011-2022 走看看