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

时间:2024-03-13 21:25:39

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

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

 

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

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

(当然这个是静止截图)

 

先说说实现原理:

  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
          }
} } }