瞭解紅藍眼鏡原理以後剩下的事情就簡單了php
若是不清楚紅藍眼鏡原理,請先看上一篇:製做立體圖像(一):紅藍眼鏡原理html
另外你應該已經準備好了一副紅藍眼鏡(若是沒有請點擊這裏,而後關閉本頁面:)windows
如今戴上眼鏡,先看看咱們要作到的最終效果,一個旋轉的立體地球:app
(固然這個是靜止截圖)ide
先說說實現原理:函數
如下是詳細說明:post
//根據mesh名稱、半徑、經緯線條數建立對應的mesh void MyApplication::createSphere(const std::string& meshName, const float r, const int nRings, const int nSegments)
//左眼紋理 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"); ...同上... ;
設置相機ui
//設置相機位置、焦距 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/fp");
材質stereo/fp定義:(這裏使用cg腳本以支持direct3d+opengl,同時代碼也簡短)this
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:spa
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:(
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);
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; };
//易變動部分 #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(); }
//系統不常變動部分實現 #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; }
附:代碼使用Test/earth原始材質,若是在nasa下載個高清的圖片叫earth.jpg,須要指定對應的材質:
material Test/earth{ technique { pass { texture_unit { texture earth.jpg }} } }