|
这是从前学习Ogre时随手写下的注释,原来贴在燕尘的主页上,不幸主页被黑,好多篇都丢了。连我的发文资格也没了。 在硬盘上偶然找到一篇,贴在这里。
 //这是Ogre(RC1版本)中的一个很漂亮的演示: //幽蓝的天空下,广阔的草地随风起舞;Ogre鬼头深藏草丛中,仿佛远古的遗迹;天空飞舞着闪动的彩灯,仿佛外太空使者来访...... //呵呵,好美,但我不能贴出图来,因为燕版主还没给我这个权利。 //只好自己编译了去看了.......假如你的电脑是块很老很老的显示卡,恐怕也看不见草的摇摆了。 //这个Demo场景不算复杂,只有几百个mesh,用老技术完全可以很容易实现。采用新技术可算是杀鸡用宰牛刀了。 //该程序的主要原理是,使用三个方片,贴alpha草叶图,三个方片按竖中轴各60度交叉,形成一个草墩。 //场景中放置26X26个草墩,形成草地。草墩顶点坐标左右前后轻微移动........ //通过这个Demo,可初步了解一下新技术的采用方法,包括: //1.静态几何体(StaticGeometry)的使用: //这是Ogre1.0RC新出现的内容,将不需要移动的多个Mesh按区域集合,渲染时按批渲染。据Ogre称:这样比逐个渲染提高效率。 //Ogre为此做了一个庞大的管理器。这未免有点太夸张了——Ogre的很多地方都很夸张。 //2.CG程序的使用 //cg是目前的新潮。Ogre为我们提供了一个采用cg的简便的平台,可以跳过cg编译等一大堆东西,直接切入主题。 //本程序的cg不复杂,通过这个程序,我们将知道如何定义顶点脚本,怎么在顶点脚本嵌入cg程序,主程序如何把参数传给cg程序,如何写一个初步的cg脚本。 //(材料及cg脚本的注释在程序后面) //3.控制器(Controller)的使用,如何自动驱动灯光的闪动。这可能是老内容了 //注意,本程序包括在Ogre 1.0RC中,Demo_Grass,老的(0.152版)没有。 //以下是注释,非关键的地方就懒得注了。 /* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team Also see acknowledgements in Readme.html You may use this sample code for anything you like, it is not covered by the LGPL like the rest of the engine. ----------------------------------------------------------------------------- */ /** \file Grass.cpp \brief Specialisation of OGRE's framework application to show the use of the StaticGeometry class to create 'baked' instances of many meshes, to create effects like grass efficiently. **/ //老的头文件 #include "ExampleApplication.h" #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" #endif //定义键盘控制函数,按B键可以看到静态几何体的区域划分的边框 #define KEY_PRESSED(_key,_timeDelay, _macro) \ { \ if (mInputDevice->isKeyDown(_key) && timeDelay //参数定义 #define GRASS_HEIGHT 300 //草墩高300 #define GRASS_WIDTH 250 //草墩宽250 #define GRASS_MESH_NAME "grassblades" //草片mesh名 #define GRASS_MATERIAL "Examples/GrassBlades" //草片材料名定义 #define OFFSET_PARAM 999 //偏移参数 Light* mLight; SceneNode* mLightNode = 0; AnimationState* mAnimState = 0; ColourValue mMinLightColour(0.5, 0.1, 0.0); ColourValue mMaxLightColour(1.0, 0.6, 0.0); Real mMinFlareSize = 40; //闪光使用一个带光环的贴图,可以按此原理做简单的光晕了。 Real mMaxFlareSize = 80; StaticGeometry* mStaticGeom; /** This class 'wibbles' the light and billboard */ //灯光闪动控制对象 class LightWibbler : public ControllerValue { protected: Light* mLight; Billboard* mBillboard; ColourValue mColourRange; ColourValue mHalfColour; Real mMinSize; Real mSizeRange; Real intensity; public: //建立灯光对象,入口参数包括灯,转片,颜色范围,大小范围 LightWibbler(Light* light, Billboard* billboard, const ColourValue& minColour, const ColourValue& maxColour, Real minSize, Real maxSize) { mLight = light; mBillboard = billboard; mColourRange.r = (maxColour.r - minColour.r) * 0.5; mColourRange.g = (maxColour.g - minColour.g) * 0.5; mColourRange.b = (maxColour.b - minColour.b) * 0.5; mHalfColour = minColour + mColourRange; //保存亮度的中间值为参考 mMinSize = minSize; mSizeRange = maxSize - minSize; } //取灯光强度值函数,由控制器调用 virtual Real getValue (void) const { return intensity; } //灯光强度值设置函数,由控制器调用 virtual void setValue (Real value) { intensity = value; ColourValue newColour; // Attenuate the brightness of the light //在原始的的亮度中间值,增加或减少 newColour.r = mHalfColour.r + (mColourRange.r * intensity); newColour.g = mHalfColour.g + (mColourRange.g * intensity); newColour.b = mHalfColour.b + (mColourRange.b * intensity); mLight->setDiffuseColour(newColour); mBillboard->setColour(newColour); //转片与亮度一起放大缩小 // set billboard size Real newSize = mMinSize + (intensity * mSizeRange); mBillboard->setDimensions(newSize, newSize); } }; //听筒处理 class GrassListener : public ExampleFrameListener { protected: SceneManager* mSceneManager; bool mShowBBs; public: //构造函数 GrassListener(RenderWindow* win, Camera* cam, SceneManager* sceneManager) : ExampleFrameListener(win, cam), mSceneManager(sceneManager), mShowBBs(false) { } //草波处理函数 void waveGrass(Real timeElapsed) { //根据当前时间,确定变化参数 offset //坐标:简单理解为x左右方向,z前后方向,y上下方向。 static Real xinc = Math: I * 0.4; static Real zinc = Math: I * 0.55; static Real xpos = Math::RangeRandom(-Math: I, Math: I); //x随机位置(-3.14到3.14间) static Real zpos = Math::RangeRandom(-Math: I, Math: I); //z随机位置(-3.14到3.14间) xpos += xinc * timeElapsed; //x加时间因数*PI*0.4 zpos += zinc * timeElapsed; //z加时间因数*PI*0.55 ,前后幅度比左右略大 // Update vertex program parameters by binding a value to each renderable static Vector4 offset(0,0,0,0); //定义offset,这是要传给顶点程序的 StaticGeometry::RegionIterator rit = mStaticGeom->getRegionIterator(); //取得静态几何体的区域接口 //主循环遍历所有几何体区域, 刷新其各个组件的材料的参数 while (rit.hasMoreElements()) { //取得静态几何体的一个区域 StaticGeometry::Region* reg = rit.getNext(); //区域 //按区域摇摆,确定摇摆参数 //这样,一个区域都一样地摆动,有点机械 // a little randomness xpos += reg->getCentre().x * 0.001; //x根据中心位置作少许偏移 zpos += reg->getCentre().z * 0.001; //z.... offset.x = Math::Sin(xpos) * 0.05; //草的摇摆幅度x offset.z = Math::Sin(zpos) * 0.05; //草的摇摆幅度z StaticGeometry::Region: ODIterator lodit = reg->getLODIterator(); //LOD //这个循环列举每个区域的所有Mesh元素 while (lodit.hasMoreElements()) { StaticGeometry: ODBucket* lod = lodit.getNext(); StaticGeometry: ODBucket::MaterialIterator matit = lod->getMaterialIterator(); //取得材料接口 //一个元素可能采用不止一种材料,故命名为材料桶,列举所有材料元素 while (matit.hasMoreElements()) { StaticGeometry::MaterialBucket* mat = matit.getNext(); StaticGeometry::MaterialBucket::GeometryIterator geomit = mat->getGeometryIterator(); //geomit //循环列举材料筒所有材料 while (geomit.hasMoreElements()) { StaticGeometry::GeometryBucket* geom = geomit.getNext(); //将新的偏移参数(offset)传送到geomit geom->setCustomParameter(OFFSET_PARAM, offset); //OFFSET_PARAM为索引指针,在本程序开始定义为999, //与材料里面顶点程序的定义匹配:材料里定义为param_named_auto offset custom 999 //这样,就将offset传送到材料的offset参数 //本材料使用的cg程序,将按此设定作顶点处理 } } } } } //每帧前处理 bool frameStarted(const FrameEvent& evt) { bool ok = ExampleFrameListener::frameStarted(evt); //父功能调用 static Real timeDelay = 0; timeDelay -= evt.timeSinceLastFrame; //更新时间参数 if (mAnimState) mAnimState->addTime(evt.timeSinceLastFrame); //动画器处理,驱动灯光位置变化 KEY_PRESSED(KC_B, 1, mShowBBs = !mShowBBs; mSceneManager->showBoundingBoxes(mShowBBs); //按B键,打开包围盒显示。(或者关上) ) waveGrass(evt.timeSinceLastFrame); //调草波处理函数 return ok; } }; //主过程 class Grass_Application : public ExampleApplication { public: Grass_Application() {}
