• 制作立体图像(下):用Ogre渲染立体图像


    了解红蓝眼镜原理之后剩下的事情就简单了

    如果不清楚红蓝眼镜原理,请先看上一篇:制作立体图像(一):红蓝眼镜原理

    另外你应该已经准备好了一副红蓝眼镜(如果没有请点击这里,然后关闭本页面:)

    现在戴上眼镜,先看看我们要做到的最终效果,一个旋转的立体地球:

    (当然这个是静止截图)

    先说说实现原理:

    1. 在坐标原点创建一个圆球模型,并贴上地球纹理
    2. 在恰当位置创建两个相机,并将两个相机的结果渲染到左右纹理
    3. 绘制全屏四边形,并应用立体材质,材质中通过shader对步骤2的纹理做红绿蓝混合,这个全屏四边形就是我们最终想要的结果

    以下是详细说明:

    1. 创建三维模型
      这一步最重要的是制作一副高清的地球纹理图,类似下面这样

      不过图片nasa早就为你准备好了,你可以到这里下载任何你想要的(鬼子真的很强大)
      创建地球mesh的代码也早有人帮你写好了,详见附带文件中函数:
      //根据mesh名称、半径、经纬线条数创建对应的mesh
      void MyApplication::createSphere(const std::string& meshName, const float r, const int nRings, const int nSegments)
    2. 相机设置
      渲染到纹理,左眼使用主相机mCamera,需另创建右眼相机
      //左眼纹理
          Ogre::TexturePtr textureLeft = Ogre::TextureManager::getSingleton().createManual("textureLeft", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWindow->getWidth(), mWindow->getHeight(), 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
          Ogre::RenderTexture *targetLeft = textureLeft->getBuffer()->getRenderTarget();
          targetLeft->addViewport(mCamera);
      
          //右眼纹理
          Ogre::Camera *cameraRight = mSceneMgr->createCamera("cameraRight");
          ...同上... ;

      设置相机

      //设置相机位置、焦距
          const int x = 10, y = 150, z = 400;
          mCamera->setPosition(-x, y, z);
          cameraRight->setPosition(x, y, z);
          mCamera->lookAt(0, 0, 0);
          cameraRight->lookAt(0, 0, 0);
          mCamera->setFrustumOffset(x + x);
          mCamera->setFocalLength(Ogre::Math::Sqrt(x*x + y*y + z*z));
      setFrustumOffset setFocalLength 为Ogre提供的用于立体渲染辅助方法,可调整视角偏移
      你可以通过设置很远的焦距和很小的fovy制作出看上去很远很大的地球

    3. 全屏四边形,最终的渲染效果
      这里使用Ogre::Rectangle2D:
          mScreen = new Ogre::Rectangle2D(true);
          mScreen->setCorners(-1, 1, 1, -1, true);
          mScreen->setMaterial("stereo/fp");

      材质stereo/fp定义:(这里使用cg脚本以支持direct3d+opengl,同时代码也简短)

      fragment_program fpCG cg
      {
          source stereo.cg
          entry_point RedCyan
          profiles ps_2_0 arbfp1
      }
      
      material stereo/fpCG
      { technique { pass {
                  fragment_program_ref fpCG{}
                  
                  texture_unit
                  {
                      texture textureLeft
                  }
                  texture_unit 
                  {
                      texture textureRight
                  }
      } } }

      材质脚本指定了左右相机渲染的textureLeft、textureRight两幅纹理,并引用RedCyan着色器

      CG脚本,stereo.cg:

      void RedCyan(
          float2 uv : TEXCOORD0,
          out float4 color :COLOR,
          uniform sampler2D t1 : register(s0),
          uniform sampler2D t2 : register(s1))
      {
          color = float4(tex2D(t1, uv) * float4(1, 0, 0, 0) + tex2D(t2, uv) * float4(0, 1, 1, 1));
      }

      简单的取左右纹理对应红+绿蓝分量即可
      注:这里用的乘法后相加,如果直接先取左右纹理颜色,再提取rgb分量的形式,如:color = float4(c1.r, c2.g, c2.b, 1)会导致与direct3d不兼容,and i don't konw why:(


    4. 其它
      因为我们使用全屏四边形,在左右相机渲染纹理的时候需要隐藏,不然有可能将我们的四边形渲染到纹理中
      这里需要实现RenderTargerListener接口,在渲染前后做显隐控制:
          virtual void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
          {
              mScreen->setVisible(false);
          }
          virtual void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
          {
              mScreen->setVisible(true);
          }

      同时在createScene中注册对应的listener:

          targetLeft->addListener(this);
          targetRight->addListener(this);
    5. 最后是锦上添花的一步:让我们的地球转起来
      bool MyApplication::frameRenderingQueued(const Ogre::FrameEvent &evt)
      {
          mEarthNode->yaw(Ogre::Radian(evt.timeSinceLastFrame * 0.5));
          return true;
      }
      附程序代码:

      #pragma once
      
      #include <vector>
      #include <fstream>
      #include <string>
      
      #include <Ogre/Ogre.h>
      #include <OIS/OIS.h>
      
      class MyApplication: public Ogre::RenderTargetListener, public Ogre::FrameListener, public OIS::KeyListener 
      {
      public:
          MyApplication(void){
              mSceneMgr = NULL;
              mRoot = NULL;
          }
      
          ~MyApplication(void){
              mInputManager->destroyInputObject(mKeyboard);
              mInputManager->destroyInputObject(mMouse);
              OIS::InputManager::destroyInputSystem(mInputManager);
              delete mRoot;
          }
      
          int startup();
      
      private:
          void createScene();
      
          virtual void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
          {
              mScreen->setVisible(false);
          }
      
          virtual void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
          {
              mScreen->setVisible(true);
          }
      
          Ogre::MovableObject* createSphere();
      
          void createSphere(const std::string& meshName, const float r, const int nRings = 16, const int nSegments = 16);
      
          bool frameStarted(const Ogre::FrameEvent& evt);
      
          bool frameEnded(const Ogre::FrameEvent& evt);
      
          bool frameRenderingQueued(const Ogre::FrameEvent &evt);
      
          bool keyPressed(const OIS::KeyEvent &e);
      
          bool keyReleased(const OIS::KeyEvent &e) { return true; }
      
          void _createAxis(const int lenth); //创建坐标轴:  x red, y green, z blue
      
          void _loadResources(const char* resoureFile);
      
          void _createInput();
      
          void _showDebugOverlay(bool show);
      
          void _updateStats(void);
      
          void _keyPressedDefault(const OIS::KeyEvent &e);
      
          //默认键盘、鼠标导航
          bool _navigateDefault(const Ogre::FrameEvent& evt);
      
          Ogre::SceneManager* mSceneMgr;
          Ogre::RenderWindow* mWindow;
          Ogre::Camera* mCamera;
          Ogre::Root* mRoot;
          Ogre::SceneNode* mRootNode;        //根节点
      
          OIS::InputManager* mInputManager;
          OIS::Keyboard* mKeyboard;
          OIS::Mouse* mMouse;
      
          Ogre::SceneNode* mEarthNode;
          Ogre::Rectangle2D* mScreen;
      
          int mNumScreenShots;    //截图顺序号
      
          bool mStatsOn;
          Ogre::Overlay* mDebugOverlay;
      };
      MyApplication.h
      //易变更部分
      #include "MyApplication.h"
      
      void MyApplication::createScene()
      {
          mEarthNode = mRootNode->createChildSceneNode();
          mEarthNode->attachObject(createSphere());
      
          //左眼纹理
          Ogre::TexturePtr textureLeft = Ogre::TextureManager::getSingleton().createManual("textureLeft", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWindow->getWidth(), mWindow->getHeight(), 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
          Ogre::RenderTexture *targetLeft = textureLeft->getBuffer()->getRenderTarget();
          targetLeft->addViewport(mCamera);
      
          //右眼纹理
          Ogre::Camera *cameraRight = mSceneMgr->createCamera("cameraRight");
          cameraRight->setAspectRatio(Ogre::Real(mWindow->getWidth()) / Ogre::Real(mWindow->getHeight()));
          Ogre::TexturePtr textureRight = Ogre::TextureManager::getSingleton().createManual("textureRight", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWindow->getWidth(), mWindow->getHeight(), 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
          Ogre::RenderTexture *targetRight = textureRight->getBuffer()->getRenderTarget();
          targetRight->addViewport(cameraRight);
      
          //设置相机位置、焦距
          const int x = 10, y = 150, z = 400;
          mCamera->setPosition(-x, y, z);
          cameraRight->setPosition(x, y, z);
          mCamera->lookAt(0, 0, 0);
          cameraRight->lookAt(0, 0, 0);
          mCamera->setFrustumOffset(x + x);
          mCamera->setFocalLength(Ogre::Math::Sqrt(x*x + y*y + z*z));
      
          mScreen = new Ogre::Rectangle2D(true);
          mScreen->setCorners(-1, 1, 1, -1, true);
          mScreen->setMaterial("stereo/fpCG");
          mRootNode->attachObject(mScreen);
      
          targetLeft->addListener(this);
          targetRight->addListener(this);
      }
      
      bool MyApplication::keyPressed(const OIS::KeyEvent &e)
      {
          _keyPressedDefault(e);
      
      
          return true;
      }
      
      bool MyApplication::frameStarted(const Ogre::FrameEvent& evt)
      {
          //if(!_navigateDefault(evt)) return false;
          mKeyboard->capture();
          if(mKeyboard->isKeyDown(OIS::KC_ESCAPE)){
              return false;
          }
      
          return true;
      }
      
      bool MyApplication::frameEnded(const Ogre::FrameEvent& evt){
          _updateStats();
      
          return true;
      }
      
      bool MyApplication::frameRenderingQueued(const Ogre::FrameEvent &evt)
      {
          mEarthNode->yaw(Ogre::Radian(evt.timeSinceLastFrame * 0.5));
          return true;
      }
      
      Ogre::MovableObject* MyApplication::createSphere(){
          createSphere("mySphereMesh", 100, 100, 100);
          Ogre::Entity* sphereEntity = mSceneMgr->createEntity ("mySphereEntity", "mySphereMesh");
          sphereEntity->setMaterialName("Test/earth");
      
          return sphereEntity;
      }
      
      //根据mesh名称、半径、经纬线条数创建对应的mesh
      void MyApplication::createSphere(const std::string& meshName, const float r, const int nRings, const int nSegments)
      {
          Ogre::MeshPtr pSphere = Ogre::MeshManager::getSingleton().createManual(meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
          Ogre::SubMesh *pSphereVertex = pSphere->createSubMesh();
      
          Ogre::VertexData* vertexData = new Ogre::VertexData();
          pSphere->sharedVertexData = vertexData;
      
          // define the vertex format
          Ogre::VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
          size_t currOffset = 0;
          // positions
          vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
          currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
          //// DIFFUSE
          //vertexDecl->addElement(0, currOffset, VET_FLOAT3, Ogre::VES_DIFFUSE);
          //currOffset += VertexElement::getTypeSize(VET_FLOAT3);
          // normals
          vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
          currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
          //// two dimensional texture coordinates
          vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
          currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
      
          // allocate the vertex buffer
          vertexData->vertexCount = (nRings + 1) * (nSegments+1);
          Ogre::HardwareVertexBufferSharedPtr vBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(vertexDecl->getVertexSize(0), vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
          Ogre::VertexBufferBinding* binding = vertexData->vertexBufferBinding;
          binding->setBinding(0, vBuf);
          float* pVertex = static_cast<float*>(vBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
      
          // allocate index buffer
          pSphereVertex->indexData->indexCount = 6 * nRings * (nSegments + 1);
          pSphereVertex->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, pSphereVertex->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
          Ogre::HardwareIndexBufferSharedPtr iBuf = pSphereVertex->indexData->indexBuffer;
          unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
      
          float fDeltaRingAngle = float(Ogre::Math::PI / nRings);
          float fDeltaSegAngle = float(2 * Ogre::Math::PI / nSegments);
          unsigned short wVerticeIndex = 0 ;
      
          // Generate the group of rings for the sphere
          for( int ring = 0; ring <= nRings; ring++ ) {
              float r0 = r * sinf (ring * fDeltaRingAngle);
              float y0 = r * cosf (ring * fDeltaRingAngle);
      
              // Generate the group of segments for the current ring
              for(int seg = 0; seg <= nSegments; seg++) {
                  float x0 = r0 * sinf(seg * fDeltaSegAngle);
                  float z0 = r0 * cosf(seg * fDeltaSegAngle);
      
                  // Add one vertex to the strip which makes up the sphere
                  *pVertex++ = x0;
                  *pVertex++ = y0;
                  *pVertex++ = z0;
      
                  Ogre::Vector3 vNormal = Ogre::Vector3(x0, y0, z0).normalisedCopy();
                  *pVertex++ = vNormal.x;
                  *pVertex++ = vNormal.y;
                  *pVertex++ = vNormal.z;
      
                  *pVertex++ = (float) seg / (float) nSegments;
                  *pVertex++ = (float) ring / (float) nRings;
      
                  if (ring != nRings) {
                      // each vertex (except the last) has six indices pointing to it
                      *pIndices++ = wVerticeIndex + nSegments + 1;
                      *pIndices++ = wVerticeIndex;               
                      *pIndices++ = wVerticeIndex + nSegments;
                      *pIndices++ = wVerticeIndex + nSegments + 1;
                      *pIndices++ = wVerticeIndex + 1;
                      *pIndices++ = wVerticeIndex;
                      wVerticeIndex ++;
                  }
              }; // end for seg
          } // end for ring
      
          // Unlock
          vBuf->unlock();
          iBuf->unlock();
          // Generate face list
          pSphereVertex->useSharedVertices = true;
      
          // the original code was missing this line:
          pSphere->_setBounds( Ogre::AxisAlignedBox(Ogre::Vector3(-r, -r, -r), Ogre::Vector3(r, r, r) ), false );
          pSphere->_setBoundingSphereRadius(r);
          // this line makes clear the mesh is loaded (avoids memory leaks)
          pSphere->load();
      }
      MyApplication.cpp
      //系统不常变更部分实现
      #include "MyApplication.h"
      #include "windows.h"
      
      int main(int argc, char *argv[])
      {
          //设置当前工作目录,用于文件关联打开方式
          std::string file(argv[0]);
          SetCurrentDirectoryA(file.substr(0, file.find_last_of("\\")).c_str());
      
          MyApplication app;
          app.startup();
      }
      
      int MyApplication::startup()
      {
      
      #ifdef _DEBUG
          mRoot = new Ogre::Root("../plugins_d.cfg", "../ogre.cfg", "../Ogre.log");
      #else
          mRoot = new Ogre::Root("../plugins.cfg", "../ogre.cfg", "../Ogre.log");
      #endif
      
          if(!mRoot->showConfigDialog()){
              //if(!mRoot->showConfigDialog()){
              return -1;
          }
      
          mWindow = mRoot->initialise(true, "Ogre3D");
          mSceneMgr = mRoot->createSceneManager(Ogre::ST_EXTERIOR_CLOSE);
      
          mCamera = mSceneMgr->createCamera("camera");
          mCamera->setPosition(Ogre::Vector3(100, 200, 300));
          mCamera->lookAt(Ogre::Vector3(0, 0, 0));
          mCamera->setNearClipDistance(10); //default [100, 100 * 1000]
      
          Ogre::Viewport* viewport = mWindow->addViewport(mCamera);
          viewport->setBackgroundColour(Ogre::ColourValue(0.0, 0.0, 0.0));
          mCamera->setAspectRatio(Ogre::Real(viewport->getActualWidth())/Ogre::Real(viewport->getActualHeight()));
      
          mRootNode = mSceneMgr->getRootSceneNode();
      
          _loadResources("../resources_testStereo.cfg");
      
          createScene();
          _createAxis(100);
          _createInput();
      
          mDebugOverlay = Ogre::OverlayManager::getSingleton().getByName("Core/DebugOverlay");
          _showDebugOverlay(true);
      
          mRoot->addFrameListener(this);
      
          mRoot->startRendering();
          return 0;
      }
      
      void MyApplication::_createAxis(const int lenth)
      {
          Ogre::ManualObject *mo = mSceneMgr->createManualObject();
          mo->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
          mo->position(lenth, 0, 0);
          mo->colour(1.0, 0, 0);
          mo->position(0, 0, 0);
          mo->colour(1.0, 0, 0);
          mo->position(0, lenth, 0);
          mo->colour(0, 1.0, 0);
          mo->position(0, 0, 0);
          mo->colour(0, 1.0, 0);
          mo->position(0, 0, lenth);
          mo->colour(0, 0, 1.0);
          mo->position(0 , 0, 0);
          mo->colour(0, 0, 1.0);
          mo->end();
          mRootNode->attachObject(mo);
      }
      
      void MyApplication::_loadResources(const char* resourceFile)
      {
          Ogre::ConfigFile cf;
          cf.load(resourceFile);
      
          Ogre::ConfigFile::SectionIterator sectionIter = cf.getSectionIterator();
          Ogre::String sectionName, typeName, dataName;
          while(sectionIter.hasMoreElements()){
              sectionName = sectionIter.peekNextKey();
              Ogre::ConfigFile::SettingsMultiMap *settings = sectionIter.getNext();
              Ogre::ConfigFile::SettingsMultiMap::iterator i;
              for(i=settings->begin(); i!=settings->end(); i++){
                  typeName =i->first;
                  dataName = i->second;
      
                  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(dataName, typeName, sectionName);
              }
          }
      
          Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
      }
      
      void MyApplication::_updateStats(void)
      {
          static Ogre::String currFps = "Current FPS: ";
          static Ogre::String avgFps = "Average FPS: ";
          static Ogre::String bestFps = "Best FPS: ";
          static Ogre::String worstFps = "Worst FPS: ";
          static Ogre::String tris = "Triangle Count: ";
          static Ogre::String batches = "Batch Count: ";
      
          // update stats when necessary
          try {
              Ogre::OverlayElement* guiAvg = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
              Ogre::OverlayElement* guiCurr = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
              Ogre::OverlayElement* guiBest = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
              Ogre::OverlayElement* guiWorst = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");
      
              const Ogre::RenderTarget::FrameStats& stats = mWindow->getStatistics();
              guiAvg->setCaption(avgFps + Ogre::StringConverter::toString(stats.avgFPS));
              guiCurr->setCaption(currFps + Ogre::StringConverter::toString(stats.lastFPS));
              guiBest->setCaption(bestFps + Ogre::StringConverter::toString(stats.bestFPS)
                  +" "+Ogre::StringConverter::toString(stats.bestFrameTime)+" ms");
              guiWorst->setCaption(worstFps + Ogre::StringConverter::toString(stats.worstFPS)
                  +" "+Ogre::StringConverter::toString(stats.worstFrameTime)+" ms");
      
              Ogre::OverlayElement* guiTris = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
              guiTris->setCaption(tris + Ogre::StringConverter::toString(std::max((int)stats.triangleCount, 230) - 230));
      
              Ogre::OverlayElement* guiBatches = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/NumBatches");
              guiBatches->setCaption(batches + Ogre::StringConverter::toString((int)stats.batchCount - 10));
      
              //Ogre::OverlayElement* guiDbg = Ogre::OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
              //guiDbg->setCaption("mDebugText");
          }
          catch(...) { /* ignore */ }
      }
      
      
      void MyApplication::_showDebugOverlay(bool show)
      {
          if (mDebugOverlay)
          {
              if (show)
                  mDebugOverlay->show();
              else
                  mDebugOverlay->hide();
          }
      }
      
      void MyApplication::_createInput()
      {
          OIS::ParamList parameters;
          unsigned int windowHandle = 0;
          std::ostringstream windowHandleString;
      
          mWindow->getCustomAttribute("WINDOW", &windowHandle);
          windowHandleString<<windowHandle;
          parameters.insert(std::make_pair("WINDOW", windowHandleString.str()));
      
          mInputManager = OIS::InputManager::createInputSystem(parameters);
          mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, true));
          mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, true));
          mKeyboard->setEventCallback(this);
      }
      
      
      void MyApplication::_keyPressedDefault(const OIS::KeyEvent &e)
      {
          if(e.key == OIS::KC_SYSRQ)
          {
              std::ostringstream ss;
              ss << "screenshot_" << ++mNumScreenShots << ".png";
              mWindow->writeContentsToFile(ss.str());
          }
          else if(e.key == OIS::KC_G)
          {
              mStatsOn = !mStatsOn;
              _showDebugOverlay(mStatsOn);
          }
          else if(e.key == OIS::KC_R)
          {
              if(mCamera->getPolygonMode() == Ogre::PM_SOLID)
              {
                  mCamera->setPolygonMode(Ogre::PM_WIREFRAME);
              }
              else
              {
                  mCamera->setPolygonMode(Ogre::PM_SOLID);
              }
          }
      }
      
      //默认键盘、鼠标导航
      bool MyApplication::_navigateDefault(const Ogre::FrameEvent& evt)
      {
          mKeyboard->capture();
          if(mKeyboard->isKeyDown(OIS::KC_ESCAPE)){
              return false;
          }
      
          Ogre::Vector3 translate(0, 0, 0);
          if(mKeyboard->isKeyDown(OIS::KC_W)){
              translate +=Ogre::Vector3(0, 0, -1);
          }
          if(mKeyboard->isKeyDown(OIS::KC_S)){
              translate += Ogre::Vector3(0, 0, 1);
          }
          if(mKeyboard->isKeyDown(OIS::KC_A)){
              translate += Ogre::Vector3(-1, 0, 0);
          }
          if(mKeyboard->isKeyDown(OIS::KC_D)){
              translate += Ogre::Vector3(1, 0, 0);
          }
          if(mKeyboard->isKeyDown(OIS::KC_Q)){
              translate += mCamera->getOrientation().Inverse() * Ogre::Vector3(0, 1, 0);
          }
          if(mKeyboard->isKeyDown(OIS::KC_E)){
              translate += mCamera->getOrientation().Inverse() *  Ogre::Vector3(0, -1, 0);
          }
      
          Ogre::Real speed = mCamera->getPosition().y;
          if(speed < 5) speed =5;
          mCamera->moveRelative(translate * evt.timeSinceLastFrame *  speed);
      
      
          if(mKeyboard->isKeyDown(OIS::KC_UP)){
              mCamera->pitch(Ogre::Radian(-evt.timeSinceLastFrame));
          }else if(mKeyboard->isKeyDown(OIS::KC_DOWN)){
              mCamera->pitch(Ogre::Radian(evt.timeSinceLastFrame));
          }
          if(mKeyboard->isKeyDown(OIS::KC_LEFT)){
              mCamera->yaw(Ogre::Radian(evt.timeSinceLastFrame));
          }
          if(mKeyboard->isKeyDown(OIS::KC_RIGHT)){
              mCamera->yaw(Ogre::Radian(-evt.timeSinceLastFrame * 0.3f));
          }
      
          mMouse->capture();
          Ogre::Real rotX = Ogre::Math::Clamp(mMouse->getMouseState().X.rel * evt.timeSinceLastFrame * -1, -0.1f, 0.1f);
          Ogre::Real rotY = Ogre::Math::Clamp(mMouse->getMouseState().Y.rel * evt.timeSinceLastFrame * -1, -0.1f, 0.1f);
      
          mCamera->yaw(Ogre::Radian(rotX));
          mCamera->pitch(Ogre::Radian(rotY));
      
          return true;
      }
      MyApplicationConst.cpp

      附:代码使用Test/earth原始材质,如果在nasa下载个高清的图片叫earth.jpg,需要指定对应的材质:

    material Test/earth
    { technique { pass {
              texture_unit
              {
                texture earth.jpg
              }
    } } }

      

  • 相关阅读:
    aidl 详解
    为什么我选择用 flutter
    hunting
    Dynamic programming
    android 微信开发交流群
    hash function 字符串哈希函数
    函数也可以看成是空间的变换
    语法树
    生活中的物理随处可见
    about coroutine
  • 原文地址:https://www.cnblogs.com/wiki3d/p/4086938.html
Copyright © 2020-2023  润新知