月下的博客

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  34 Posts :: 0 Stories :: 59 Comments :: 0 Trackbacks

常用链接

留言簿(5)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 

自从以前看了clayman博客后,就很想把自己代码里dxeffect框架替代掉。如今实习结束正好有短暂的自由时间,所以就ogrematerial开刀吧。

  山寨代码也是有学问的,如果你想搞明白这些代码的含义的话,最后之前就对这些已经有了一定的使用经历和认知,第一步先得自己想一下material系统的组成:
material
应该包含的:1Material->n Techinque->(n Pass + extra params)->(n TextureUnit + m or 0 shaderUnit + n RenderStates(部分)),Pass里的TextureUnit又包含了这一组texture的相关信息和设置,TexcoordSet, AddressMode, FilterSetting等等, ShaderUnit则是对应于vsfs以及新的gs,普通点也就最多两组。shaderUnit包含了具体的shader和其shaderParams,为何要把shadershaderParams分开呢,不说设计优雅的考虑,简单的说就是方便,很多时候params可能会需要共享,譬如shaderA里用到mvpMat,和eyePos diffuse,specular,可能shaderB也只用到这些,但是两个shader的内部计算并不相同。到此我们只分析了作为程序中的材质的结构,还需要考虑如果编写一个类似fx格式的material script,这也是另外一个大头,简单的说就是要做个脚本解释,然后给shader参数加上语义,减少大部分需要手动更新的shaderParams。鉴于我如今还没把这块扒完,等下次再写这块吧。


   当你至少能闭着眼睛想透这些了,那就可以开始看ogrematerial system了,先说句题外话,我读ogre的代码其实读挺久了。一直觉得很难读,开始觉得是自己水平不够,但后来实习才发现很多人都有这想法,甚至wolfgang对此也有批评,而自己曾经写了个olong引擎,接触过ipad编程的人应该对这些代码都比较熟悉~ogre里用了大量的设计模式,使得整个渲染流程从不同类里跳来跳去(SceneMgr这块看着最累了。。其实有很多地方看着都很累。。)个人觉得应该把renderSceneMgr里抽离出来做个RenderMgr,(记住这里的RenderMgr的概念和ogreRenderSystem,后者是提供底层渲染API的接口,而前者是管理renderData并最终递交给RenderSystem,以前也见有别人博客讲过这个问题)让SceneMgr专门管理Scene。。
 
打住打住。。我是来写material的。。咳咳。。相当于渲染来说,ogrematerial system还是相当易读的,也是因为这些模块相互关系几乎都是单向的,我们开始编写的时候可以先不考虑Technique,就是直接1 Material->n Pass->...当然甚至你都可以把multi pass去掉,就等同于1pass,当然由于pass都代码实际也没多少,我们还是加上好了。

原始渲染的伪代码就是:

 1for each pass
 2{
 3  setRenderStates(like alphe belnd, depth..)
 4  for each texture unit
 5    setTexSettings();
 6  if(hasVertexShader())
 7  {
 8    bindVertexShader();
 9    setShaderParams(vs);
10  }

11  if(hasFragmentShader())
12  {
13    bindFragmentShader();
14    setShaderParams(fs);
15  }

16  drawPrimitives();
17}

加入的RenderQueue也是一样,只是pass和对应的renderData我们是从renderqueue里取。

看到这里你会觉得,嘿,很简单嘛,但如果你看了ogre里关于shader参数的细节,你就知道这块还是比较复杂的。我们刚只说了一个运行过程,而建立params到constant registers的关系,以及如何编写autoConstants的代码都是魔鬼的细节~~对于dx,我们可以简单点,直接使用ID3DXConstantTable,它提供了set接口。但ogre则是直接从ID3DXConstantTable中解析参数。

   设计这块的时候,脑子里先要对我整个过程有个清晰的认识:一方面是从shader里(我们这里假设是hlsl,asm先不考虑),另一方面则是解析参数读入材质脚本里定义的参数(named,autoNamed,或者index),最后将两者对应上。