protected: SceneNode *mpObjsNode; // the node wich will hold our entities //建立一个草墩的Mesh体, void createGrassMesh() { //使用硬件缓存建立 // Each grass section is 3 planes at 60 degrees to each other // Normals point straight up to simulate correct lighting //每墩草使用3个平面组成,三个以y轴中心交叉的方片,片间角度为60度。 //其贴图采用带alpha通道的png格式——这是游戏里通常做树木的手法 //2006.1.22:这涉及到透明物体的排序,Ogre如何实现许多的面排序的? //分组的目的是否是利于先组内排序,再组间排序以减少运算量? MeshPtr msh = MeshManager::getSingleton().createManual(GRASS_MESH_NAME, ResourceGroupManager: EFAULT_RESOURCE_GROUP_NAME); SubMesh* sm = msh->createSubMesh(); sm->useSharedVertices = false; sm->vertexData = new VertexData(); //建立新的顶点数据 sm->vertexData->vertexStart = 0; sm->vertexData->vertexCount = 12; //12个顶点 //加入顶点附加参数描述,即包括位置,法线,贴图坐标 VertexDeclaration* dcl = sm->vertexData->vertexDeclaration; size_t offset = 0; dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION); //顶点位置 offset += VertexElement::getTypeSize(VET_FLOAT3); dcl->addElement(0, offset, VET_FLOAT3, VES_NORMAL); //法线 offset += VertexElement::getTypeSize(VET_FLOAT3); dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES); //贴图坐标 offset += VertexElement::getTypeSize(VET_FLOAT2); //建立顶点硬件缓存 HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton() .createVertexBuffer( offset, 12, HardwareBuffer::HBU_STATIC_WRITE_ONLY); //锁定,设置单个草墩顶点 float* pReal = static_cast(vbuf->lock(HardwareBuffer::HBL_DISCARD)); Vector3 baseVec(GRASS_WIDTH/2, 0, 0); Vector3 vec = baseVec; Quaternion rot; rot.FromAngleAxis(Degree(60), Vector3::UNIT_Y); int i; for (i = 0; i //点2 // position *pReal++ = vec.x; *pReal++ = GRASS_HEIGHT; *pReal++ = vec.z; // normal *pReal++ = 0; *pReal++ = 1; *pReal++ = 0; // uv *pReal++ = 1; *pReal++ = 0; //点3 // position *pReal++ = -vec.x; *pReal++ = 0; *pReal++ = -vec.z; // normal *pReal++ = 0; *pReal++ = 1; *pReal++ = 0; // uv *pReal++ = 0; *pReal++ = 1; //点4 // position *pReal++ = vec.x; *pReal++ = 0; *pReal++ = vec.z; // normal *pReal++ = 0; *pReal++ = 1; *pReal++ = 0; // uv *pReal++ = 1; *pReal++ = 1; vec = rot * vec; } vbuf->unlock(); sm->vertexData->vertexBufferBinding->setBinding(0, vbuf); //顶点设置完毕,绑定到硬件缓存0 //下面设置面片,索引到上面的顶点 sm->indexData->indexCount = 6*3; //3个平面,每面2个三角形, sm->indexData->indexBuffer = HardwareBufferManager::getSingleton() .createIndexBuffer(HardwareIndexBuffer::IT_16BIT, 6*3, HardwareBuffer::HBU_STATIC_WRITE_ONLY); uint16* pI = static_cast( sm->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); for (i = 0; i *pI++ = 0 + off; //第2个三角形 *pI++ = 2 + off; *pI++ = 3 + off; } sm->indexData->indexBuffer->unlock(); //数据设定完毕 sm->setMaterialName(GRASS_MATERIAL); //指定为GRASS_MATERIAL材料 } |
|