Ogre1.8地形和天空盒的建立过程详解(一块地形)

本文详细介绍了如何在Ogre1.8中构建地形和天空盒,包括地形算法、Ogre的LOD论文链接、以及使用Ogre的地形API创建和配置地形的过程。通过创建帧监听器实现相机移动,并使用资源加载器设置地形纹理。此外,还讨论了`Terrain::getLayerBlendMapSize()`函数的作用和设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译环境 WIN7 32  VS2008   Ogre的版本 1.8

Ogre的地形算法是采用Geometry MIPMap的算法,当然贴图也是采用MIPMap的算法,这里有Ogre LOD算法的论文解释:http://www.flipcode.com/articles/article_geomipmaps.pdf

看到Ogre的Terrain,突然也想自己实现一个地形的打算,不过估计要过一段时间了,现在来说,ClipMap算法应该更适应现在的游戏,具体的内容可以查看http://www.docin.com/p-118698727.html,对于了解现在LOD的地形算法,这是一篇相当不错的论文

 

为了简化,就一个main.cpp文件,这个cpp文件中包含了监听器类和Application类以及main入口函数:

#include <Ogre.h>
#include <OIS/OIS.h>
#include <iostream>
#include <OgreTerrain.h>
#include <OgreTerrainLayerBlendMap.h>
#include <OgreTerrainGroup.h>

class MyFrameListener : public Ogre::FrameListener
{
private:
 OIS::InputManager *m_pInputManage;
 OIS::Keyboard *m_pKeyBoard;
 OIS::Mouse *m_pMouse;
 Ogre::Camera *m_pCamera;
 Ogre::Viewport *m_pViewport;
 Ogre::Timer m_Time;

 bool m_bWirmline;

 float m_fMovementSpeed;

public:
 MyFrameListener( Ogre::RenderWindow *pWin, Ogre::Camera *pCamera, Ogre::Viewport *pViewport )
 {
  m_pCamera = pCamera;
  m_fMovementSpeed = 10;

  m_Time.reset();

  m_pViewport = pViewport;
  m_bWirmline = true;

  OIS::ParamList Params;
  size_t WindowHandle = 0;
  std::ostringstream WinHandleString;
  pWin->getCustomAttribute( "WINDOW", &WindowHandle );
  WinHandleString<<WindowHandle;

  Params.insert( std::make_pair( "WINDOW", WinHandleString.str() ) );

  m_pInputManage = OIS::InputManager::createInputSystem( Params );

  m_pKeyBoard = static_cast<OIS::Keyboard*>( m_pInputManage->createInputObject( OIS::OISKeyboard, false ) );
  m_pMouse = static_cast<OIS::Mouse*>( m_pInputManage->createInputObject( OIS::OISMouse, false ) );

 }
 ~MyFrameListener()
 {
  m_pInputManage->destroyInputObject( m_pKeyBoard );
  m_pInputManage->destroyInputObject( m_pMouse );
  OIS::InputManager::destroyInputSystem( m_pInputManage );
 }


