2006年05月29日

是一个开源的、高效的2D图形库,它的网站:http://www.antigrain.com/

AGG的功能与GDI+的功能非常类似,但提供了比GDI+更灵活的编程接口,其产生的图形的质量也非常高,而且它是跨平台的,其宣传可以在非常多的操作系统上运行。

功能:

  • Rendering of arbitrary polygons with Anti-Aliasing and Subpixel Accuracy.
  • Gradients and Gouraud Shading.
  • Fast filtered image affine transformations, including many interpolation filters (bilinear, bicubic, spline16, spline36, sinc, Blackman).
  • Strokes with different types of line joins and line caps.
  • Dashed line generator.
  • Markers, such as arrowheads/arrowtails.
  • Fast vectorial polygon clipping to a rectangle.
  • Low-level clipping to multiple rectangular regions.
  • Alpha-Masking.
  • A new, fast Anti-Alias line algorithm.
  • Using arbitrary images as line patterns.
  • Rendering in separate color channels.
  • Perspective and bilinear transformations of vector and image data.
  • Boolean polygon operations (and, or, xor, sub) based on Alan Murta’s
    General Polygon Clipper.
  • 2006年05月17日

    http://earthobservatory.nasa.gov/Newsroom/BlueMarble/BlueMarble_monthlies.html

    这次整个地球是一个文件,但是提供了一年中12个月不同的照片,也提供了不同分辨率的图片,分别是8千米/象素,2km/象素,500米/想素。

    这样,处理的命令行就变为:osgdem  –whole-globe -t globe.tif –geocentric -l 12 -o globe.ive -a globe.osga

    注:下载的图片是jpg格式,直接处理程序会出错,存为tif格式处理就没问题了。

    2006年05月15日

    基于fox1.6.4,按照fox的邮件列表一位日本朋友的方法(http://sourceforge.net/mailarchive/message.php?msg_id=15420326)修改源码编译后就能输入中文了,他上面讲得很清楚。

    不过他只修改了FXText类,我自己又修改了FXTextField类,只需在FXMAPFUNC(SEL_QUERY_HELP,0,FXTextField::onQueryHelp),这行后面添加FXMAPFUNC(SEL_IME_TEXT,0,FXTextField::onCmdInsertString),这行就行。

    一共修改了五个文件,可以到下面地址下载修改过的文件。

    http://www.flmnware.com/community/viewtopic.php?t=227

    2006年05月13日

    2006年05月12日

    很早就听说你,试了一下感觉也不错,轻量级适合我的口味,可是,可是,你怎么不支持输入法啊?

    看你开发进度这么快,什么时候才能添加输入法支持啊?

    http://www.fox-toolkit.org/

    三、摄像机控制

    (最近更新:2006314)

    原文地址:http://www.flmnware.com/


    目的:通过本教程,学会用键盘和鼠标实现摄像机的控制。





    1、事件处理模型

    OpenSceneGraph里,类GUIEventHandler为任何想要处理GUI事件的类提供了一个基本接口。处理事件的方法是:


    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);



    在这个方法里,有两个参数,第一个是GUI事件的供给者,第二个参数用来handle方法对GUI进行反馈,它可以让GUIEventHandler根据输入事件让GUI进行一些动作。

    如果要进行事件处理,可以从GUIEventHandler继承出自己的类,然后覆盖handle方法,在里面进行事件处理。osgProducer::Viewer类维护一个GUIEventHandler队列,事件在这个队列里依次传递,handle的返回值决定这个事件是否继续让后面的GUIEventHandler处理,如果返回true,则停止处理,如果返回false,后面的GUIEventHandler还有机会继续对这个事件进行响应。

    如果想向Viewer类加入自己的GUIEventHandler,可以这样,假设我的GUIEventHandlerCMyGUIEventHandler


    viewer.getEventHandlerList().push_back(new CmyGUIEventHandler());

    viewer.getEventHandlerList().push_back(new CmyGUIEventHandler());

    viewer.getEventHandlerList().push_back(new CmyGUIEventHandler());



    如果想向节点加入自己的GUIEventHandler,就设置节点的事件回调。


    node.setEventCallback(new CmyGUIEventHandler());

    node.setEventCallback(new CmyGUIEventHandler());

    node.setEventCallback(new CmyGUIEventHandler());



    GUIEventHandler可处理的事件类型可以用ea.getEventType()得到,包括以下几种:


    事件类型

    说明

    PUSH

    鼠标按键按下

    RELEASE

    鼠标按键抬起

    DOUBLECLICK

    鼠标按键双击

    DRAG

    鼠标按下按键拖动

    MOVE

    鼠标移动

    KEYDOWN

    键盘按键按下

    KEYUP

    键盘按键抬起

    FRAME

    每帧触发的事件

    RESIZE

    窗口改变大小

    SCROLLUP

    鼠标滚轮上滚

    SCROLLDOWN

    鼠标滚轮下滚

    SCROLLLEFT

    鼠标滚轮左滚

    SCROLLRIGHT

    鼠标滚轮右滚


    还可以通过ea.getKey()得到键盘按键码,通过ea.getButtonMask()得到鼠标按键码。因为跨平台的关系,OpenSceneGraph自己定义了键盘码,具体可参考GUIEventAdapter头文件。



    2、视图矩阵控制模型

    OpenSceneGraph的摄像机操作,由osgGA::MatrixManipulator的派生类来完成,可以调用osgProducer::Viewer类的addCameraManipulator方法把自己的摄像机加入进去。默认情况下,Viewer自带了5个摄像机,这些在第一篇里讲过,可用数字键在这些摄像机之间切换,我们自定义的,也依次排在后面。

    OpenSceneGraph里,所有的视图矩阵操作都是通过矩阵来完成的,不同摄像机之间的交互也通过矩阵,这样就提供了一个通用的模型,不管你习惯使用gluLookAt方式的,还是习惯操作摄像机位置姿态方式的,都可以很容易嵌入OpenSceneGraph的框架中,因为所有方式的最后结果就是矩阵。

    要实现自己的摄像机,关键是实现osgGA::MatrixManipulator类的以下五个纯虚函数:


    virtual void setByMatrix(const osg::Matrixd& matrix);

    virtual void setByMatrix(const osg::Matrixd& matrix);

    virtual void setByMatrix(const osg::Matrixd& matrix);



    这个函数在从一个摄像机切换到另一个摄像机时调用,用来把上一个摄像机的视图矩阵传过来,这样就可依此设定自己的初始位置了。

    virtual void setByInverseMatrix(const osg::Matrixd& matrix);

    virtual void setByInverseMatrix(const osg::Matrixd& matrix);

    virtual void setByInverseMatrix(const osg::Matrixd& matrix);



    这个方法当在外部直接调用ViewersetViewByMatrix方法时,把设置的矩阵传过来,让摄像机记住新更改的位置。

    virtual osg::Matrixd getMatrix() const;

    virtual osg::Matrixd getMatrix() const;

    virtual osg::Matrixd getMatrix() const;



    SetByMatrix方法需要的矩阵就是用这个方法得到的,用来向下一个摄像机传递矩阵。

    virtual osg::Matrixd getInverseMatrix() const;

    virtual osg::Matrixd getInverseMatrix() const;

    virtual osg::Matrixd getInverseMatrix() const;



    这个是最重要的方法,这个方法每帧会被调用,它返回当前的视图矩阵。

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us);

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us);

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us);



    在这个方法里进行时间的处理,改变自己的状态,进而在 getInverseMatrix 被调用时,改变场景内摄像机的位置姿态。


    3、类第一人称射击游戏的摄像机

    这个摄像机最早在OpenGVS里实现,在OpenGVS里,摄像机的操作是把它想象成场景中一个有位置姿态的物体。这个摄像机的功能包括adws控制左右前后行走,用HomeEnd控制上下移动,鼠标控制转向,并用空格控制是否使用鼠标的转向功能,用=-控制移动步长。

    摄像机的位置用位置和姿态来表示,键盘鼠标控制位置姿态的改变,值得注意的是将位置姿态转化为视图矩阵,看方法 getInverseMatrix()


    osg::Matrixd

    FPSCamera::getInverseMatrix() const

    {

    osg::Matrixd mat;

    mat.makeRotate(_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),

    _vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),

    _vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));

    return osg::Matrixd::inverse(mat * osg::Matrixd::translate(_vPosition));

    }

    osg::Matrixd

    FPSCamera::getInverseMatrix() const

    {

    osg::Matrixd mat;

    mat.makeRotate(_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),

    _vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),

    _vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));

    return osg::Matrixd::inverse(mat * osg::Matrixd::translate(_vPosition));

    }

    osg::Matrixd

    FPSCamera::getInverseMatrix() const

    {

    osg::Matrixd mat;

    mat.makeRotate(_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),

    _vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),

    _vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));

    return osg::Matrixd::inverse(mat * osg::Matrixd::translate(_vPosition));

    }



    其中_vPosition是位置,_vRotation是姿态,代码中,先得到旋转矩阵,可以用矩阵类的 makeRotate 方法,参数是旋转角度和旋转轴,然后用矩阵类的translate方法得到平移矩阵,用旋转矩阵左乘平移矩阵,就得到摄像机的旋转变换矩阵,但是视图矩阵是变换矩阵的反矩阵,所以最后要用矩阵类的 inverse方法的到反矩阵。



    总结:OpenSceneGraph提供的事件处理非常直接好用,而视图变换这种基于矩阵的设计,一般化了视图矩阵的操作,可以用各种方法实现摄像机的控制,很容易把以前的程序移植过来。



    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com


    二、节点的更新回调和位置姿态控制

    (最近更新:2006314)

    原文地址:http://www.flmnware.com/


    目的:通过本教程,学会将自定义节点添加入场景图,并控制节点在每一帧的运动。





    1、添加自定义节点

    OpenSceneGraph提供的节点类(Node及其子类)实现了通用的场景图结构,但是不可能提供每一个用户需要的类,比如我要做一个飞行模拟程序,OpenSceneGraph不会给我提供一个Aircraft类。所以要实现自己的应用程序逻辑,就要自定义自己的类,这个类以OpenSceneGraph提供的类为基类,并能添加到场景图中,实现自己的特定功能。

    一个飞机的运动,最简单情况是我们要考虑飞机的位置和姿态,而要想在OpenSceneGraph里控制一个模型的位置、旋转或缩放,需要在这个节点上面添加一个Transform的子类,这要用到矩阵运算,矩阵运算非常复杂,对于控制位置姿态这个经常用到的功能,OpenSceneGraph提供了类PositionAttitudeTransform,通过它可以方便控制位置姿态而不用理会复杂的矩阵运算。

    飞机需要导入一个模型,在场景图中,我们就把这个模型加为这个类实例的儿子,这样,这个类可以这样定义,飞机类CCessna


    class CCessna :

    public osg::PositionAttitudeTransform

    {

    public:

    CCessna(void);

    ~CCessna(void);

    private:

    osg::ref_ptr<osg::Node> _Model;

    };

    class CCessna :

    public osg::PositionAttitudeTransform

    {

    public:

    CCessna(void);

    ~CCessna(void);

    private:

    osg::ref_ptr<osg::Node> _Model;

    };

    class CCessna :

    public osg::PositionAttitudeTransform

    {

    public:

    CCessna(void);

    ~CCessna(void);

    private:

    osg::ref_ptr<osg::Node> _Model;

    };

    class CCessna :

    public osg::PositionAttitudeTransform

    {

    public:

    CCessna(void);

    ~CCessna(void);

    private:

    osg::ref_ptr<osg::Node> _Model;

    };



    成员变量_Model是保存模型的指针。

    这样,在构造函数里,将_Model加为儿子:


    CCessna::CCessna(void)

    {

    _Model = osgDB::readNodeFile("cessna.osg");

    this->addChild(_Model.get());

    }

    CCessna::CCessna(void)

    {

    _Model = osgDB::readNodeFile("cessna.osg");

    this->addChild(_Model.get());

    }

    CCessna::CCessna(void)

    {

    _Model = osgDB::readNodeFile("cessna.osg");

    this->addChild(_Model.get());

    }

    CCessna::CCessna(void)

    {

    _Model = osgDB::readNodeFile("cessna.osg");

    this->addChild(_Model.get());

    }



    继续使用第一个教程的源码,将这一句:


    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);

    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);

    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);

    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);



    改为:


    CCessna* cessna = new Ccessna();

    viewer.setSceneData(cessna);

    CCessna* cessna = new Ccessna();

    viewer.setSceneData(cessna);

    CCessna* cessna = new Ccessna();

    viewer.setSceneData(cessna);

    CCessna* cessna = new Ccessna();

    viewer.setSceneData(cessna);



    就能看到效果了。


    2、给节点添加更新回调并控制节点运动

    为了控制飞机的运动,我们应该在每一帧根据速度等条件计算飞机的位置,并更新飞机节点,但是我们的更新代码应该加在哪里呢?

    在程序的主循环里,可以看到这句:


    viewer.update();

    viewer.update();

    viewer.update();

    viewer.update();



    在这个方法里,OpenSceneGraph会遍历场景图,调用每个节点的更新回调,我们只要把自己代码放在更新回调里,就能保证每帧代码得到运行,也就能实现我们要求的功能了。

    OpenSceneGraph中,每个回调都是类NodeCallback的子类,我们只需要继承它,就能实现功能了。为了保证飞机更新的时候能得到需要的足够信息,又不让飞机的内部实现暴露于外,我们让更新回调只进行一个转手的操作,而实际的更新函数放在CCessna类里。

    更新回调代码如下:


    class CCessnaUpdateCallback :

    public osg::NodeCallback

    {

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)

    {

    CCessna* cessna = dynamic_cast<CCessna*>(node);

    if(cessna != NULL)

    {

    cessna->update();

    }

    }

    };

    class CCessnaUpdateCallback :

    public osg::NodeCallback

    {

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)

    {

    CCessna* cessna = dynamic_cast<CCessna*>(node);

    if(cessna != NULL)

    {

    cessna->update();

    }

    }

    };

    class CCessnaUpdateCallback :

    public osg::NodeCallback

    {

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)

    {

    CCessna* cessna = dynamic_cast<CCessna*>(node);

    if(cessna != NULL)

    {

    cessna->update();

    }

    }

    };

    class CCessnaUpdateCallback :

    public osg::NodeCallback

    {

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)

    {

    CCessna* cessna = dynamic_cast<CCessna*>(node);

    if(cessna != NULL)

    {

    cessna->update();

    }

    }

    };



    这个类覆盖了基类对操作符()的重载,所以说它实际上是一个函数对象,因为这个重载是通用的,在函数里我们要先动态转换成 CCessna 类的指针,然后调用她的更新成员函数。

    为了让这个回调起作用,不要忘了在 CCessna 类的构造函数里设置更新回调:


    this->setUpdateCallback(new CCessnaUpdateCallback());

    this->setUpdateCallback(new CCessnaUpdateCallback());

    this->setUpdateCallback(new CCessnaUpdateCallback());

    this->setUpdateCallback(new CCessnaUpdateCallback());



    下面设计让飞机绕圈飞行,同时让飞机横滚,代码都是写计算操作,需要注意的是PositionAttitudeTransform类提供的设置姿态的方法不是使用通常习惯的HPR为参数,而是一个四元数,关于四元数数学请参考相关书籍,但是HPR向四元数的转换还是很直接的,这就是我们的setRotation方法:


    void

    CCessna::setRotation(osg::Vec3& rot)

    {

    osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),

    rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),

    rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));

    this->setAttitude(q);

    }

    void

    CCessna::setRotation(osg::Vec3& rot)

    {

    osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),

    rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),

    rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));

    this->setAttitude(q);

    }

    void

    CCessna::setRotation(osg::Vec3& rot)

    {

    osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),

    rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),

    rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));

    this->setAttitude(q);

    }

    void

    CCessna::setRotation(osg::Vec3& rot)

    {

    osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),

    rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),

    rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));

    this->setAttitude(q);

    }



    update方法里,通过设置位置和旋转来控制飞机运动,具体请查看代码。


    3、完善程序

    由于Viewer类在程序开始会根据模型的包围盒调整视点初始位置,这样能保证程序开始能看到模型,但是因为我们的模型是运动的,运行一点之后飞机会飞出视线,所以我们给飞机提供一个场地,这个场地就是一个正方体,代码如下:


    osg::Group* root = new osg::Group();

    osg::Geode* geode = new osg::Geode();

    geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));

    root->addChild(geode);

    CCessna* cessna = new CCessna();

    root->addChild(cessna);

    viewer.setSceneData(root);

    osg::Group* root = new osg::Group();

    osg::Geode* geode = new osg::Geode();

    geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));

    root->addChild(geode);

    CCessna* cessna = new CCessna();

    root->addChild(cessna);

    viewer.setSceneData(root);

    osg::Group* root = new osg::Group();

    osg::Geode* geode = new osg::Geode();

    geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));

    root->addChild(geode);

    CCessna* cessna = new CCessna();

    root->addChild(cessna);

    viewer.setSceneData(root);

    osg::Group* root = new osg::Group();

    osg::Geode* geode = new osg::Geode();

    geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));

    root->addChild(geode);

    CCessna* cessna = new CCessna();

    root->addChild(cessna);

    viewer.setSceneData(root);



    这里用到了Geode 类和Drawable类的子类ShapeDrawableGeode 是场景图的叶子节点,所有的几何体都包含在 Geode 里,而一个 Geode 里可以有多个Drawable的子类,我们看到的图像真正是Drawable画的,这个后面在讨论。



    总结:要想实现自己的功能,加入自己的代码,就要定义自己的节点类,由于OpenSceneGraph使用了组合设计模式,我们自己定义的类在OpenSceneGraph看来和它自己的类是一样的,OpenSceneGraph的这种场景图结构,基本上是业界的一种标准,很多程序都实现了类似的结构。



    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com


    一、最简单的OpenSceneGraph程序

    (最近更新:2006314)

    原文地址:http://www.flmnware.com/


    目的:通过本教程,学会OpenSceneGraph工程属性的设置,加载并查看三维模型,熟悉Viewer类提供的用户操作。




    1、建立和设置工程

    启动Microsoft Visual Studio .NET 2003,建立一个Win32控制台项目,在应用程序设置选项卡里,选择空项目,点击完成。为了在工程选项里出现C/C++的选项,先给工程添加一个空的C++源文件。

    OpenSceneGraph运行需要多线程DLL的运行时库和RTTI,所以,在建立每个OpenSceneGraph工程后,第一件事就是更改工程设置。

    打开项目属性,在C/C++选项卡里,点击代码生成页,更改运行时库,对于Debug版,用多线程调试 DLL (/Mdd),对于Release版,用多线程 DLL (/MD)

    设置C/C++选项卡里的语言页的启用运行时类型信息为:是(/GR)

    OpenSceneGraph程序需要链接对应的库文件,打开链接器选项卡里的输入页,设置附加依赖项,对于Debug版,是OpenThreadsWin32d.lib Producerd.lib osgd.lib osgDBd.lib osgFXd.lib osgGAd.lib osgParticled.lib osgProducerd.lib osgSimd.lib osgTerraind.lib osgTextd.lib osgUtild.lib,对于Release版,设置这些lib文件不带“d”的版本。

    这样,工程属性就设置好了。


    2、编辑程序

    在刚才建立的空文件里输入如下内容:

    #include <osgDB/ReadFile>

    #include <osgProducer/Viewer>

    int

    main(int, char**)

    {

    osgProducer::Viewer viewer;

    viewer.setUpViewer();

    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);

    viewer.realize();

    while (!viewer.done())

    {

    viewer.sync();

    viewer.update();

    viewer.frame();

    }

    viewer.sync();

    return 0;

    }

    #include <osgDB/ReadFile>

    #include <osgProducer/Viewer>

    int

    main(int, char**)

    {

    osgProducer::Viewer viewer;

    viewer.setUpViewer();

    osg::Node* node = osgDB::readNodeFile("cessna.osg");

    viewer.setSceneData(node);

    viewer.realize();

    while (!viewer.done())

    {

    viewer.sync();

    viewer.update();

    viewer.frame();

    }

    viewer.sync();

    return 0;

    }



    编译运行后,就能看到一个飞机的模型。


    NOTE如果看不到模型,检查OSG1.0是否正确安装,即OSG_FILE_PATH是否设置正确。


    这个程序的核心是,osgProducer命名空间的Viewer类实例,这个类负责管理窗口的创建,控制投影矩阵,控制OpenGL的渲染,实现程序的主循环。它还具有控制场景渲染状态,查看帧速,抓图等功能。OsgDB命名空间的readNodeFile函数负责从文件里读入三维模型及其纹理等数据,它返回读入的节点,可以加到场景图中。主循环分三步,同步,更新和绘制,当用户按下ESC键时,osgProducer::Viewer::done()返回true,主循环结束。


    3、程序的操作


    鼠标:

    摄像机操作器

    Trackball

    Flight

    Drive

    Terrain

    左键

    旋转视图

    加速

    加速

    旋转视图

    中键

    平移视图

    停止

    停止

    平移视图

    右键

    缩放

    减速/倒退

    减速/倒退

    缩放




    键盘:

    Escape

    退出程序

    Space

    重置摄像机位置

    1

    选择“轨迹球(Trackball)”摄像机操作器

    2

    选择“飞行(Flight)”摄像机操作器

    3

    选择“驾驶(Drive)”摄像机操作器

    4

    选择“地形(Terrain)”摄像机操作器

    5

    选择“不明飞行物(UFO)”摄像机操作器

    b

    开关背面剔除

    f

    全屏/窗口切换

    h

    显示帮助

    H

    在摄像机控制器是 UFO ,重置到起始位置

    l

    开关灯光

    o

    将当前场景的场景图保存到文件"saved_model.osg"

    O

    抓图,保存为文件"saved_image*.rgb"

    s

    显示帧速等信息

    t

    开关纹理

    v

    开关垂直同步

    w

    切换多边形、线、点显示模式

    z

    开始记录摄像机路径

    Z

    如果正在记录摄像机路径,保存路径到文件"saved_animation.path",然后开始播放这个路径文件



    总结:OpenSceneGraphViewer类本身提供了很多功能,所以,只需要简单的几句代码,就能有一个不错的程序,这些功能如果用OpenGL实现,需要很多的时间。当不再为这些基础功能操心的时候,我们就能把精力放在我们要实现的功能上面。



    作者:flmn

    网站:http://www.flmnware.com

    作者:flmn

    网站:http://www.flmnware.com


    http://www.flmnware.com/osg/tutorial/

    我网站的教程,现在已经有3篇了。

    计划如下:

     一、最简单的OpenSceneGraph程序

    学会OpenSceneGraph工程属性的设置,加载并查看三维模型,熟悉Viewer类提供的用户操作。


    二、节点的更新回调和位置姿态控制

    学会将自定义节点添加入场景图,并控制节点在每一帧的运动。


    三、摄像机控制

    学会用键盘和鼠标实现摄像机的控制。


    四、点选[HTML][PDF]


    五、文字osgText[HTML][PDF]


    六、渲染状态操作[HTML][PDF]


    七、绘制几何体[HTML][PDF]


    八、嵌入OpenGL语句[HTML][PDF]


    九、使用GLSL[HTML][PDF]


    十、粒子系统[HTML][PDF]


    十一、地形模块osgTerrain[HTML][PDF]


    十二、3ds Max插件OSGExp[HTML][PDF]