-
简介
这节课NeHe课程教我们怎样使用OpenGL中的多视口来显示场景,多视口的方式可以用来显示场景中更多的信息。在3DMax中我们经常会看到模型的正视图、侧视图等可以在OpenGL中使用不同的视口达到同样的效果。在OSG中每一个osg::Camera可以用来渲染不同的场景,我们可以使用多个相机并通过给这些相机设置不同的视口来达到这样的效果。在OSG中提供了这样的类来管理多个View视景器,不同的视景器拥有不同的Camera和场景根节点。
-
实现
首先需要修改的是我们框架中的osgViewer::Viewer类的继承,我们将它修改成CompositeView,用它来管理多个视景器:
class ViewerWidget : public QWidget, public osgViewer::CompositeViewer
我们在创建窗口的过程中创建多个View:
//添加多个相机
// 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);
}
按课程中的方式添加了4个View用来显示不同的场景,这些View的参数可以不同,我们把其中一个按NeHe课程中的方式设置为正投影的方式(Ortho)
接下来我们创建每一个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();
}
.......
}
最后把不同的场景添加到各自的视景器View之中,运行程序:
附:本课源码(源码中可能存在着错误和不足之处,仅供参考)
#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();
}