osg实例介绍

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:osg实例介绍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 查看评论