zoukankan      html  css  js  c++  java
  • osg实例介绍

     osg实例介绍

    转自:http://blog.csdn.net/yungis/article/list/1

    [原]osgmotionblur例子
    该例子演示了运动模糊的效果。
    一下内容是转自网上的:
    原理:
    引用内容
    对于运动画面,将当前帧画面与上一帧画面进行alpha融合,以产生出残影——运动模糊效果。
    通过使用累积缓存来完成这项工作。
    OpenGL提供一个累积缓存,可以用来存储当前指定的颜色缓存里面的内容,并进行一定的运算操作。
    通过函数glAccum可以对累积缓存进行操作。

    glAccum介绍如下:
    引用内容
    void glAccum(GLenum op, GLfloat value)
    op值用于指定操作类型,包括:
    GL_ACCUM    从指定的颜色缓存里面读取像素数据,将其像素值(R, G, B, A)乘以value的结果加上累积缓存里面的值以后再保存到累积缓存;
    GL_LOAD     从指定的颜色缓存里面读取像素数据,将其像素值乘以value的结果直接保存到累积缓存;
    GL_ADD      将累积缓存里面的像素值与value相加以后保存到累积缓存;
    GL_MULT     将累积缓存里面的像素值与value相乘以后保存到累积缓存;
    GL_RETURN   将累积缓存里面的像素值乘以value以后保存到指定的颜色缓存;
    在这个例子中,用到了GL_MULT、GL_ACCUM、GL_RETURN。
    在主程序中osg::DisplaySettings::instance()->setMinimumNumAccumBits(8,8,8,8);设置了积累缓冲区各位数。
    然后,osgViewer::Viewer::Windows windows;
        viewer.getWindows(windows);
        for
    (osgViewer::Viewer::Windows::iterator itr = windows.begin();
            itr != 
    windows.end();
            ++itr)
        {
            (*itr)->add(new MotionBlurOperation

    (persistence));
        }在视景体中每个图形窗体都添加了MotionBlurOperation类,MotionBlurOperation继承Operation,在virtual void operator () (osg::Object* object)方
    法中如果是第一次渲染,则清楚积累缓冲区的值,glClearColor(0, 0, 0, 0);         
    glClear(GL_ACCUM_BUFFER_BIT);
    之后,glAccum(GL_MULT, s);
            glAccum(GL_ACCUM, 1 - s);
            glAccum(GL_RETURN, 1.0f);通过这三个函数计算。
    我们一个一个的进行说明,首先,glAccum(GL_MULT, s);积累缓冲区中的值乘以s,结果写进积累缓冲区中,glAccum(GL_ACCUM, 1 - s);然后,颜色缓冲区中的值乘以1-s,结果写入积累缓冲区中,最后,glAccum(GL_RETURN, 1.0f);把积累缓存区中的值乘以1,写入颜色缓存区中,完成运动模糊的效果,这里可以看出积累缓冲区中的值只有一份,是每一帧不同比率的一个累计,也就是说第一帧的累计效果也是存在的,只是随着帧数的增加,第一帧的影像比率趋近于0了。这几个函数中不难得出,s值越大,前几帧的效果存在的时间越长,运动模糊效果越明显,如果s趋近于1,程序将很长时间是黑屏,因为glClearColor(0, 0, 0, 0);glClear(GL_ACCUM_BUFFER_BIT);积累缓冲区颜色值为黑色,s趋近于1,第一帧的影像非常大,所以很长时间一直为最初的颜色——黑色。

    这里必须要深入研究一下,glAccum应用的时刻,以上的说明中可以看出,glAccum应该是一帧中颜色缓冲区中的值计算完之后应用,在这个例子中用了otionBlurOperation,
    MotionBlurOperation继承Operation。我们就看看Operation是做什么的。到了Operation的定义,说它是实现图形操作的基类,子类必须重写virtual void operator () (Object*) = 0;方法,实现相应的功能。在GraphicsContext类中typedef std::list< ref_ptr<Operation> > GraphicsOperationQueue; GraphicsOperationQueue              _operations;定义了一个Operation类型的一个链表,这个链表又在什么地方应用的了?还回到GraphicsContext::runOperations()中,记得上一篇osgmemorytest例子中,提到过GraphicsContext::runOperations(),在这里实现(*(camera->getRenderer()))(this);渲染工作。这一次我们又找的了这个函数,(*_currentOperation)(this);遍历_operations这个链表,然后执行每个Operation中的operator () (Object*)方法,从runOperations()这个函数中,我们知道了osg的渲染过程,先进行渲染,然后在执行_operations中的操作。
    我们回到例子中,MotionBlurOperation这个类,把它加入到了所有的图形上下文中osgViewer::Viewer::Windows windows;    viewer.getWindows(windows);
        for

    (osgViewer::Viewer::Windows::iterator itr = windows.begin();
            itr != 
    windows.end();
            ++itr)
        {
            (*itr)->add(new MotionBlurOperation
    (persistence));
        },glAccum函数执行的时候正好和我们之前所判断的一样,先渲染,渲染后对颜色缓存区中的值进行累积。

    对这个例子和积累缓冲区总算有了一些理解,但这里还存在一个问题,如果想只对场景中的某个部分做运动模糊应该如何处理???比如有一个足球场,一个运动中的足球,只对这个足球进行运动模糊处理,如何去做???

    这个问题自己还没有去验证,自己想的一个大概方法是,相同位置的的两个相机,一个关注足球之外的场景,不做运动模糊处理;另外一个只关注足球,做运动模糊处理,然后,两幅场景融合为一个场景。
    作者:yungis 发表于2013/7/30 23:44:12 原文链接
    阅读:518 评论:1 查看评论
     
    [原]osgmemorytest例子
    osgmemorytest这个例子也从一帧入手。
    eventTraversal();
    updateTraversal();
    renderingTraversals();
    事件回调、更新回调、渲染。
    OpenGL是状态机,调整好各个状态,然后绘制几何体。OSG中同理,renderingTraversals中根据每个StateSet绘制Drawable。无论是加载的模型还是自己绘制的几何体,最后都是Drawable。

    renderingTraversals中最主要的还进行了剔除(CullVisitor),这里不多说,主要说OSG中OpenGL的状态的执行。 

    进入renderingTraversals函数内部,找到(*itr)->runOperations();(对于单线程来说),这句话进行了场景的绘制工作。顺着这个藤一直摸下去,为了思路清晰,列出序号:
    1、调用了GraphicsContext::runOperations();
    2、在GraphicsContext::runOperations()中找到(*(camera->getRenderer()))(this);
    3、跟到了Renderer中的void Renderer:perator () (osg::GraphicsContext* context)
    {  
    if (_graphicsThreadDoesCull)
        {
            cull_draw();
        }
        else
        {
            draw();
        }
    }。
    4、在Renderer的draw方法中找到了 sceneView->draw();
    5、如果不需要立体显示,在 sceneView->draw();中会找到_renderStage->drawPreRenderStages(_renderInfo,previous);
    _renderStage->draw(_renderInfo,previous);
    6、然后执行了RenderStage中的drawInner
    7、RenderBin::draw(renderInfo,previous);
    8、drawImplementation(renderInfo,previous);
    9、RenderLeaf* rl = dw_itr->get();     rl->render(renderInfo,previous);
    10、state.apply(rg->getStateSet());_drawable->draw(renderInfo);最终实现绘制
    好复杂的一条路了,对于多线程而言与以上的步骤有些不同,但大体差不多。

    几个重要的类,GraphicsContext--Renderer--SceneView--RenderStage(RenderBin)--RenderLeaf--Drawable。

    最终的实现都在具体的Drawable中,就像上面说的,无论是加载的模型还是自己绘制的几何体,最后都是Drawable。这样最终会调用可用的Drawable实现绘制。
    以上这么一条复杂的路线,要重点说的是第10步中的state.apply(rg->getStateSet());

    下面从这个调用说起,State存储了OpenGL需要的所有的状态,是OSG与OpenGL状态的接口,进入这个函数中看看做了什么。
    在这个函数中找到了applyAttributeList(_attributeMap,dstate->getAttributeList());这么一句话,深入下去。循环调用了applyAttribute这个函数,而传递的就是StateAttribute,这就是OSG中某个状态属性,在applyAttribute中找到了attribute->apply(*this);状态属性执行了apply把State传入进去。进入StateAttribute中的apply,不禁大失所望,什么也没有??

    StateAttribute是基类,打开API文档,看看继承StateAttribute的状态属性吧,有几十个之多,我们重点找几个看看。
    Fog,进入Fog的Fog::apply(State& state),在这里看见了OpenGL函数,glFogi以及一系列的设置。
    PolygonOffset,进入PolygonOffset的PolygonOffset::apply(State&),在这里看见了glPolygonOffset执行多边形偏移。
    Texture2D,进入Texture2D::apply(State& state),在这里看见了glTexImage2D。
    ...
    attribute->apply(*this)这么短短的一句话,就执行了所有状态属性的设置。
    这篇文章的标题是osgmemorytest,怎么绕了这么多?下面回到osgmemorytest例子中。

    定义了好多的基类,MemoryTest、GLObject、GLMemoryTest。
    有用的是ContextTest,osg::ref_ptr<osg::GraphicsContext> window = osg::GraphicsContext::createGraphicsContext(traits.get());创建了GraphicsContext,根据pbuffer来决定是PixelBuffer还是Window。
    StateAttributeObject中执行了_attribute->apply(*renderInfo.getState());看到这里就知道了为什么上面说了那么多,都是在说这个类的作用。
    TextureTest中最终都调用了StateAttributeObject。
    FboTest中也调用了StateAttributeObject。
    DrawableObject调用了_drawable->draw(renderInfo);
    GeometryTest调用了DrawableObject中的函数。

    从以上几个类的功能来说,不难看出他们所作的事情,1、创建窗口;2、设置状态;3、绘制;
    因此整个的例子大体上描绘了OSG渲染的一个过程,而main函数中所做的就是创建窗口、设置状态、绘制。打印出contexts数目,objects applied次数已经执行所用的实际。
    作者:yungis 发表于2013/6/7 23:32:32 原文链接
    阅读:600 评论:0 查看评论
     
    [原]关于例子
    最近很久没有写文章了,很久没有登录csdn了。最初只是为了一边研究一遍学osg而写的一些体会,没想到有这么多的志同道合的朋友们关注。此时此刻觉得有些感动,osg例子还需要继续下去,专题还需要继续下去。有精力的话,osgearth、osgocean、delta3d真的想一边学习研究一边写下去。有那么多osgChina上的朋友支持,以后有新的例子会在osgChina上发表的,多谢支持!
    作者:yungis 发表于2013/6/6 23:47:56 原文链接
    阅读:580 评论:1 查看评论
     
    [原]osgmanipulator例子
    这个例子演示了osg中拖拽器的使用,可以控制模型的移动、旋转缩放。
    createDemoScene函数,如果没有加载模型,默认的创建几个基本几何体,bool 
    fixedSizeInScreen参数决定拖拽器是否固定大小。这个函数里面的代码很简单,创建了几个基
    本几何体,然后把他们包装上不同的拖拽器,加入到场景中。
    TabBoxDragger,
    TabPlaneDragger,
    TabBoxTrackballDragg,
    TrackballDragger,Translate1DDragger,
    Translate2DDragger,
    TranslateAxisDragger
    一种有这么七种拖拽器,TabBoxDragger,
    包围盒,用于缩放和移动几何体,TabPlaneDragger,

    个平面,用于平面内缩放和移动几何体,TabBoxTrackballDragg,
    一个包围盒和一个包围球,用
    于移动、缩放、选择几何体,TrackballDragger包围球,用于旋转几何体,
    Translate1DDragger,
    Translate2DDragger,
    TranslateAxisDragger
    一个、两个、三个箭头的拖拽
    器,分别在不同的方向上移动几何体。
    我们进入addDraggerToScene函数,这个函数就是包装拖拽器的地方,首先根据名称创建拖拽器
    createDragger
    我们进入osgManipulator这个库,看看他是怎么设计的,Dragger类继承MatrixTransform拖拽器
    的基类,在拖拽器中考虑到继承关系,拖拽器可能会有父拖拽器,
    setActivationModKeyMask setActivationKeyEvent非常重要,设置按指定键的时候,鼠标才可
    以拖动拖拽器。
    traverse递归函数是场景中的每个节点都会调用的,因此在这个函数里,当是事件遍历的时候调
    用了内置的handle方法。实现拖拽动作。
    这个思路又给我们一些启示,与场景的交互,不一定要继承EventHandler,通过traverse同样可
    以实现。这些需要深入理解在事件遍历的时候osg都干了些什么。
    addTransformUpdating这个方法,通过回调实现变换。
    还有其他的一些设置,比如操作器是否可以,是否接受事件,创建拖拽器几何体等等。
    CompositeDragger类继承Dragger,是组合拖拽器,多个拖拽器组合成一个拖拽器。
    RotateCylinderDragger、RotateSphereDragger、Scale1DDragger、Scale2DDragger、
    Translate1DDragger、Translate2DDragger都是继承Dragger
    ScaleAxisDragger、TabBoxDragger、TabBoxTrackballDragger、TabPlaneDragger、
    TabPlaneTrackballDragger、TrackballDragger、TranslateAxisDragger、
    TranslatePlaneDragger都是继承CompositeDragger
    而对外暴露的操作器一般都是组合操作器。
    Command创建了移动,缩放,旋转的命令,在Dragger中使用。
    回到例子中来,osgManipulator::Dragger* dragger = createDragger(name);根据指定的名称
    创建拖拽器,判断是否缩放拖拽器,如果不缩放拖拽器,则通过DraggerContainer类,在遍历的
    时候计算矩阵保持拖拽器的固定大小。
    dragger->addTransformUpdating(selection);设置更新矩阵,用于更新拖拽器自身的位置。
     dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_CTRL);
        dragger-
    >setActivationKeyEvent('a');
    设置了当按住ctrl+ a的时候,拖拽器有效。
    前文分析的有些乱,下面做一个总结:
    拖拽器实现指定模型的平移、缩放、选择等操作,(拖拽器本身也是模型,根据不同拖拽方式,
    setupDefaultGeometry设置geometry),Dragger类是基类,CompositeDragger是组合拖拽器的
    基类,Dragger派生出很多子类,都是单一的功能,比如只向右移动,而我们应用的拖拽器一般
    都是组合拖拽器,比如向x,y,z三个方向移动,这就是三个Dragger的叠加,组合拖拽器就是这
    些基本拖拽器的叠加。
    拖拽器在操控指定模型的时候,通过DraggerCallback来更新实现变换。
    dragger->addTransformUpdating(selection);就把模型的MatrixTransform给了拖拽器用于更新
    。拖拽器又是怎么更新的呢?拖拽器又是怎么样改变模型姿态的呢?
    在Dragger递归的时候traverse,如果是事件遍历,调用Dragger的handle,然后判断鼠标的按下
    、释放、移动等各种状态,求交运算, dragger->handle(_pointer, ea, aa);然后调用具体的
    某个拖拽器的handle,通过Command和receive来最终实现拖拽效果。
    作者:yungis 发表于2013/4/27 7:58:37 原文链接
    阅读:2025 评论:0 查看评论
     
    [原]osglogo例子
    在osg中osgGetVersion()获取osg的版本信息。
    本例子通过Geode和Geometry创建最基本的几何体,实现logo的绘制,不进行详细的研究。
    里面的地球,通过ShapeDrawable纹理贴图,MatrixTransform的setUpdateCallback(new 
    osg::AnimationPathCallback实现自动的旋转。
    本例子MyBillboardTransform : public osg::PositionAttitudeTransform实现了朝向一直屏幕
    的效果,这里需要比较一下MyBillboardTransform、Billboard、AutoTransform朝向屏幕的实现
    方法。首先看MyBillboardTransform:
    重写computeLocalToWorldMatrix方法,计算视点坐标
    bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const
     {
                osg::Quat billboardRotation;
                osgUtil::CullVisitor* 
    cullvisitor = dynamic_cast<osgUtil::CullVisitor*>(nv);
                if (cullvisitor)
       
             {
                    osg::Vec3 eyevector = cullvisitor->getEyeLocal()-
    _position;
                    eyevector.normalize();
                    osg::Vec3 side = 
    _axis^_normal;
                    side.normalize();
                    float angle = atan2


    (eyevector*_normal,eyevector*side);
                    billboardRotation.makeRotate
    (osg::PI_2-angle,_axis);
                }
                matrix.preMultTranslate(_position);

               matrix.preMultRotate(billboardRotation);
                matrix.preMultRotate
    (_attitude);
                matrix.preMultTranslate(-_pivotPoint);
                return 
    true;
            }实现节点一直朝向屏幕
    看看Billboard
    Billboard是在CullVisitor  void CullVisitor::apply(Billboard& node)的时候也是获取视点
    坐标,调用computeMatrix来实现的,根据不同的设置方法计算computeMatrix(Matrix& 
    modelview, const Vec3& eye_local, const Vec3& pos_local) const中的modelview。
    AutoTransform也是在AutoTransform::computeLocalToWorldMatrix(Matrix& 
    matrix,NodeVisitor*)中计算computeMatrix()修改_cachedMatrix.makeRotate(_rotation);
        
    _cachedMatrix.postMultTranslate(_position);
        _cachedMatrix.preMultScale(_scale);
       
     _cachedMatrix.preMultTranslate(-_pivotPoint);这几个影响节点编号的属性来实现的。
    总结起来,他们都是在剔除的过程中通过CullVisitor的apply方法,调用各自的方法来完成朝向


    屏幕矩阵的计算。
    作者:yungis 发表于2013/4/23 7:55:50 原文链接
    阅读:645 评论:0 查看评论
     
    [原]osglogicop例子
    该例子演示了opengl中的glLogicOp功能,指定不同的逻辑运算实现当前颜色和帧缓冲的颜色计
    算。有以下几种参数
    GL_CLEAR 0
    GL_SET 1
    GL_COPY s
    GL_COPY_INVERTED ~s
    GL_NOOP d
    GL_INVERT ~d
    GL_AND s & d
    GL_NAND ~(s & d)
    GL_OR s | d
    GL_NOR ~(s | d)
    GL_XOR s ^ d
    GL_EQUIV ~(s ^ d)
    GL_AND_REVERSE s & ~d
    GL_AND_INVERTED ~s & d
    GL_OR_REVERSE s | ~d
    GL_OR_INVERTED ~s | d
    s代表当前颜色,d代表帧缓冲中的颜色。前面是glLogicOp中可以接受的参数,后面是运算结果
    。在osg中对应LogicOp类,它继承StateAttribute,属于状态属性的一种。在类的内部定义了以
    上参数的枚举。
    回到例子中,定义了 osg::LogicOp*   logicOp =   new osg::LogicOp
    (osg::LogicOp::OR_INVERTED);并且 stateset->setAttributeAndModes
    (logicOp,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);启用属性和模式。
    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);设置绘制优先级。
    setRenderingHint和setRenderBinDetails都可以设置节点绘制的顺序,在osg中根据他们参数的
    数值进行节点渲染顺序的配置,达到可以控制节点渲染顺序的功能。
    接下来通过TechniqueEventHandler来控制当前logicOp 使用的逻辑运算符。
    作者:yungis 发表于2013/4/22 7:36:58 原文链接
    阅读:371 评论:0 查看评论
     
    [原]osglightpoint例子
    该例子演示了光点的效果,主要应用osgSim库中的LightPoint、LightPointNode、
    SequenceGroup、BlinkSequence,osgSim库属于仿真库,扩展库。应用osg核心库完成一些指定
    的效果。因此研究这个例子只需要指定以上这几个类的作用即可。
    LightPoint是光点类,有如下属性:
    bool                        _on;
            osg::Vec3                   _position;
           
     osg::Vec4                   _color;
            float                       _intensity;
          float                       _radius;
            osg::ref_ptr<Sector>        

    _sector;
            osg::ref_ptr<BlinkSequence> _blinkSequence;
            BlendingMode      
              _blendingMode;
    是否打开、位置、颜色、强度、半径、扇区、闪烁、模式
    从以上的属性可以指定,这个光点可以调整大小位置,可以运动可以变换颜色,闪烁效果。
    而LightPointNode是光点节点,里面保存了一个光点列表typedef std::vector< LightPoint > 
    LightPointList;
    SequenceGroup用于关联一组序列,内部只有一个基本时刻double      _baseTime;
    BlinkSequence闪烁序列,内部的属性:double                      _pulsePeriod;
           
    double                      _phaseShift;
            PulseData                   
    _pulseData;
            osg::ref_ptr<SequenceGroup> _sequenceGroup;从中可以看出可以添加很
    多脉冲,每个脉冲的间隔、停顿等。它属于LightPoint光点的一个属性,也就说一个光点可以以
    SequenceGroup定义的基本时间为基本仿真时间,根据BlinkSequence中设置的变换颜色和光点强
    度和脉冲。
    明白了以上几个类之间的关系,这个例子就很好理解了。
    在createLightPointsDatabase函数中创建了很多光点,设定了位置和颜色的变换范围,里面有
    一个:
    lpn->setPointSprite设置了光点添加纹理使用模糊的效果,必须指定0纹理单元(后面研究实现
    方法)。
    CreateBlinkSequenceLightNode函数创建了闪烁的光点,设置序列组,添加脉冲,设置强度位置
    等等。
    我们详细的研究一下LightPointNode,说是光点节点,但是它本身不发光,但可以通过其他方式
    模拟出发光的效果。这个节点很特别,在osg中节点组成场景树,通过剔除遍历场景树构建状态
    树和渲染树,如果让我们自己实现这个LightPointNode节点,我们想到的方式可能是
    LightPointNode : public osg:Geode让他继承geode,然后adddrawable把光点的drawable添加
    进去进行渲染。
    而实际中LightPointNode并没有采用这种方法,而是继承Node,并且没有add任何的子节点。所
    有的的功能都是在traverse递归的时候实现的。
    这就涉及到了如何跳过场景树去绘制节点,答案是在剔除的时候去手动构建状态树,我们进入代
    码看看是怎么样手动构建的。
    首先 osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);用于判断只
    有在剔除遍历的时候才继续运行下面的代码,
    osg::Matrix matrix = *(cv->getModelViewMatrix());
            osg::RefMatrix& projection 
    = *(cv->getProjectionMatrix());
            osgUtil::StateGraph* rg = cv-
    >getCurrentStateGraph();
            if (rg->leaves_empty())
            {
                // this 
    is first leaf to be added to StateGraph
                // and therefore should not 
    already know current render bin,
                // so need to add it.
                cv-
    >getCurrentRenderBin()->addStateGraph(rg);
            }
    获取模型视图矩阵、获取投影矩阵、获取当前的渲染根节点。
    typeid(*object)==typeid(LightPointDrawable)
    用于判断object是否是(LightPointDrawable)类型的,如果是返回true否则返回false。
    接下来 drawable = _pointSprites ? new LightPointSpriteDrawable : new 
    LightPointDrawable;
    这里我们看到了_pointSprites ,这就是是否让LightPointNode使用纹理,如果使用纹理则new 
    LightPointSpriteDrawable否则new LightPointDrawable。并且把这个drawable设置成了当前的
    userdata。
    接下来把这个drawable收到的添加到rg->addLeaf(new osgUtil::RenderLeaf
    (drawable,&projection,NULL,FLT_MAX));渲染叶中,到目前为止需要注意,这个drawable中还
    没有任何内容,接下来就需要根据_lightPointList去向这个drawable添加绘制的内容,注意添
    加addBlendedLightPoint和addAdditiveLightPoint。
    现在我们进入LightPointDrawable中一看究竟,LightPointDrawable继承Drawable,需实现
    drawImplementation接口,关于drawImplementation我们会在不久的以后进行详细的研究。
    这里根据_sizedOpaqueLightPointList、_sizedBlendedLightPointList、
    _sizedAdditiveLightPointList中的内容进行了绘制,在这里面看到了状态的切换,看到了
    opengl的代码。
    再补充一下,LightPointDrawable中没有应用模糊纹理,因此state.applyTextureMode
    (0,GL_TEXTURE_1D,false);
        state.applyTextureMode(0,GL_TEXTURE_2D,false);
    而看看LightPointSpriteDrawable,state.applyTextureMode(0,GL_TEXTURE_2D,true);这里应
    用了纹理,这就是两者差别的体现。
    研究完了这一趟,似乎触及到了osg中核心的一些东西。至于我们刚才提出的问题为什么没有把
    他设计成Geode,而是继承Node,接下来大家一起思考。
    作者:yungis 发表于2013/4/19 7:43:24 原文链接
    阅读:510 评论:0 查看评论
     
    [原]osglight例子
    先列上两个链接介绍osg中的矩阵变换。
    http://www.cnblogs.com/indif/archive/2011/05/13/2045106.html
    http://www.cppblog.com/acmiyou/archive/2009/08/24/94292.html
    opengl中矩阵是列主序,osg中是行主序。
    个人觉得这样从应用上去考虑原因,opengl是最低层低级的软件和硬件的接口,考虑的是高效性
    能,osg是高级三维引擎,考虑的是应用和便于理解。opengl中的矩阵使一个16大小的一维数组
    ,因此在进行矩阵运算的时候只需要指针++就可以到下一个。
    而实际的应用中,我们一般理解点是行主序的三个数,
     osg当中:  newpos  =oldpos * T *R    //先执行平移 后执行旋转    (全局坐标系)
    因此执行以上的变换,先平移后旋转,很容易理解。
    在来说一下MatrixTransform 和PositionAttitudeTransform,一个是矩阵变换一个是位置姿态
    ,MatrixTransform 是按着矩阵相乘的顺序做变换,而PositionAttitudeTransform是按着SRT的
    顺序变换,所以对于PositionAttitudeTransform无论是先设置位置旋转缩放还是后设置结果都
    一样。我们从源代码中看个究竟。
    对于MatrixTransform 
    bool MatrixTransform::computeLocalToWorldMatrix(Matrix& matrix,NodeVisitor*) const
    {
      
      if (_referenceFrame==RELATIVE_RF)
        {
            matrix.preMult(_matrix);
        }
        
    else // absolute
        {
            matrix = _matrix;
        }
        return true;
    }
    直接做了矩阵的左乘,即_matrix*matrix
    而PositionAttitudeTransform
    bool PositionAttitudeTransform::computeLocalToWorldMatrix(Matrix& 

    matrix,NodeVisitor*) const
    {
        if (_referenceFrame==RELATIVE_RF)
        {
            
    matrix.preMultTranslate(_position);
            matrix.preMultRotate(_attitude);
            
    matrix.preMultScale(_scale);
            matrix.preMultTranslate(-_pivotPoint);
        }
        
    else // absolute
        {
            matrix.makeRotate(_attitude);
            
    matrix.postMultTranslate(_position);
            matrix.preMultScale(_scale);
            
    matrix.preMultTranslate(-_pivotPoint);
        }
        return true;
    }
    则与顺序无关,完全是按着_scale*_attitude*_position*matrix
    而_pivotPoint指定了变换轴,最终把节点移动到变换轴处。
    关于RELATIVE_RF和ABSOLUTE_RF我们前面的例子介绍过。
    我们回到例子中:
    createRoom函数中创建了一个正方体,里面有个
    osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform();
    pat添加了ModelTransformCallback根据仿真时间重新计算姿态。       pat->setPivotPoint
    (loaded_bs.center());
    有了上面的介绍我们就很容易理解了,设置节点的包围球中心为轴心。
    osg::StateSet* wall = new osg::StateSet;
        wall->setMode
    (GL_CULL_FACE,osg::StateAttribute::ON);
    设置GL_CULL_FACE打开,即节点的正面被裁切,实现朝着观察者的面不绘制的效果。
    createWall绘制一面墙
    osgUtil::SmoothingVisitor::smooth(*geom);自动计算了法向量。
    createLights创建灯。
    我们着重研究一下这个函数
    LightSource继承Group,是场景中的光源节点,保存了一个ref_ptr<Light>                  
    _light;Light是具体的光照类,内部保存了如下属性:
    int _lightnum;                           // OpenGL light number
            Vec4 

    _ambient;                           // r, g, b, w
            Vec4 _diffuse;              

                 // r, g, b, w
            Vec4 _specular;                          // r, g, 

    b, w
            Vec4 _position;                          // x, y, z, w
            Vec3 

    _direction;                         // x, y, z
            float _constant_attenuation;   

              // constant
            float _linear_attenuation;               // linear
          
      float _quadratic_attenuation;            // quadratic
            float _spot_exponent; 

                       // exponent
            float _spot_cutoff;                      // 

    spread
    一个光源的基本属性颜色衰减等,_lightnum是与opengl的光源id相对于,默认为0。
    在createLights这个函数中创建了两个光源,把一个点和一个光源添加到MatrixTransform中,
    这样模拟了点就是光源的效果,MatrixTransform中添加了AnimationPathCallback一个动画,
    光源AnimationPathCallback我们之前的例子中研究过,很按着指定的路径运动。
    最后一点    viewer.getCamera()->setCullingMode( viewer.getCamera()->getCullingMode() 
    & ~osg::CullStack::SMALL_FEATURE_CULLING);
    去除了细节裁剪,这样在场景剔除的时候就不会用到SMALL_FEATURE_CULLING。
    作者:yungis 发表于2013/4/17 7:54:12 原文链接
    阅读:615 评论:1 查看评论
     
    [原]osglauncher例子
    该例子通过读取osg.conf文件,把一些图片路径和应用程序路径读取到了Xample里面。通过
    setupGraph函数把每个图片添加到窗体中,PickHandler继承GUIEventHandler,获取鼠标所选图
    片对应的应用程序名称,同时更新_updateText(显示图片的名称),单击后运行响应的应用程
    序。这里用了 system();指定指定的程序。
    作者:yungis 发表于2013/4/12 7:37:23 原文链接
    阅读:343 评论:0 查看评论
     
    [原]osgkeyboardmouse例子
    本例子演示了拾取的功能。
    PickHandler继承GUIEventHandler
    ‘s’键通过CreateModelToSaveVisitor把选中的节点写出。
    virtual void apply(osg::Node& node)
        {
            osgFX::Scribe* scribe = 
    dynamic_cast<osgFX::Scribe*>(&node);
            if (scribe)
            {
                for
    (unsigned int i=0; i<scribe->getNumChildren(); ++i)
                {
                    
    _group->addChild(scribe->getChild(i));
                }
            }
            else
            {
       
            traverse(node);
            }
        }
    ‘o’键写出了整个场景。
    ‘p’键切换拾取的方式。
    在例子中用了两种求交的方式:1、PolytopeIntersector;指定一个矩形区域求交,构造函数
    PolytopeIntersector(CoordinateFrame cf, double xMin, double yMin, double xMax, 
    double yMax);通过cf指定了一种坐标参考,后面的参数指定了一个矩形。
     enum CoordinateFrame
            {
                WINDOW,
                PROJECTION,
                
    VIEW,
                MODEL
            };
    关于这几种方式:MODEL方式表示使用世界坐标,传入参数也就直接是世界坐标;WINDOW、
    PROJECTION和VIEW都是传入屏幕/投影窗口/视口坐标作为参数,并且设法在求交过程中将它们转
    换为世界坐标。如果希望使用WINDOW等方式,需要节点是相机节点。我们进入代码中看看这几种
    方式的实现:
    polytopeintersector.cpp中,
    osg::Matrix matrix;
        switch (_coordinateFrame)
        {
            case(WINDOW):
     if (iv.getWindowMatrix()) matrix.preMult( *iv.getWindowMatrix() );
                if 

    (iv.getProjectionMatrix()) matrix.preMult( *iv.getProjectionMatrix() );
                
    if (iv.getViewMatrix()) matrix.preMult( *iv.getViewMatrix() );
                if 
    (iv.getModelMatrix()) matrix.preMult( *iv.getModelMatrix() );
                break;
          
      case(PROJECTION):
                if (iv.getProjectionMatrix()) matrix.preMult( 

    *iv.getProjectionMatrix() );
                if (iv.getViewMatrix()) matrix.preMult( 

    *iv.getViewMatrix() );
                if (iv.getModelMatrix()) matrix.preMult( 

    *iv.getModelMatrix() );
                break;
            case(VIEW):
                if 

    (iv.getViewMatrix()) matrix.preMult( *iv.getViewMatrix() );
                if 


    (iv.getModelMatrix()) matrix.preMult( *iv.getModelMatrix() );
                break;
          
      case(MODEL):
                if (iv.getModelMatrix()) matrix = *iv.getModelMatrix();
        
            break;
        }
    无论是哪一种方式,最终都转换到了世界坐标。
    2、LineSegmentIntersector;使用构造函数LineSegmentIntersector(CoordinateFrame cf, 
    double x, double y);指定一点求交。
    所以为了更精确的球交,应使用osgUtil::Intersector::WINDOW窗口坐标,viewer->getCamera
    ()->accept(iv);
    toggleScribe函数就是使用osgFX::Scribe实现框体选中的效果。
    作者:yungis 发表于2013/4/11 7:05:25 原文链接
    阅读:499 评论:0 查看评论
     
    [原]osgkeyboard例子
    本例子演示了键盘事件,比较简单。
    通过KeyboardModel类绘制出一个键盘,KeyboardEventHandler继承GUIEventHandler,实现键盘
    事件的处理。
    作者:yungis 发表于2013/4/11 6:18:42 原文链接
    阅读:313 评论:0 查看评论
     
    [原]osgkdtree例子
    本例子演示了KDTree,实际上没什么内容。我们就在这没内容的几行代码中挖一挖。
    关于KDTree的介绍http://www.cnblogs.com/eyeszjwang/articles/2429382.html,在这里可以
    看看。我们不研究算法,只研究代码和实现。
    KDTree为场景的求交提供了一种更加有效率、更加精确的算法。
    例子中只有一句和KDTree有关的osgDB::Registry::instance()->setBuildKdTreesHint
    (osgDB::ReaderWriter::Options::BUILD_KDTREES);在Registry中注册构建KDTree。我们进入
    Registry中,构造函数:
    _buildKdTreesHint = Options::NO_PREFERENCE;
        _kdTreeBuilder = new 
    osg::KdTreeBuilder;
        
        const char* kdtree_str = getenv("OSG_BUILD_KDTREES");

    if (kdtree_str)
        {
            bool switchOff = (strcmp(kdtree_str, "off")==0 || 

    strcmp(kdtree_str, "OFF")==0 || strcmp(kdtree_str, "Off")==0 );
            if 

    (switchOff) _buildKdTreesHint = Options::DO_NOT_BUILD_KDTREES;
            else 
    _buildKdTreesHint = Options::BUILD_KDTREES;
        }
    从这里可以看出KdTreeBuilder是构建KDTree的地方,首先,KDTree没有指定,读取环境变量,
    如果环境变量中没有off字样的就设置Options::BUILD_KDTREES;可见在osg默认是构建了KDTree
    的。顺着这些信息看看,KDTree是在什么地方构建的?
    在DatabasePager中我们可以看到如下的代码:
    _kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone();
    virtual void apply(osg::Geode& geode)
        {
            StateToCompile::apply(geode);

      if (_kdTreeBuilder.valid())
            {
                geode.accept(*_kdTreeBuilder);
       }
        }
    他们都在FindCompileableGLObjectsVisitor这个类中,DatabasePager分页数据库,管理
    PagedLOD和ProxyNode的,里面存放了数据请求列表、数据等待删除列表、数据等待编译列表、
    数据等待融合场景列表、而这个类就是查找需要编译的数据。(具体内容看《最长的一帧》)
    KDTree就是在这里构建的。
    KdTreeBuilder是构建的关键类,不用说继承自NodeVisitor,要遍历场景的。里面只有void 
    apply(osg::Geode& geode);因为它只对几何体感兴趣。
    KDTree的构建把我们带到了KDTree这个类中,具体的算法不做分析了,KDTree的用法在上次求交
    的例子中有了应用,求交更加精确。
    Registry是一个单例,在程序的运行中只有一个对象,因此这个的程序中又有一个
    KdTreeBuilder。
    作者:yungis 发表于2013/4/9 7:36:45 原文链接
    阅读:1030 评论:0 查看评论
     
    [原]osgintersection例子
    先说一下osgSim库,它提供虚拟仿真效果的节点工具,用于特殊效果。
    这个例子中涉及到了osgSim::LineOfSight,我们就来看看这个类是干什么,从字面上可知,它
    是视线、瞄准线。
    DatabaseCacheReadCallback这个类继承ReadCallback,在相交的测试中,场景可能有PagedLOD
    ,而计算相交过程中,PagedLOD不是精度最高的节点(如地球),这样计算的就不准确,这个
    ReadCallback类就保证了在IntersectionVisitor::apply(osg::PagedLOD& plod)时候,加载最
    高精度的PagedLOD,从而保证计算的精度。但加载节点是比较耗时的。而这个
    DatabaseCacheReadCallback是一个缓存数据库,把加载过的PagedLOD缓存下来,下次直接从缓
    存中加载。readNodeFile是主要的函数。
    LineOfSight类建立和地球的相交线。通过LOS结构存储一个起点和一个终点,把和场景的交点保
    存在Intersections中。在内部保存一个osgUtil::IntersectionVisitor            
    _intersectionVisitor;进行相交测试。
    computeIntersections是相交计算的主要函数,IntersectorGroup存放了一组相交的线段,之前
    我们提到过,在osg中有多种相交的计算(线、面、体),线与场景相交最为常见。
    接下来:
    _intersectionVisitor.reset();
        _intersectionVisitor.setTraversalMask

    (traversalMask);
        _intersectionVisitor.setIntersector( intersectorGroup.get() );
    相交访问器重置,设置掩码,添加相交的数据Intersector,Intersector有四个子类,用于和场
    景求交。
     scene->accept(_intersectionVisitor);
    通过这个函数遍历场景计算相交的点。在IntersectionVisitor中,inline void intersect
    (osg::Drawable* drawable) { _intersectorStack.back()->intersect(*this, drawable); }
    是具体的求交的方法,具体的算法隐藏在子类中,线段相交的算法在
    LineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* 
    drawable)中,具体的实现算法我们不做研究,从中可以看到KdTree,这个类为场景的求交提供
    了更加精确的工具,之后我们马上就会研究这个东西。
    接下来,就可以获取相交的成果了,
     osgUtil::LineSegmentIntersector::Intersections& intersections = lsi-
    >getIntersections();
                
                for
    (osgUtil::LineSegmentIntersector::Intersections::iterator itr = intersections.begin
    ();
                    itr != intersections.end();
                    ++itr)
                {
       
                const osgUtil::LineSegmentIntersector::Intersection& intersection = 
    *itr;
                    if (intersection.matrix.valid()) intersectionsLOS.push_back( 
    intersection.localIntersectionPoint * (*intersection.matrix) );
                    else 
    intersectionsLOS.push_back( intersection.localIntersectionPoint  );
                }
    从中获取相交的点。
    这里我们不妨思考一下,在osg中为什么有那么多的visitor和callback存在呢?无论什么功能都
    使用的visitor,我想osg作为一种非常成熟的渲染引擎,必须从构架上清晰,这些visitor都是
    osg的扩展功能,不能影响总体架构。更重要是是osg采用的是树状结构构建场景、进行渲染的,
    通过这个visitor很容易实现递归遍历场景,这就是访问者模式应用之所在,而callback提供了
    一种扩展的接口。而且在visitor过程中存在很多成对的东西,如:push/pop、在求交访问器中
    存在enter/leave,这就是访问者进入-离开,没有改变场景。访问者模式的缺点也就清除了,他
    必须重载所有的节点类,如果osg中有新的节点类型添加,改动会很大。
    离题万里了,回到例子中,看看HeightAboveTerrain,功能也类似,求的是和地形的交点,这个
    地形就具有具体的空间参考了。
    接下来,求出求交时间,打印交点。
    ElevationSlice求的面与地形的交点。
    else if中没有应用osg封装的类,直接使用LineSegmentIntersector求交,当然,这样只会对当
    前场景进行计算,如果有PagedLOD,则不会加载,然后打印出结果。
    作者:yungis 发表于2013/4/8 7:55:42 原文链接
    阅读:1503 评论:0 查看评论
     
    [原]osgimpostor例子
    本例子演示了impostor代替节点的应用。
    以下是转自网上翻译源码的内容:
    Impostor是一种LOD组节点,它既允许通过子结点到视点的距离来选择相应的子结点,又可以通
    过到视点的距离来选择图片缓存。Impostor的原理是:通过缓存几何体(realgeometry)的图像
    ,然后在之后的帧内使用该图像代替该几何体。这有点像Billboard,但是, 它是实时更新并与
    视点的位置相关的。通过映射了该纹理的四边形,可以降低场景的复杂度以提高性能。OSG中的
    impostor并不是完全按照上述网址中的技术来实现的,但是这应该是个不错的开始。OSG的
    Impostor结点远没有那么复杂,因为使用时你并不需要重构(重新组织)你的整个场景。你所要
    做的就是为Impostor结点的每一个子结点设置相应的LOD可见范围值(visiblerange value),
    并且设置一个Impostor阈值,来告诉渲染器(renderer)在什么距离以外应该使用Impostor的图
    像缓存。osg::CullVisitor会自动处理所有的渲染前期(pre-renderingstages)的设置,计算
    出所需的ImpostorSprites(这个类封装了图像缓存与四边形),并随视点的变化更新它们。如
    果你使用osg::SceneView/CullVisitor,所有为了支持Impostor的复杂过程都会被很好的隐藏。
    关于Impostor还有很多改进计划:
    1) 估计一个ImpostorSprite在多少帧内会被重用,如果被重用的次数不会多于一个最小阈值的
    话,就不创建这个ImpostorSprite而是直接使用几何体
    2)与内存中的纹理共享数据
    3)对ImpostorSprite使用简单的3D几何体而不是Billboard
    4)减小ImpostorSprite的大小使之更适合内在的(underlying)几何体
    Impostor继承自LOD,我们都知道LOD实现在指定范围内显示物体,Switch节点可以开关节点,这
    些是怎么实现的呢,我们进入源码看看究竟。
    virtual void traverse(NodeVisitor& nv);每个类型的节点当访问它的时候,都会进行递归调
    用,而递归的关键就是traverse。
    void Group::traverse(NodeVisitor& nv)
    {
        for(NodeList::iterator 

    itr=_children.begin();
            itr!=_children.end();
            ++itr)
        {
    (*itr)->accept(nv);
        }
    }
    这是Group的递归,直接遍历所有的子节点。
    再来看看LOD的递归调用,
    for(unsigned int i=0;i<numChildren;++i)
                {    
                    if 
    (_rangeList[i].first<=required_range && required_range<_rangeList[i].second)
             
           {
                        _children[i]->accept(nv);
                    }
                }
    是的,判断了该节点是否在指定的视野内,如果不在就不进行递归调用,这样在osg中无论是更
    行回调、事件回调、裁切回调,包括自己实现的回调,都会进行判断,不在视野内就忽略。
    而在Impostor中的traverse呢,通过ImpostorSprite创建图片代替,在createImpostorSprite这
    个函数中实现具体的图片创建过程,通过帧缓存对象,把节点选择到纹理中。
    回到例子中,CreateHouses()创建了一个房子模型,加入到nodes中,LayoutAsGrid()函数通过
    代替节点加入400个模型,
    osgSim::Impostor * impostor = new osgSim::Impostor();
                impostor-
    >setImpostorThreshold(static_cast<float> (Threshold));
                impostor-
    >addChild(groups[i]);
                impostor->setRange(0, 0.0f, 1e7f);
             
    impostor->setCenter(groups[i]->getBound().center());
    设置代替节点的参数。
    TestManipulator继承CameraManipulator,测试的操作器。写自己的操作器只需重写几个类就可
    以实现,
     /** set the position of the matrix manipulator using a 4x4 Matrix.*/
            virtual 
    void setByMatrix(const osg::Matrixd& matrix);
            /** set the position of the 
    matrix manipulator using a 4x4 Matrix.*/
            virtual void setByInverseMatrix
    (const osg::Matrixd& matrix) { setByMatrix(osg::Matrixd::inverse(matrix)); 
    /** get the position of the manipulator as 4x4 Matrix.*/
            virtual osg::Matrixd 
    getMatrix() const;
            /** get the position of the manipulator as a inverse 
    matrix of the manipulator, typically used as a model view matrix.*/
            virtual 
    osg::Matrixd getInverseMatrix() const;
    最重要的是getInverseMatrix这个函数,osg中每一帧的更新回调会调用操作器的这个函数赋值
    给相机,实现相机的更新。
    virtual void setNode(osg::Node*);
    /** Move the camera to the default position. 
                May be ignored by 
    manipulators if home functionality is not appropriate.*/
            virtual void home
    (const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us);
          
            /** 
    Start/restart the manipulator.*/
            virtual void init(const 
    osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us);
            /** handle events, 
    return true if handled, false otherwise.*/
            virtual bool handle(const 
    osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us);
    这几个函数在重写的时候也经常用到,设置中心节点,初始位置、初始化,事件处理。
    作者:yungis 发表于2013/4/2 7:17:31 原文链接
    阅读:672 评论:1 查看评论
     
    [原]osgimagesequence例子
    本例子演示了图片的播放
    createModel创建geometry,createState创建stateset。
    ImageSequence继承自ImageStream,ImageStream继承自Image,实现图像流的播放控制,
    ImageSequence可以实现作为纹理贴图时纹理控制。有三种模式
    enum Mode
            {
                PRE_LOAD_ALL_IMAGES,

    PAGE_AND_RETAIN_IMAGES,
                PAGE_AND_DISCARD_USED_IMAGES
            };
    全部提前加载、用到时候加载和用完后舍去。imageSequence->play();之后就可以按着设定的模
    式播放图片。
    FindImageStreamsVisitor类把所有的ImageStream放置到_imageStreamList中,然后通过
    MovieEventHandler进行暂停、播放、循环等设置。
    作者:yungis 发表于2013/3/27 7:25:24 原文链接
    阅读:400 评论:1 查看评论
     
    [原]osghud例子
    本例子演示了HUD的功能,之前的很多例子都用到了HUD,而且实现HUD有很多的方式。其实HUD也
    是一个节点,唯一的特别之处就是从一个指定的位置观察这个节点,这个节点一直以平面的方式
    投到创建的表面,不随着场景和视点的变换而变换。
    createHUD()这个函数:
    第一步、创建个相机,它的自节点绘制到这个HUD中。
    第二步、设置投影矩阵,这个就是投影到场景的屏幕上。
    第三部、设置相对帧,setReferenceFrame(osg::Transform::ABSOLUTE_RF);camera-
    >setViewMatrix(osg::Matrix::identity());这个保证这个节点不随着场景和视点的变换而变化

    第四部、清除深度缓存camera->setClearMask(GL_DEPTH_BUFFER_BIT);
    第五步、设置渲染顺序camera->setRenderOrder(osg::Camera::POST_RENDER);这个保证了最后
    渲染,HUD一直在场景的最外面。
    第六步、camera->setAllowEventFocus(false);保证该HUD不接受事件。
    之后在HUD上添加文字和画板。
    在main函数中颜色了添加HUD的几种方式:
    第一种、因为这里的HUD载体是Camera节点,所以 osg::Camera* hudCamera = createHUD();
            // set up cameras to render on the first window available.
    hudCamera->setGraphicsContext(windows[0]);
            hudCamera->setViewport
    (0,0,windows[0]->getTraits()->width, windows[0]->getTraits()->height);
    viewer.addSlave(hudCamera, false);
    把相机设置了响应的图形上下文,作为一个附属相机添加
    到viewer中。
    第二种、这里的相机有事一个节点, group->addChild(createHUD());
    作为一个节点添加中场景中。
    这里还有两个类:
     SnapImage* postDrawCallback = new SnapImage("PostDrawCallback.png");
    viewer.getCamera()->setPostDrawCallback(postDrawCallback);        
           
    viewer.addEventHandler(new SnapeImageHandler('p',postDrawCallback));
    SnapImage* finalDrawCallback = new SnapImage("FinalDrawCallback.png");
    viewer.getCamera()->setFinalDrawCallback(finalDrawCallback);        
            
    viewer.addEventHandler(new SnapeImageHandler('f',finalDrawCallback));
    用于把HUD内容写出来,这里需要说明的是setPreDrawCallback、setPostDrawCallback、
    setFinalDrawCallback相机在绘制中有三次的回调,回之前,绘制后,最后,在
    RenderStage::draw函数中被调用。
    作者:yungis 发表于2013/3/25 7:29:24 原文链接
    阅读:831 评论:0 查看评论
     
    [原]osghangglide例子
    这个例子主要模拟了飞机视角的操作器,绘制了一些必要的场景。
    从createModel开始,ClearNode是这样的一个节点,清除颜色和深度缓冲区,并且设
    置“RenderBin”为-1。(关于RenderBin之前的例子中有说明)通过setRequiresClear设置清除颜色是否可用。这个节点比场景中的其
    他节点先绘制。在CullVisitor中的CullVisitor::apply(osg::ClearNode& node)使用,也就是
    说每一帧都调用这个函数清空颜色和深度缓冲。
    Transform是变换的基类,MoveEarthySkyWithEyePointTransform继承Transform实现场景跟着鼠
    标的变换。重写了computeLocalToWorldMatrix和computeWorldToLocalMatrix,这里用了
    getEyeLocal()这个函数获取当前的视点坐标,进入CullStack这个函数,看看这里面有存储了当
    前场景很多数据,
    inline osg::Viewport* getViewport();
            inline osg::RefMatrix* 
    getModelViewMatrix();
            inline osg::RefMatrix* getProjectionMatrix();
    inline osg::Matrix getWindowMatrix();
            inline const osg::RefMatrix* getMVPW();
    inline const osg::Vec3& getEyeLocal() const { return _eyePointStack.back(); }
    inline const osg::Vec3& getViewPointLocal() const { return _viewPointStack.back(); }
    模型矩阵、投影矩阵、MVPW矩阵等等。相机是CullStack的孙子节点,因此说相机也有这些方法
    可用。
    例子之后构建了一颗模型树。
    makeSky()绘制天空,RenderBin设置-2,makeBase()设置-1,makeTrees()绘制树,
    makeTerrain()绘制地形,makeTank()绘制坦克。
    最后viewer.setCameraManipulator(new GliderManipulator());设置了GliderManipulator这个
    操作器,写自己的操作器需要继承CameraManipulator,主要重写getInverseMatrix这个函数。
    这个操作器中的其他函数就不做深入的研究了。
    作者:yungis 发表于2013/3/25 7:02:49 原文链接
    阅读:493 评论:1 查看评论
     
    [原]osggraphicscost例子
    这个例子比较有用,平均了三维场景CPU和GPU的花费。
    进入GraphicsCostEstimator这个类,
     CostPair estimateCompileCost(const osg::Geometry* geometry) const { return 
    _geometryEstimator->estimateCompileCost(geometry); }
        CostPair estimateDrawCost
    (const osg::Geometry* geometry) const { return _geometryEstimator->estimateDrawCost
    (geometry); }
        CostPair estimateCompileCost(const osg::Texture* texture) const { 
    return _textureEstimator->estimateCompileCost(texture); }
        CostPair 
    estimateDrawCost(const osg::Texture* texture) const { return _textureEstimator-
    >estimateDrawCost(texture); }
        CostPair estimateCompileCost(const osg::Program* 
    program) const { return _programEstimator->estimateCompileCost(program); }
    CostPair estimateDrawCost(const osg::Program* program) const { return 
    _programEstimator->estimateDrawCost(program); }
        CostPair estimateCompileCost
    (const osg::Node* node) const;
        CostPair estimateDrawCost(const osg::Node* node) 
    const;
    对Geometry、Texture、Program和Node的编译和绘制都做了评估。
    typedef std::pair<double, double> CostPair;
    代表CPU和GPU所花的时间。
    GraphicsCostEstimator负责评估Geometry、Texture、Program、Node编译和绘制所用的时间。
    CollectCompileCosts这个类是计算时间的类,继承自osg::NodeVisitor,先来回忆一下
    osg::NodeVisitor,它是一个访问者模式,节点accept它,它就可以通过apply函数实现相应的
    功能。NodeCallback就是通过NodeVisitor实现,只是在没一帧的更新回调中都调用了这个
    accept递归遍历。
    我们回到这个例子,这个例子统计的时间并不是运行时统计的,而是事先定制好了一些基本的时
    间,然后通过Geometry、Texture、Program、Node中的数据量。
    看GeometryCostEstimator的setDefaults函数:
    void GeometryCostEstimator::setDefaults()
    {
        double transfer_bandwidth = 
    10000000000.0; // 1GB/sec
        double gpu_bandwidth = 50000000000.0; // 50 GB/second
       
     double min_time = 0.00001; // 10 nano seconds.
        _arrayCompileCost.set(min_time, 
    1.0/transfer_bandwidth, 256); // min time 1/10th of millisecond, min size 256
        
    _primtiveSetCompileCost.set(min_time, 1.0/transfer_bandwidth, 256); // min time 

    1/10th of millisecond, min size 256
        _arrayDrawCost.set(min_time, 

    1.0/gpu_bandwidth, 256); // min time 1/10th of millisecond, min size 256;
       
    _primtiveSetDrawCost.set(min_time, 1.0/gpu_bandwidth, 256); // min time 1/10th of 
    millisecond, min size 256;
        _displayListCompileConstant = 0.0;
        
    _displayListCompileFactor = 10.0;
    }
    通过这些基本的单位时间,计算总体的时间。
    作者:yungis 发表于2013/3/24 11:23:19 原文链接
    阅读:391 评论:0 查看评论
     
    [原]osggpx例子
    首先来看Track这个类,维护了一个TrackSegment集合。
    TrackSegment维护了一个TrackPoint集合。
    TrackPoint有经纬高和时间组成,在每一刻能唯一确定位置。
    readTrack这个函数读取了一个gpx后缀的文件。
    这个函数中涉及到了osg好些文件操作,osgDB::XmlNode是osg中对xml的处理,返回一个Track。
    接下来看createTrackModel这个函数,根据Track绘制了很多线,值得注意的是,这里Track中存
    储是的经纬高,需要通过EllipsoidModel把它转换到世界坐标,转换函数
    convertLatLongHeightToXYZ,把这些路径绘制到了地球上。
    computeAveragedSpeedTrackSegment、computeSmoothedTrackSegment这两个函数也很好理解,
    在地球上绘制两点直接的线,可能出现不平滑,地球上弧型的,因此需要在两点中进行平滑处理。第一个函数根据平均速度。
    如果outputFilename不为空,会把这个Track写出到指定文件中。
    这个例子本身并不难,应用了osg中封装的的xml处理接口,应用了EllipsoidModel这个类,这个
    类在编写数字地球的时候经常用到。
    作者:yungis 发表于2013/3/22 6:50:36 原文链接
    阅读:530 评论:0 查看评论
     
    [原]osggeometryshaders例子
    SomePoints继承Geometry,绘制了八个点,关闭了光照,设置点的大小为6.
    然后应用的了shader,添加了"u_anim1"这个变量,并且添加了SineAnimation更新回调。
    先来看createShader()这个函数,创建Program,添加了顶点着色器、片元着色器和几何着色器。
    几何着色器介绍:
     Geometry Shader Tutorials:http://appsrv.cse.cuhk.edu.hk/~ymxie/Geometry_Shader/
         Geometry Shader Concepts & Examples:          http://www.cnblogs.com/Jedimaster/archive/2007/06/26/796107.html
     pgm->setParameter( GL_GEOMETRY_VERTICES_OUT_EXT, 4 ); 着色器输出元素个数
         pgm->setParameter( GL_GEOMETRY_INPUT_TYPE_EXT, GL_POINTS );色器输入元素类型
         pgm->setParameter( GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_LINE_STRIP );着色器输出元素类型
    这是几何着色器扩展支持,输入的是点,输出的是线。
    在每个着色器中定义"#version 120 "
    "#extension GL_EXT_geometry_shader4 : enable "
    打开几个着色器
    static const char* vertSource = {
    "#version 120 "
    "#extension GL_EXT_geometry_shader4 : enable "
    "uniform float u_anim1; "
    "varying vec4 v_color; "
    "void main(void) "
    "{ "
    "    v_color = gl_Vertex; "
    "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; "
    "} "
    };
    把点的坐标轴当做颜色传递,计算点的位置。
    static const char* geomSource = {
    "#version 120 "
    "#extension GL_EXT_geometry_shader4 : enable "
    "uniform float u_anim1; "
    "varying in vec4 v_color[]; "
    "varying out vec4 v_color_out; "
    "void main(void) "
    "{ "
    "    vec4 v = gl_PositionIn[0]; "
    "    v_color_out = v + v_color[0]; "
    " "
    "    gl_Position = v + vec4(u_anim1,0.,0.,0.);  EmitVertex(); "
    "    gl_Position = v - vec4(u_anim1,0.,0.,0.);  EmitVertex(); "
    "    EndPrimitive(); "
    " "
    "    gl_Position = v + vec4(0.,1.0-u_anim1,0.,0.);  EmitVertex(); "
    "    gl_Position = v - vec4(0.,1.0-u_anim1,0.,0.);  EmitVertex(); "
    "    EndPrimitive(); "
    "} "
    };
    计算了一下颜色,每两点作为一条线
    static const char* fragSource = {
    "#version 120 "
    "#extension GL_EXT_geometry_shader4 : enable "
    "uniform float u_anim1; "
    "varying vec4 v_color_out; "
    "void main(void) "
    "{ "
    "    gl_FragColor = v_color_out; "
    "} "
    };
    把最终的颜色写入gl_framcolor中
    作者:yungis 发表于2013/3/22 6:16:15 原文链接
    阅读:485 评论:0 查看评论
  • 相关阅读:
    莫队模板
    CF600E Lomsat gelral
    JZOJ 捕老鼠
    JZOJ 4896. 【NOIP2016提高A组集训第16场11.15】兔子
    JZOJ 4895【NOIP2016提高A组集训第16场11.15】三部曲
    双端队列xLIS问题
    最大K段和
    你真的了解ES6的promise吗?
    JS对象和数组深浅拷贝总结②
    当前页码删除唯一数据后加载前一页内容
  • 原文地址:https://www.cnblogs.com/flylong0204/p/4562587.html
Copyright © 2011-2022 走看看