 bool frameStarted( const Ogre::FrameEvent& evt )
 {
  m_pKeyBoard->capture();
  bool bWalk = false;
  if( m_pKeyBoard->isKeyDown( OIS::KC_ESCAPE ) )
  {
   return false;
  }
  if( m_pKeyBoard->isKeyDown( OIS::KC_R ) && m_Time.getMilliseconds() > 250 )
  {
   m_Time.reset();
   if( m_bWirmline )
   {
    m_pCamera->setPolygonMode( Ogre::PolygonMode::PM_WIREFRAME );
    m_bWirmline = false;
   }
   else
   {
    m_pCamera->setPolygonMode( Ogre::PolygonMode::PM_SOLID );
    m_bWirmline = true;
   }
  }
  Ogre::Vector3 translate( 0, 0, 0 );

  if( m_pKeyBoard->isKeyDown( OIS::KC_W ) )
  {
   translate += Ogre::Vector3( 0, 0, -1 );
  }
  if( m_pKeyBoard->isKeyDown( OIS::KC_S ) )
  {
   translate += Ogre::Vector3( 0, 0, 1 );
  }
  if( m_pKeyBoard->isKeyDown( OIS::KC_D ) )
  {
   translate += Ogre::Vector3( 1, 0, 0 );
  }
  if( m_pKeyBoard->isKeyDown( OIS::KC_A ) )
  {
   translate += Ogre::Vector3( -1, 0, 0 );
  }

  m_pCamera->moveRelative( translate * m_fMovementSpeed * evt.timeSinceLastFrame * m_fMovementSpeed * 8 );

  m_pMouse->capture();

  float fDotX = m_pMouse->getMouseState().X.rel * evt.timeSinceLastFrame * -1;
  float fDotY = m_pMouse->getMouseState().Y.rel * evt.timeSinceLastFrame * -1;

  m_pCamera->yaw( Ogre::Radian( fDotX ) );
  m_pCamera->pitch( Ogre::Radian( fDotY ) );


  return true;
 }

 bool frameRenderingQueued( const Ogre::FrameEvent& evt )
 {
  return true;
 }

 bool frameEnded( const Ogre::FrameEvent& evt)
 {
  return true;
 }
};

class MyApplication
{
private:
 Ogre::Root *m_pRoot;
 Ogre::SceneManager *m_pSceneManage;
 MyFrameListener *m_pFrameListener;
 bool m_bKeepRunning;
 Ogre::TerrainGlobalOptions *m_pTerrGloOp;
 Ogre::TerrainGroup *m_pTerrGroup;
 bool m_bLoadNewMap;

public:
 MyApplication()
  : m_pRoot( NULL ),
  m_pSceneManage( NULL ),
  m_pFrameListener( NULL ),
  m_bKeepRunning( true ),
  m_pTerrGloOp( NULL ),
  m_bLoadNewMap( false )
 {
 }

 ~MyApplication()
 {
  if( m_pTerrGloOp )
  {
   delete m_pTerrGloOp;
  }
  if( m_pTerrGroup )
  {
   delete m_pTerrGroup;
  }

  if( m_pRoot )
  {
   delete m_pRoot;
   m_pRoot = NULL;
  }
 }

 void RenderOneFrame()
 {
  Ogre::WindowEventUtilities::messagePump();
  m_bKeepRunning = m_pRoot->renderOneFrame();
 }

 bool KeepRunning()
 {
  return m_bKeepRunning;
 }

 void LoadResources()
 {
  Ogre::ConfigFile cf;
  cf.load( "resources_d.cfg" );
  Ogre::ConfigFile::SectionIterator SecIter = cf.getSectionIterator();
  Ogre::String SectionName, DataName, TypeName;

  while( SecIter.hasMoreElements() )
  {
   SectionName = SecIter.peekNextKey();
   Ogre::ConfigFile::SettingsMultiMap *SetMap = SecIter.getNext();
   Ogre::ConfigFile::SettingsMultiMap::iterator SetIter;
   for( SetIter = SetMap->begin(); SetIter != SetMap->end(); ++SetIter )
   {
    TypeName = SetIter->first;
    DataName = SetIter->second;
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation( DataName, TypeName, SectionName );
   }
  }

  Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
 }