0.类型解释
看过OgreGpuProgramParams.cpp/h的人都知道:GpuConstantDefinition,GpuNamedConstants,GpuLogicalIndexUse,GpuSharedParameters,GpuSharedParametersUsage,一堆的struct和class。。其实按照我们上面这个思路逐步加断点分析,代码也没那么繁琐。暂时不看GpuSharedParameters这块,大概看下GpuConstantDefinition,重点变成员是physicalIndex和logicalIndex,前者的解释是buffer中的起始地址,每个GpuProgramParameters里都包含了vector<int>和vector<float>两个buffer,他们存储了shader变量的值,而physicalIndex就是对应vector的索引。而logicalIndex则是我们后面将hlsl编译成asm后这些变量所绑定的寄存器id,而由于constant reg的size都是4,即一个register为4个float的大小,所以这些logicalIndex则不一定连续,譬如第一个uniform为float1那么第二个uniform的logicalIndex虽然是1,但实际跨过了4个float,而实际内存中我们的float(int同) buffer里,两个变量之间的则就空了3个float,这就是physicalIndex的作用,所以我们还需要建立一个从logicalIndex到physicalIndex的map,也就是两个GpuLogicalBuffersStructPtr对象(这两个对象和GpuNamedConstantsPtr在GpuProgram类和GpuProgramParameters是共享的,都是sharedPtr)

1.解析shader的变量
  
我们从再具体的读取代码来看,假设我们已经从脚本里读入了对应的shader,经过一堆调用后(略。。)在D3D9HLSLProgram的函数buildConstantDefinitions->processParamElement将每个参数的registerIndex,physicalIndex, type(原子类型,即float, int,),size都写入GpuLogicalBuffersStructPtrGpuNamedConstantsPtr对象里(后者当然是只有highLevelShader里才有),并会为这个定义同样在插入带下标的一对键值,以便于如果我们传入的参数是数组(譬如float4x4[5]),则在程序里可以通过数组下标访问对应的数组变量。再constantDef建立好之后,将GpuNamedConstantsPtr传入到GpuProgramParameters对象里,然后向其中的两个vector添加上述对象大小的空间,在将两个LogicalToPhysicalMap的指针也传到shaderParams里,这样整个GpuProgramParameters几乎就完工了。

2.解析材质里定义的params,并将其与前者对应上
  如今就差再把material script定义的param解析出来并与之前shader里的变量进行对应。材质脚本读出的每个变量的信息包含了param类型(namedauto_namedindexauto_index),name,type(float,int..),autoType(auto的才有),初始值等。我们根据其autoType在全局变量AutoConstantDictionary中查找是否有定义,然后调用set[Named]AutoConstant,在这里我们在会在我们之前从hlsl里解析出的GpuNamedConstantsPtr里查找是否有该名字的变量(毕竟shader里的变量才是最终有用的,甚至由于编译器优化的关系,由于shader编写者可能定义了一个变量而未实际使用,在编译shader后,这个变量实际是会被省略的,这样在GpuNamedConstantsPtr也就找不到了)然后调用_setRawAutoConstant将这个变量加入到这个GpuProgramParameters对象所持有的autoConstant中。

(ps:关于_getFloatConstantLogicalIndexUse的用途我没看懂,为何需要去在mFloatLogicalToPhysical里去查找这个logicalIndex是否存在呢,我觉得如果走到这一步肯定shader里肯定就是定义了的,因为这个logicalIndex是由mNamedConstants里取出,我觉得是没必要的。。)

3,更新autoParamsbuffer并最终在DP前进行更新

把上面的过程看懂了,下面就很好理解了。GpuProgramParameters::_updateAutoParams里就是对GpuProgramParameters对象所持有的autoConstant值进行更新,当然这是我们只是对GpuProgramParameters持有的buffer里的值做更新,最终在文章开头的伪代码中的setShaderParamsogre里叫bindGpuProgramParamters而这里的更新就轻而易举了,我们遍历logicalToPhysicalMap对每个param取出对应的logicalIndexdataPointervector4ofCount,然后调用SetVertex/PixelShaderConstantF/I即可。



呼。。终于把这块写完了。。上述只是一个实现自己的material的基本思路。关于优化的话,我开头推荐的clayman的文章里有很多叙述。关于怎么写ogre script translator,等下一篇再说。。

posted on 2011-04-18 23:32 月下圆舞曲 阅读(4067) 评论(1)  编辑 收藏 引用 所属分类: 开发

Feedback

# re: 沿着ogre来实现material system(一) 2012-08-20 09:09 sxx
09:03:57: OGRE EXCEPTION(2:InvalidParametersException): Parameter called worldVeiwMatrix does not exist. in GpuProgramParameters::_findNamedConstantDefinition at ..\..\..\..\OgreMain\src\OgreGpuProgramParams.cpp (line 1435)  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理