这节课NeHe课程教我们怎样使用OpenGL中的多视口来显示场景,多视口的方式可以用来显示场景中更多的信息。在3DMax中我们经常会看到模型的正视图、侧视图等可以在OpenGL中使用不同的视口达到同样的效果。在OSG中每一个osg::Camera可以用来渲染不同的场景,我们可以使用多个相机并通过给这些相机设置不同的视口来达到这样的效果。在OSG中提供了这样的类来管理多个View视景器,不同的视景器拥有不同的Camera和场景根节点。
首先需要修改的是我们框架中的osgViewer::Viewer类的继承,我们将它修改成CompositeView,用它来管理多个视景器:
class ViewerWidget : public QWidget, public osgViewer::CompositeViewer
//添加多个相机 // view one { osgViewer::View* view = new osgViewer::View; view->setName("View one"); this->addView(view); view->setSceneData(scene1); view->getCamera()->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); view->getCamera()->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); view->getCamera()->setName("Cam one"); view->getCamera()->setViewport(new osg::Viewport(0,0, traits->width/2, traits->height/2)); view->getCamera()->setGraphicsContext(gw); view->addEventHandler(new ManipulatorSceneHandler); }
接下来我们创建每一个View加载的场景根节点:
osg::Node* buildScene1() { osg::Group *scene1= new osg::Group; osg::MatrixTransform *zoomMT1 = new osg::MatrixTransform; zoomMT1->setMatrix(osg::Matrix::translate(0,0,-7)); osg::MatrixTransform *rotX1 = new osg::MatrixTransform; rotX1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::X_AXIS, 0.5)); osg::MatrixTransform *rotY1 = new osg::MatrixTransform; rotY1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Y_AXIS, 0.3)); osg::MatrixTransform *rotZ1 = new osg::MatrixTransform; rotZ1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 0.2)); scene1->addChild(zoomMT1); zoomMT1->addChild(rotX1); rotX1->addChild(rotY1); rotY1->addChild(rotZ1); rotZ1->addChild(createCylinder()); return scene1; }最后在场景的帧循环事件中我们需要更新几何体的纹理,方式参考NeHe中的实现,在几何体表面绘制迷宫图案。
case (osgGA::GUIEventAdapter::FRAME): { done=true; // Set done To True for (int x=0; x<width; x+=2) // Loop Through All The Rooms { for (int y=0; y<height; y+=2) // On X And Y Axis { if (tex_data[((x+(width*y))*3)]==0) // If Current Texture Pixel (Room) Is Blank done=false; // We Have To Set done To False (Not Finished Yet) } } if (done) // If done Is True Then There Were No Unvisited Rooms { Sleep(5000); Reset(); } ....... }
附:本课源码(源码中可能存在着错误和不足之处,仅供参考)
#include "../osgNeHe.h" #include <QtCore/QTimer> #include <QtGui/QApplication> #include <QtGui/QVBoxLayout> #include <osgViewer/Viewer> #include <osgViewer/CompositeViewer> #include <osgDB/ReadFile> #include <osgQt/GraphicsWindowQt> #include <osg/MatrixTransform> #include <osg/Texture2D> #include <osg/ShapeDrawable> #include <osg/AnimationPath> ////////////////////////////////////////////////////////////////////////// // User Defined Variables int mx,my; const int width = 128; const int height = 128; BOOL done; BOOL sp; BYTE r[4], g[4], b[4]; BYTE *tex_data; osg::Texture2D *g_Texture2D; GLfloat xrot, yrot, zrot; void UpdateTex(int dmx, int dmy) { tex_data[0+((dmx+(width*dmy))*3)]=255; tex_data[1+((dmx+(width*dmy))*3)]=255; tex_data[2+((dmx+(width*dmy))*3)]=255; } void Reset (void) { ZeroMemory(tex_data, width * height *3); srand(GetTickCount()); for (int loop=0; loop<4; loop++) { r[loop]=rand()%128+128; g[loop]=rand()%128+128; b[loop]=rand()%128+128; } mx=int(rand()%(width/2))*2; my=int(rand()%(height/2))*2; } bool Initialize() { tex_data=new BYTE[width*height*3]; Reset(); osg::Image *image = new osg::Image; image->setImage(width, height, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tex_data, osg::Image::NO_DELETE); g_Texture2D = new osg::Texture2D; g_Texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); g_Texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); g_Texture2D->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); g_Texture2D->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); return true; } class DrawableTextureUpdateCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor*, osg::Drawable* d) { if (!d) return; osg::Image *image = new osg::Image; image->setImage(width, height, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tex_data, osg::Image::NO_DELETE); g_Texture2D->setImage(image); d->getOrCreateStateSet()->setTextureAttributeAndModes(0, g_Texture2D); } }; ////////////////////////////////////////////////////////////////////////// osg::Geode* createFirstQuad() { osg::Geode *geode = new osg::Geode; osg::Geometry *geometry = new osg::Geometry; osg::Vec2Array *texArray = new osg::Vec2Array; osg::Vec3Array *vertexArray = new osg::Vec3Array; osg::Vec3Array *colorArray = new osg::Vec3Array; colorArray->push_back(osg::Vec3(1, 1, 1.0)); vertexArray->push_back(osg::Vec3(320, 0, 0.0f)); vertexArray->push_back(osg::Vec3(0, 0, 0.0f)); vertexArray->push_back(osg::Vec3(0, 240, 0.0f)); vertexArray->push_back(osg::Vec3(320, 240, 0.0f)); texArray->push_back(osg::Vec2(1.0f, 0.0f)); texArray->push_back(osg::Vec2(0.0f, 0.0f)); texArray->push_back(osg::Vec2(0.0f, 1.0f)); texArray->push_back(osg::Vec2(1.0f, 1.0f)); geometry->setVertexArray(vertexArray); geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL); geometry->setTexCoordArray(0, texArray, osg::Array::BIND_PER_VERTEX); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertexArray->size())); geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, g_Texture2D); geometry->setUpdateCallback(new DrawableTextureUpdateCallback); geode->addDrawable(geometry); return geode; } osg::Geode* createThirdQuad() { osg::Geode *geode = new osg::Geode; osg::Geometry *geometry = new osg::Geometry; osg::Vec2Array *texArray = new osg::Vec2Array; osg::Vec3Array *vertexArray = new osg::Vec3Array; osg::Vec3Array *colorArray = new osg::Vec3Array; colorArray->push_back(osg::Vec3(1, 1, 1.0)); vertexArray->push_back(osg::Vec3(1.0f, 1.0f, 0.0f)); vertexArray->push_back(osg::Vec3(-1.0f, 1.0f, 0.0f)); vertexArray->push_back(osg::Vec3(-1.0f, -1.0f, 0.0f)); vertexArray->push_back(osg::Vec3(1.0f, -1.0f, 0.0f)); texArray->push_back(osg::Vec2(1.0f, 1.0f)); texArray->push_back(osg::Vec2(0.0f, 1.0f)); texArray->push_back(osg::Vec2(0.0f, 0.0f)); texArray->push_back(osg::Vec2(1.0f, 0.0f)); geometry->setVertexArray(vertexArray); geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL); geometry->setTexCoordArray(0, texArray, osg::Array::BIND_PER_VERTEX); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertexArray->size())); geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, g_Texture2D); geometry->setUpdateCallback(new DrawableTextureUpdateCallback); geode->addDrawable(geometry); return geode; } osg::Geode* createSphere() { osg::Geode *sphere = new osg::Geode; osg::ShapeDrawable *sphereDrawable = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0,0,0), 4.0)); sphere->addDrawable(sphereDrawable); sphereDrawable->getOrCreateStateSet()->setTextureAttributeAndModes(0, g_Texture2D); sphereDrawable->setUpdateCallback(new DrawableTextureUpdateCallback); return sphere; } osg::Geode* createCylinder() { osg::Geode *cylinder = new osg::Geode; osg::ShapeDrawable *cylinderDrawable = new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(0,0,0), 1.5, 4.0)); cylinder->addDrawable(cylinderDrawable); cylinderDrawable->getOrCreateStateSet()->setTextureAttributeAndModes(0, g_Texture2D); cylinderDrawable->setUpdateCallback(new DrawableTextureUpdateCallback); return cylinder; } class ManipulatorSceneHandler : public osgGA::GUIEventHandler { public: ManipulatorSceneHandler() { } public: virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { osgViewer::View *viewer = dynamic_cast<osgViewer::View*>(&aa); if (!viewer) return false; if (!viewer->getSceneData()) return false; if (ea.getHandled()) return false; osg::Group *root = viewer->getSceneData()->asGroup(); switch(ea.getEventType()) { case (osgGA::GUIEventAdapter::FRAME): { done=TRUE; // Set done To True for (int x=0; x<width; x+=2) // Loop Through All The Rooms { for (int y=0; y<height; y+=2) // On X And Y Axis { if (tex_data[((x+(width*y))*3)]==0) // If Current Texture Pixel (Room) Is Blank done=FALSE; // We Have To Set done To False (Not Finished Yet) } } if (done) // If done Is True Then There Were No Unvisited Rooms { Sleep(5000); Reset(); } // Check To Make Sure We Are Not Trapped (Nowhere Else To Move) if (((mx>(width-4) || tex_data[(((mx+2)+(width*my))*3)]==255)) && ((mx<2 || tex_data[(((mx-2)+(width*my))*3)]==255)) && ((my>(height-4) || tex_data[((mx+(width*(my+2)))*3)]==255)) && ((my<2 || tex_data[((mx+(width*(my-2)))*3)]==255))) { do // If We Are Trapped { mx=int(rand()%(width/2))*2; // Pick A New Random X Position my=int(rand()%(height/2))*2; // Pick A New Random Y Position } while (tex_data[((mx+(width*my))*3)]==0); // Keep Picking A Random Position Until We Find } // One That Has Already Been Tagged (Safe Starting Point) int dir=int(rand()%4); // Pick A Random Direction if ((dir==0) && (mx<=(width-4))) // If The Direction Is 0 (Right) And We Are Not At The Far Right { if (tex_data[(((mx+2)+(width*my))*3)]==0) // And If The Room To The Right Has Not Already Been Visited { UpdateTex(mx+1,my); // Update The Texture To Show Path Cut Out Between Rooms mx+=2; // Move To The Right (Room To The Right) } } if ((dir==1) && (my<=(height-4))) // If The Direction Is 1 (Down) And We Are Not At The Bottom { if (tex_data[((mx+(width*(my+2)))*3)]==0) // And If The Room Below Has Not Already Been Visited { UpdateTex(mx,my+1); // Update The Texture To Show Path Cut Out Between Rooms my+=2; // Move Down (Room Below) } } if ((dir==2) && (mx>=2)) // If The Direction Is 2 (Left) And We Are Not At The Far Left { if (tex_data[(((mx-2)+(width*my))*3)]==0) // And If The Room To The Left Has Not Already Been Visited { UpdateTex(mx-1,my); // Update The Texture To Show Path Cut Out Between Rooms mx-=2; // Move To The Left (Room To The Left) } } if ((dir==3) && (my>=2)) // If The Direction Is 3 (Up) And We Are Not At The Top { if (tex_data[((mx+(width*(my-2)))*3)]==0) // And If The Room Above Has Not Already Been Visited { UpdateTex(mx,my-1); // Update The Texture To Show Path Cut Out Between Rooms my-=2; // Move Up (Room Above) } } UpdateTex(mx,my); // Update Current Room } break; case(osgGA::GUIEventAdapter::KEYDOWN): { if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Space) { Reset(); } } break; default: break; } return false; } }; class ViewerWidget : public QWidget, public osgViewer::CompositeViewer { public: ViewerWidget(osg::Node *scene1 = NULL, osg::Node *scene2 = NULL, osg::Node *scene3 = NULL, osg::Node *scene4 = NULL) { QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,640,480), scene1, scene2, scene3, scene4); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(renderWidget); layout->setContentsMargins(0, 0, 0, 1); setLayout( layout ); connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) ); _timer.start( 10 ); } QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene1=0, osg::Node* scene2=0, osg::Node* scene3=0, osg::Node* scene4=0) { const osg::GraphicsContext::Traits* traits = gw->getTraits(); ////////////////////////////////////////////////////////////////////////// //添加多个相机 // view one { osgViewer::View* view = new osgViewer::View; view->setName("View one"); this->addView(view); view->setSceneData(scene1); view->getCamera()->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); view->getCamera()->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); view->getCamera()->setName("Cam one"); view->getCamera()->setViewport(new osg::Viewport(0,0, traits->width/2, traits->height/2)); view->getCamera()->setGraphicsContext(gw); view->addEventHandler(new ManipulatorSceneHandler); } // view two { osgViewer::View* view = new osgViewer::View; view->setName("View two"); this->addView(view); view->setSceneData(scene2); view->getCamera()->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); view->getCamera()->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); view->getCamera()->setName("Cam two"); view->getCamera()->setViewport(new osg::Viewport(traits->width/2,0, traits->width/2, traits->height/2)); view->getCamera()->setGraphicsContext(gw); } // view three { osgViewer::View* view = new osgViewer::View; view->setName("View three"); this->addView(view); view->setSceneData(scene3); view->getCamera()->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); //view->getCamera()->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); view->getCamera()->setProjectionMatrixAsOrtho(0, traits->width/2.0, 0, traits->height/2.0, -1,1); view->getCamera()->setName("Cam three"); view->getCamera()->setViewport(new osg::Viewport(0, traits->height/2, traits->width / 2, traits->height/2)); view->getCamera()->setGraphicsContext(gw); } // view four { osgViewer::View* view = new osgViewer::View; view->setName("View four"); this->addView(view); view->setSceneData(scene4); view->getCamera()->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); view->getCamera()->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); view->getCamera()->setName("Cam four"); view->getCamera()->setViewport(new osg::Viewport(traits->width/2, traits->height/2, traits->width / 2, traits->height/2)); view->getCamera()->setGraphicsContext(gw); } return gw->getGLWidget(); } osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false ) { osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = name; traits->windowDecoration = windowDecoration; traits->x = x; traits->y = y; traits->width = w; traits->height = h; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } virtual void paintEvent( QPaintEvent* event ) { frame(); } protected: QTimer _timer; }; osg::Node* buildScene1() { osg::Group *scene1= new osg::Group; osg::MatrixTransform *zoomMT1 = new osg::MatrixTransform; zoomMT1->setMatrix(osg::Matrix::translate(0,0,-7)); osg::MatrixTransform *rotX1 = new osg::MatrixTransform; rotX1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::X_AXIS, 0.5)); osg::MatrixTransform *rotY1 = new osg::MatrixTransform; rotY1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Y_AXIS, 0.3)); osg::MatrixTransform *rotZ1 = new osg::MatrixTransform; rotZ1->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 0.2)); scene1->addChild(zoomMT1); zoomMT1->addChild(rotX1); rotX1->addChild(rotY1); rotY1->addChild(rotZ1); rotZ1->addChild(createCylinder()); return scene1; } osg::Node* buildScene2() { osg::Group *scene2 = new osg::Group; osg::MatrixTransform *zoomMT = new osg::MatrixTransform; zoomMT->setMatrix(osg::Matrix::translate(0,0,-2)); osg::MatrixTransform *rotX = new osg::MatrixTransform; rotX->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-45.0), osg::X_AXIS)); osg::MatrixTransform *rotZ = new osg::MatrixTransform; rotZ->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 0.5)); scene2->addChild(zoomMT); zoomMT->addChild(rotX); rotX->addChild(rotZ); rotZ->addChild(createThirdQuad()); return scene2; } osg::Node* buildScene3() { return createFirstQuad(); } osg::Node* buildScene4() { osg::Group *scene= new osg::Group; osg::MatrixTransform *zoomMT = new osg::MatrixTransform; zoomMT->setMatrix(osg::Matrix::translate(0,0,-14)); osg::MatrixTransform *rotX = new osg::MatrixTransform; rotX->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::X_AXIS, 0.5)); osg::MatrixTransform *rotY = new osg::MatrixTransform; rotY->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Y_AXIS, 0.2)); osg::MatrixTransform *rotZ = new osg::MatrixTransform; rotZ->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 0.4)); scene->addChild(zoomMT); zoomMT->addChild(rotX); rotX->addChild(rotY); rotY->addChild(rotZ); rotZ->addChild(createSphere()); return scene; } int main( int argc, char** argv ) { QApplication app(argc, argv); Initialize(); ViewerWidget* viewWidget = new ViewerWidget(buildScene1(), buildScene2(),buildScene3(),buildScene4()); viewWidget->setGeometry( 100, 100, 640, 480 ); viewWidget->show(); return app.exec(); }