 int StartUp()
 {
  if( !m_pRoot )
  {
   m_pRoot = new Ogre::Root( "plugins_d.cfg" );
   if( !m_pRoot->showConfigDialog() )
   {
    return -1;
   }
  }
  Ogre::RenderWindow *pWindow = m_pRoot->initialise( true );

  m_pSceneManage = m_pRoot->createSceneManager( Ogre::ST_GENERIC );
  Ogre::Camera *pCamera = m_pSceneManage->createCamera( "Camera1" );
  pCamera->setPosition( 0, 1400, 100 );
  pCamera->lookAt( 0, 0, 0 );
  pCamera->setNearClipDistance( 5 );

  Ogre::Viewport *pViewport = pWindow->addViewport( pCamera );
  pViewport->setBackgroundColour( Ogre::ColourValue( 0, 0, 0, 1 ) );
  pCamera->setAspectRatio( Ogre::Real( pViewport->getActualWidth() ) / Ogre::Real( pViewport->getActualHeight() ) );

  LoadResources();
  CreateScene();
  m_pFrameListener = new MyFrameListener( pWindow, pCamera, pViewport );
  m_pRoot->addFrameListener( m_pFrameListener );
  return 0;

 }
 void ConfigureTerrain( Ogre::Light *pLight )
 {
  m_pTerrGloOp = new Ogre::TerrainGlobalOptions();
  m_pTerrGloOp->setMaxPixelError( 8 );
  m_pTerrGloOp->setCompositeMapDistance( 3000 );
  m_pTerrGloOp->setCompositeMapAmbient( m_pSceneManage->getAmbientLight() );
  m_pTerrGloOp->setLightMapDirection( pLight->getDerivedDirection() );
  m_pTerrGloOp->setCompositeMapDiffuse( pLight->getDiffuseColour() );

  m_pTerrGroup = new Ogre::TerrainGroup( m_pSceneManage, Ogre::Terrain::ALIGN_X_Z, 513, 12000 );
  m_pTerrGroup->setFilenameConvention( Ogre::String( "SaveTerrain" ), Ogre::String( "dat" ) );
  m_pTerrGroup->setOrigin( Ogre::Vector3::ZERO );
  Ogre::Terrain::ImportData &Imp = m_pTerrGroup->getDefaultImportSettings();
  Imp.maxBatchSize = 65;  //一个tile中最多包含的顶点数
  Imp.minBatchSize = 33;  //一个tile中最少包含的顶点数
  Imp.inputScale = 600;

  Imp.layerList.resize( 3 );
  Imp.layerList[0].worldSize = 100;
  Imp.layerList[0].textureNames.push_back( "dirt_grayrocky_diffusespecular.dds" );
  Imp.layerList[0].textureNames.push_back( "dirt_grayrocky_normalheight.dds" );
  Imp.layerList[1].worldSize = 30;
  Imp.layerList[1].textureNames.push_back( "grass_green-01_diffusespecular.dds" );
  Imp.layerList[1].textureNames.push_back( "grass_green-01_normalheight.dds" );
  Imp.layerList[2].worldSize = 200;
  Imp.layerList[2].textureNames.push_back( "growth_weirdfungus-03_diffusespecular.dds" );
  Imp.layerList[2].textureNames.push_back( "growth_weirdfungus-03_normalheight.dds" );
 }

