本课实现在场景中绘制旋转的星星,主要的内容包括使用Blend方式设置纹理图片的颜色,在场景中操作物体实现简单的动画效果。
本课中描述的内容在前面的课程中已经有比较详细的介绍,本课综合运用了前面课程所学内容。
首先同NeHe教程中设置的一样,我们先创建50颗星星,代码如下
osg::Group *starGroup = new osg::Group; for (unsigned i = 0; i < StarNumber; ++i) { float dist=(float(i)/StarNumber)*5.0f; float angle= float(i)/(StarNumber * 30); osg::Group *star = new osg::Group; //设置变换节点抵消对整个场景的倾斜 osg::MatrixTransform *antiTileMT = new osg::MatrixTransform; antiTileMT->setUpdateCallback(new AntiTiltCallback); //设置变换节点抵消每颗星星的旋转 osg::MatrixTransform *antiDirectMT = new osg::MatrixTransform; antiDirectMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, -angle)); osg::MatrixTransform *directionMT = new osg::MatrixTransform; directionMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, angle)); osg::MatrixTransform *moveMT = new osg::MatrixTransform; moveMT->setMatrix(osg::Matrix::translate(dist, 0, 0)); moveMT->setUpdateCallback(new TranslateCallback); osg::MatrixTransform *spinMT = new osg::MatrixTransform; spinMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, 0.02)); star->addChild(directionMT); directionMT->addChild(moveMT); //星星叶节点 osg::Geode *starQuad = new osg::Geode; osg::Geometry *starGeometry = new osg::Geometry; osg::Texture2D *texture = new osg::Texture2D; texture->setImage(osgDB::readImageFile("Data/Star.bmp")); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); osg::Vec3Array *coords = new osg::Vec3Array; coords->push_back(osg::Vec3(-1.0f,-1.0f, 0.0f)); coords->push_back(osg::Vec3(1.0f,-1.0f, 0.0f)); coords->push_back(osg::Vec3(1.0f,1.0f, 0.0f)); coords->push_back(osg::Vec3(-1.0f,1.0f, 0.0f)); starGeometry->setVertexArray(coords); osg::Vec4Array *colors = new osg::Vec4Array; colors->push_back(osg::Vec4(rand() % 256 / 255.0f, rand() % 256 / 255.0f, rand() % 256 / 255.0f, 1.0)); starGeometry->setColorArray(colors); starGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); starGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); osg::Vec2Array *textureArray = new osg::Vec2Array; textureArray->push_back(osg::Vec2(0.0f, 0.0f)); textureArray->push_back(osg::Vec2(1.0f, 0.0f)); textureArray->push_back(osg::Vec2(1.0f, 1.0f)); textureArray->push_back(osg::Vec2(0.0f, 1.0f)); starGeometry->setTexCoordArray(0, textureArray); starGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture); starGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); starGeometry->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE, 0.5, 0.5); starGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc,osg::StateAttribute::ON); starGeometry->setUseVertexBufferObjects(true); starGeometry->setUseDisplayList(false); starGeometry->setUpdateCallback(new ColorCallback); starQuad->addDrawable(starGeometry); moveMT->addChild(antiDirectMT); antiDirectMT->addChild(antiTileMT); antiTileMT->addChild(spinMT); spinMT->addChild(starQuad); starGroup->addChild(star);
为了实现每一帧对场景进行更新实现动画效果,我们定义了一系列的更新回调
实现星星颜色的变化,我们定义了ColorCallback(继承于osg::Drawable::UpdateCallback)
class ColorCallback : public osg::Drawable::UpdateCallback { virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) { osg::Geometry *geometry = dynamic_cast<osg::Geometry*>(drawable); if (!geometry) { return; } osg::Vec4Array *colorArray = dynamic_cast<osg::Vec4Array*>(geometry->getColorArray()); if (colorArray) { osg::Vec4 color; color.set(rand() % 256 / 255.0f, rand() % 256 / 255.0f, rand() % 256 / 255.0f, 1.0); colorArray->clear(); colorArray->push_back(color); colorArray->dirty(); } } };为了实现星星的旋转我们定义了旋转更新回调(与前面课程中一样)
class RotateCallback : public osg::NodeCallback { public: RotateCallback(osg::Vec3d rotateAxis, double rotateSpeed) : osg::NodeCallback(), _rotateAxis(rotateAxis), _rotateSpeed(rotateSpeed), _rotateAngle(0.0) { //Nop } void setRotateSpeed(double speed) { _rotateSpeed = speed; } double getRotateSpeed() const { return _rotateSpeed; } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { //获取当前的平移位置 osg::Vec3d currentTranslate = currentMT->getMatrix().getTrans(); osg::Matrix newMatrix = osg::Matrix::rotate(_rotateAngle, _rotateAxis) * osg::Matrix::translate(currentTranslate); currentMT->setMatrix(newMatrix); _rotateAngle += _rotateSpeed; } traverse(node, nv); } private: osg::Vec3d _rotateAxis; //旋转轴 double _rotateSpeed; //旋转速度 double _rotateAngle; //当前旋转的角度 };
class TranslateCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { osg::Vec3d dist = currentMT->getMatrix().getTrans(); double x = dist.x(); x -= 0.01; if(x < 0.0f) x += 5.0; osg::Vec3d newDist = osg::Vec3d(x, 0, 0); currentMT->setMatrix(osg::Matrix::translate(newDist)); } traverse(node, nv); } };响应键盘操作定义在MainpulatorSceneHandler
class ManipulatorSceneHandler : public osgGA::GUIEventHandler该类在第八课中已有介绍
最后将创建的星星节点添加到根节点下
osg::Node* buildScene() { createStars(); osg::Group *root = new osg::Group; osg::MatrixTransform *zoomMT = new osg::MatrixTransform; zoomMT->setName("zoomMT"); zoomMT->setMatrix(osg::Matrix::translate(0, 0, -15.0f)); root->addChild(zoomMT); osg::MatrixTransform *tiltMT = new osg::MatrixTransform; tiltMT->setName("tiltMT"); tiltMT->setMatrix(osg::Matrix::rotate(0, osg::X_AXIS)); zoomMT->addChild(tiltMT); tiltMT->addChild(createStars()); return root; }
编译运行程序,一个漂亮的场景就出现了。
附:本课源码(源码中可能存在错误和不足,仅供参考)
#include "../osgNeHe.h" #include <QtCore/QTimer> #include <QtGui/QApplication> #include <QtGui/QVBoxLayout> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osgQt/GraphicsWindowQt> #include <osg/MatrixTransform> #include <osg/Texture2D> #include <osgGA/GUIEventAdapter> #include <osg/BlendFunc> #include <osg/Drawable> const int StarNumber = 50; double tilt = 0.0; ////////////////////////////////////////////////////////////////////////// //FindFirstNamedNodeVisitor用来遍历寻找与指定名称相同的节点 class FindFirstNamedNodeVisitor : public osg::NodeVisitor { public: FindFirstNamedNodeVisitor(const std::string& name): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _name(name), _foundNode(NULL) {} virtual void apply(osg::Node& node) { if (node.getName()==_name) { _foundNode = &node; return; } traverse(node); } std::string _name; osg::Node *_foundNode; }; ////////////////////////////////////////////////////////////////////////// //ColorCallback class ColorCallback : public osg::Drawable::UpdateCallback { virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) { osg::Geometry *geometry = dynamic_cast<osg::Geometry*>(drawable); if (!geometry) { return; } osg::Vec4Array *colorArray = dynamic_cast<osg::Vec4Array*>(geometry->getColorArray()); if (colorArray) { osg::Vec4 color; color.set(rand() % 256 / 255.0f, rand() % 256 / 255.0f, rand() % 256 / 255.0f, 1.0); colorArray->clear(); colorArray->push_back(color); colorArray->dirty(); } } }; ////////////////////////////////////////////////////////////////////////// //RotateCallback class RotateCallback : public osg::NodeCallback { public: RotateCallback(osg::Vec3d rotateAxis, double rotateSpeed) : osg::NodeCallback(), _rotateAxis(rotateAxis), _rotateSpeed(rotateSpeed), _rotateAngle(0.0) { //Nop } void setRotateSpeed(double speed) { _rotateSpeed = speed; } double getRotateSpeed() const { return _rotateSpeed; } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { //获取当前的平移位置 osg::Vec3d currentTranslate = currentMT->getMatrix().getTrans(); osg::Matrix newMatrix = osg::Matrix::rotate(_rotateAngle, _rotateAxis) * osg::Matrix::translate(currentTranslate); currentMT->setMatrix(newMatrix); _rotateAngle += _rotateSpeed; } traverse(node, nv); } private: osg::Vec3d _rotateAxis; //旋转轴 double _rotateSpeed; //旋转速度 double _rotateAngle; //当前旋转的角度 }; ////////////////////////////////////////////////////////////////////////// //AntiTIltCallback class AntiTiltCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { currentMT->setMatrix(osg::Matrix::rotate(-tilt, osg::X_AXIS)); } traverse(node, nv); } }; ////////////////////////////////////////////////////////////////////////// //TranslateCallback class TranslateCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { osg::Vec3d dist = currentMT->getMatrix().getTrans(); double x = dist.x(); x -= 0.01; if(x < 0.0f) x += 5.0; osg::Vec3d newDist = osg::Vec3d(x, 0, 0); currentMT->setMatrix(osg::Matrix::translate(newDist)); } traverse(node, nv); } }; ////////////////////////////////////////////////////////////////////////// class ManipulatorSceneHandler : public osgGA::GUIEventHandler { public: ManipulatorSceneHandler() { } virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&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::KEYDOWN): { if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Up) { FindFirstNamedNodeVisitor fnv("tiltMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *tiltMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!tiltMT) return false; osg::Matrix tiltMatrix = tiltMT->getMatrix(); osg::Quat original = tiltMatrix.getRotate(); double angle; osg::Vec3 vec; original.getRotate(angle, vec); angle += 0.03; tiltMT->setMatrix(osg::Matrix::rotate(angle, osg::X_AXIS)); tilt = angle; } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Down) { FindFirstNamedNodeVisitor fnv("tiltMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *tiltMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!tiltMT) return false; osg::Matrix tiltMatrix = tiltMT->getMatrix(); osg::Quat original = tiltMatrix.getRotate(); double angle; osg::Vec3 vec; original.getRotate(angle, vec); angle -= 0.03; tiltMT->setMatrix(osg::Matrix::rotate(angle, osg::X_AXIS)); tilt = angle; } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up) { FindFirstNamedNodeVisitor fnv("zoomMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *zoomMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!zoomMT) return false; zoomMT->setMatrix(osg::Matrix::translate(zoomMT->getMatrix().getTrans() + osg::Vec3(0, 0, 0.5))); } if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down) { FindFirstNamedNodeVisitor fnv("zoomMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *zoomMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!zoomMT) return false; zoomMT->setMatrix(osg::Matrix::translate(zoomMT->getMatrix().getTrans() + osg::Vec3(0, 0, -0.5))); } } default: break; } return false; } }; ////////////////////////////////////////////////////////////////////////// class ViewerWidget : public QWidget, public osgViewer::Viewer { public: ViewerWidget(osg::Node *scene = NULL) { QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), scene); 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* scene ) { osg::Camera* camera = this->getCamera(); camera->setGraphicsContext( gw ); const osg::GraphicsContext::Traits* traits = gw->getTraits(); camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 0.5) ); camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); camera->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0)); this->setSceneData( scene ); this->addEventHandler(new ManipulatorSceneHandler()); 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::Group* createStars() { osg::Group *starGroup = new osg::Group; for (unsigned i = 0; i < StarNumber; ++i) { float dist=(float(i)/StarNumber)*5.0f; float angle= float(i)/(StarNumber * 30); osg::Group *star = new osg::Group; osg::MatrixTransform *antiTileMT = new osg::MatrixTransform; antiTileMT->setUpdateCallback(new AntiTiltCallback); osg::MatrixTransform *antiDirectMT = new osg::MatrixTransform; antiDirectMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, -angle)); osg::MatrixTransform *directionMT = new osg::MatrixTransform; directionMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, angle)); osg::MatrixTransform *moveMT = new osg::MatrixTransform; moveMT->setMatrix(osg::Matrix::translate(dist, 0, 0)); moveMT->setUpdateCallback(new TranslateCallback); osg::MatrixTransform *spinMT = new osg::MatrixTransform; spinMT->setUpdateCallback(new RotateCallback(osg::Z_AXIS, 0.02)); star->addChild(directionMT); directionMT->addChild(moveMT); //星星叶节点 osg::Geode *starQuad = new osg::Geode; osg::Geometry *starGeometry = new osg::Geometry; osg::Texture2D *texture = new osg::Texture2D; texture->setImage(osgDB::readImageFile("Data/Star.bmp")); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); osg::Vec3Array *coords = new osg::Vec3Array; coords->push_back(osg::Vec3(-1.0f,-1.0f, 0.0f)); coords->push_back(osg::Vec3(1.0f,-1.0f, 0.0f)); coords->push_back(osg::Vec3(1.0f,1.0f, 0.0f)); coords->push_back(osg::Vec3(-1.0f,1.0f, 0.0f)); starGeometry->setVertexArray(coords); osg::Vec4Array *colors = new osg::Vec4Array; colors->push_back(osg::Vec4(rand() % 256 / 255.0f, rand() % 256 / 255.0f, rand() % 256 / 255.0f, 1.0)); starGeometry->setColorArray(colors); starGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); starGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); osg::Vec2Array *textureArray = new osg::Vec2Array; textureArray->push_back(osg::Vec2(0.0f, 0.0f)); textureArray->push_back(osg::Vec2(1.0f, 0.0f)); textureArray->push_back(osg::Vec2(1.0f, 1.0f)); textureArray->push_back(osg::Vec2(0.0f, 1.0f)); starGeometry->setTexCoordArray(0, textureArray); starGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture); starGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); starGeometry->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE, 0.5, 0.5); starGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc,osg::StateAttribute::ON); starGeometry->setUseVertexBufferObjects(true); starGeometry->setUseDisplayList(false); starGeometry->setUpdateCallback(new ColorCallback); starQuad->addDrawable(starGeometry); moveMT->addChild(antiDirectMT); antiDirectMT->addChild(antiTileMT); antiTileMT->addChild(spinMT); spinMT->addChild(starQuad); starGroup->addChild(star); } return starGroup; } osg::Node* buildScene() { createStars(); osg::Group *root = new osg::Group; osg::MatrixTransform *zoomMT = new osg::MatrixTransform; zoomMT->setName("zoomMT"); zoomMT->setMatrix(osg::Matrix::translate(0, 0, -15.0f)); root->addChild(zoomMT); osg::MatrixTransform *tiltMT = new osg::MatrixTransform; tiltMT->setName("tiltMT"); tiltMT->setMatrix(osg::Matrix::rotate(0, osg::X_AXIS)); zoomMT->addChild(tiltMT); tiltMT->addChild(createStars()); return root; } int main( int argc, char** argv ) { QApplication app(argc, argv); ViewerWidget* viewWidget = new ViewerWidget(buildScene()); viewWidget->setGeometry( 100, 100, 640, 480 ); viewWidget->show(); return app.exec(); }