logo资料库

SpeedTree学习与实践笔记.doc

第1页 / 共4页
第2页 / 共4页
第3页 / 共4页
第4页 / 共4页
资料共4页,全文预览结束
SpeedTree 学习与实践笔记(2009-04-08 10:57:05) 标签:it 分类:Speedtree 文章引自:http://www.cnblogs.com/flying_bat/archive/2008/12/14/1354884.html 1. 该插件的特点: api 无关。它本身只是数据结构和逻辑架构,没有任何渲染语句子,因此为了把它应用到自 己的引擎里,需要为之添加渲染相关的语句。而根据 sdk 的讲解,推荐用户为之搭建中间架 构,用来联系 SPEEDTREE 与自己的引擎。这样做起码有两点好处,搭建的中间架构(也推荐 别加任何 api 相关的语句),因此,即使你以后换了 api(譬如从 gl 换成 dx),中间架构还是 可以继续沿用的。还有一个好处就是,当 speedtree 更新版本的时候,你也无须修改你的引 擎,而只需要修改相对简单而且稳定的中间架构。
2. 该插件的具体特性: 注意,下面具体特性分析都是基于 SDK 里一个叫“DirectX9”的例子进行的,在这个例子里, 它给出了最基本的使用方法,同时也向用户展示了它的基本特性。 A. 树的基本渲染 通过大场景的测试,DP 的个数大致是树木棵数的两到三倍。详细分析下,发现 它一棵树分三部分绘制:树干和大树枝(branches),小树枝(fronds),树叶(leaves) Branches:使用模型来绘制 Fronds:使用两个十字交叉的面模拟小树枝,为了节省三角形。 Leaves:使用 billboard 方式绘制,这样就能产生视觉效果比较好的叶子了。 它这样划分是出于以下三方面的考虑:这几部分的渲染状态不一样,动画的状态不一样,做 LOD 的时候也不一样。具体看下面的介绍。 B. 树的阴影系统 它包括两方面的阴影。首先是树干上的阴影。其次是整棵树在地面的投影。 树干的自阴影(self shadow)是预先生成的,至于生成的算法,可能是可以根据可穿透的 光线跟踪,也可能是结合 shadow map 的逐象素地生成光照贴图(把树干的面都展开后,在 对应的地方画上阴影).有了该光照贴图,那渲染树干的时候就可以跟树干本身的纹理进行 混合产生比较真实的效果。 而整棵树在地面的透影子,则是使用一个矩形画出来的,阴影贴图也是预先生成好。渲染的 时候浮在地面。 C. 树的动画 树的三部分的动画状态都是不一样的。这对优化有极其重要的作用。风小的时候,或是树离
眼睛比较远的时候,可以不动树枝,而只是动树叶。而具体他们是怎么动的: 树叶的动画:就是一个 billboard 的来回平移以及他本身绕视坐标系统 Z 轴的转动。 树枝的动画:通过它引擎本身计算出来的矩阵进行动画。 而至于它具体怎么渲染动画的,它提供了基于 CPU 和 GPU 的方法。 基于 CPU 的方法是:创建顶点缓冲的时候, 使用 D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY 标记(这种方法能提高 CPU 修改和更新该缓冲的速度),渲染的时候实时更新顶点位置。 基于 GPU 的方法是:通过自定义的顶点 shader 程序进行,更新动画的时候,向 shader 传递 常量数组。 D. 树的光照 它可以打开和关闭实时光照,对于实时光照,树干部分又分两种情况,对于没有法线贴图的 树干,使用 per-vertex 的光照。而对于有法线贴图的,则使用 per-pixcel 光照。至于给不 给树干渲染法线贴图,则根据具体的程序决定。 而对于树叶的渲染,因为它是一个 billboard,因此也无法通过其法线来计算光照。它其实 是根据这个 billboard 的位置来确定其亮度的。通过把整棵树当成一个球来分析,而每个 billboard 的位置就相当于是球上的一点,结合光的方向,计算出该点的亮度。 E. LOD 的特点 其强大的 LOD 系统,为实现大规模的场景提供了有力的支持。这里的 LOD 分三方面:顶点的 LOD,纹理的 LOD,动画的 LOD。 (1) 顶点的 LOD:首先是针对树干,因为这里的树干是实实在在的模型。至于树干的建 立,它里面是采用贝塞尔曲线来描述整个 mesh 的,贝塞尔曲线的描述方式无疑给即时高效 率的 LOD 计算提供了可行性。同时这还针对树枝,远了之后,小树枝就不渲染了。到了一定 的距离的时候,整棵树其实就变成一个 billboard 了。 (2) 纹理的 LOD:树干上在最高精度的时候会有三套纹理:基本纹理,光照贴图,法线 贴图。随着 LOD 的进行,可以依次减去法线贴图,光照贴图,最后是本身贴图,最后只为树 干渲染一种颜色。 (3) 动画的 LOD:现在有三种动画,大树枝(模型)的动画,小树枝(两个交叉面)的 动画,以及树叶的动画。随着 LOD 的进行,依次去掉大树杆的动画,小树杆的动画,最后是 树叶的动画。这也是符合视觉效果的。 F. 文件系统 用场景来分析的话,一个场景是.stf 文件(Speed Tree Forest).该文件描述了每棵树的 相关属性。而一棵树是通过一个.spt(Speed Tree)文件来描述的.用文本编辑器打开,就 能看到里面记录了该树的所有信息。而该插件为此开发了配套了树木编辑器材。使用该编辑 器,打开.spt 文件之后,就可以对该树进行浏览以及编辑。 3.Speedtree 使用实践 它提供给用户的一个最主要的类就是 CSpeedTreeRT.这是一个 speedtree 对外界的接口,从 SpeedTreeRT.h 中可以看到,这个类其实是包括了该插件的核心类.因此,我们在使用该插 件的时候,其实全都是通过这个接口。 譬如 CSpeedTreeRT::SetCamera(eye, viewDir),通知它内部现在的摄像机的信息,然后它
内部就根据这些信息计算出正确的 billboard. 而如何加载一棵树呢?使用 CSpeedTreeRT::LoadTree(const char *treefile);输入一 个”.spt”文件,然后我们设置光照和风效果的方法如 CSpeedTreeRT::SetBranchWindMethod,SetFrondWindMethod,SetBranchLightingMethod, SetLeafLightingMethod, SetLodLimits 等,接着执行 CSpeedTreeRT::Compute(),然后它 里面就开始进行黑盒处理,最后我们就可以获取其几何数据(CspeedTreeRT::GetGeometry) 进行渲染。获取之前还可以手动去设置 LOD 级别 CSpeedTreeRT::SetLodLevel,然后你获取 到的就是经过 LOD 处理的几何数据。 不过有一点需要要注意的是,speedtree 里面用的是右手坐标系(尽管它说可以通过 define Y_UP 来改变坐标系统,但我没发现 define 改了之后有什么变化,很奇怪)。笔者开始的时候 完全没注意到这点,发现搬到自己的架构后,树全都是横着的。当时死活发现不了问题,就 去旋转每棵树。然后又发现那些树叶也无法正常地旋转成 billboard,又查了很久。后来终 于发现,是因为 speedtree 内部使用右手坐标系进行计算。而我的架构是使用左手,这样一 来,连传给 speedtree camera 的数据都要修改了, CSpeedTreeRT::SetCamera(eye, viewDir), 其中的 eye,eyeDir,都得经过变换再传进去: float3 viewDir=pCamera->GetViewDir(); float3 eye=pCamera->GetEye(); float afDirection[3]; afDirection[0] = viewDir.x; afDirection[2] = viewDir.y; afDirection[1] = -viewDir.z; CSpeedTreeRT::SetCamera(eye, afDirection); 4.把 speedtree 加到自己的引擎中去 以上所说的 CSpeedTreeRT 接口,笔者在使用的时候都是让一个 CSpeedTreeRT 对象汇聚到自 己设计的一个 tree 类里。通过这种方式来封装 speedtree,搭建中间架构。CSpeedTreeRT 这接口也许多静态函数,譬如 SetCamera,参照它的 DEMO, 直接“CSpeedTreeRT::SetCamera(eye, eyeDir);”但要实现完美地跟自己的引擎相结合, 也并不是一件容易的事情。主要是,自己的引擎本来就有一套完整的渲染系统,LOD 系统, 动画系统,而且跟 speedtree 的方式也不一样。一个极端的做法就是,对于 SpeedTreeRT, 屏蔽其实时计算,而是根据自己引擎的系统计算,这样的话,其实是只利用了 SpeedTree 的数据结果了。而另外一个极端就是,不管 speedtree 和自己引擎的关系,只保留简单的 耦合,各自使用各自的系统,只是让他们的渲染行为(LOD,光照效果等)保持一致性。至 于更好的办法,笔者也是在研讨中,我非常希望能跟读者进行探讨,这也是笔者写本笔记的 动机之一。 已投稿到: 排行榜 圈子 阅读(97)|评
分享到:
收藏