 void InitBlend( Ogre::Terrain *pTerrain )
 {
  Ogre::TerrainLayerBlendMap *pBlend1 = pTerrain->getLayerBlendMap( 1 );
  Ogre::TerrainLayerBlendMap *pBlend2 = pTerrain->getLayerBlendMap( 2 );
  Ogre::Real MinHeight1 = 70;
  Ogre::Real FadeDist1 = 40;
  Ogre::Real MinHeight2 = 70;
  Ogre::Real FadeDist2 = 15;
  float *pBlend1Point = pBlend1->getBlendPointer();
  float *pBlend2Point = pBlend2->getBlendPointer();
  for( Ogre::uint16 y = 0; y < pTerrain->getLayerBlendMapSize(); ++y )
  {
   for( Ogre::uint16 x = 0; x <pTerrain->getLayerBlendMapSize(); ++x )
   {
    Ogre::Real tx, ty;
    pBlend1->convertImageToTerrainSpace( x, y, &tx, &ty );
    Ogre::Real height = pTerrain->getHeightAtTerrainPosition( tx, ty );
    Ogre::Real val = ( height - MinHeight1 ) / FadeDist1;
    val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) );
    *pBlend1Point++ = val;

    val = ( height - MinHeight2 ) / FadeDist2;
    val = Ogre::Math::Clamp( val, static_cast<Ogre::Real>( 0 ), static_cast<Ogre::Real>( 1 ) );
    *pBlend2Point++ = val;
   }
  }

  pBlend1->dirty();
  pBlend2->dirty();
  pBlend1->update();
  pBlend2->update();
 }

 void GetTerrainImage( bool bFlipx, bool bFlipy, Ogre::Image &img )
 {
  img.load( "terrain.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
  if( bFlipx )
  {
   img.flipAroundY();
  }
  if( bFlipy )
  {
   img.flipAroundX();
  }
 }

 void DefineTerrain( long x, long y )
 {
  Ogre::String FileName = m_pTerrGroup->generateFilename( x, y );
  if( Ogre::ResourceGroupManager::getSingleton().resourceExists( m_pTerrGroup->getResourceGroup(), FileName ) )
  {
   m_pTerrGroup->defineTerrain( x, y );
  }
  else
  {
   Ogre::Image img;
   GetTerrainImage( x % 2, y % 2, img );
   m_pTerrGroup->defineTerrain( x, y, &img );
   m_bLoadNewMap = true;
  }
 }

 void CreateScene()
 {

  Ogre::Light *pLight = m_pSceneManage->createLight( "Light1" );
  pLight->setType( Ogre::Light::LT_DIRECTIONAL );
  pLight->setDirection( Ogre::Vector3(0.55, -0.3, 0.75) );
  pLight->setSpecularColour( Ogre::ColourValue( 0.4f, 0.4f, 0.4f ) );
  pLight->setDiffuseColour( Ogre::ColourValue::White );

  m_pSceneManage->setAmbientLight( Ogre::ColourValue( 0.2f, 0.2f, 0.2f ) );

  ConfigureTerrain( pLight );

  DefineTerrain( 0, 0 );

  m_pTerrGroup->loadAllTerrains( true );

  Ogre::TerrainGroup::TerrainIterator iter = m_pTerrGroup->getTerrainIterator();
  while( iter.hasMoreElements() )
  {
   Ogre::Terrain *t = iter.getNext()->instance;
   InitBlend( t );
  }

  if( m_bLoadNewMap )
  {
   //如果要反复修改TerrainGroup的数据,就不必保存地形了
   m_pTerrGroup->saveAllTerrains( true );  //注意,保存的是地形顶点和TerrainGroup的数据,TerrainGlobalOption的数据不会被保存
   m_bLoadNewMap =false;
  }

  m_pTerrGroup->freeTemporaryResources();
  Ogre::ColourValue FadeColour( 0.9, 0.9, 0.9 );
  m_pSceneManage->setFog( Ogre::FOG_LINEAR, FadeColour, 0.0f, 15000.0f, 28000.0f );
  Ogre::Plane plane;
  plane.d = 1000;
  plane.normal = Ogre::Vector3::NEGATIVE_UNIT_Y;
  m_pSceneManage->_setSkyPlane( true, plane, "Examples/CloudySky", 500, 20, true, 0.5f, 150, 150 );
 }
};


int main()
{

 MyApplication app;
 app.StartUp();
 while( app.KeepRunning() )
 {
  app.RenderOneFrame();
 }

 return 0;
}

 

  

不过在这里主要讲下Terrain::getLayerBlendMapSize()函数,这个函数返回的是Terrain的mLayerBlendMapSize。一开始没有设置的话,mLayerBlendMapSize默认是1024。也就是说将Blend Layer平铺完这个地形,然后分成1024个“区域”,然后计算高度平铺适合的纹理,混合等。可以通过Ogre::TerrainGlobalOptions::setLayerBlendMapSize( value )函数设置。

参考:http://www.cnblogs.com/WindyMax/

截图:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值