标签:
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\n" "#extension GL_EXT_geometry_shader4 : enable\n" 打开几个着色器 static const char* vertSource = { "#version 120\n" "#extension GL_EXT_geometry_shader4 : enable\n" "uniform float u_anim1;\n" "varying vec4 v_color;\n" "void main(void)\n" "{\n" " v_color = gl_Vertex;\n" " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" "}\n" }; 把点的坐标轴当做颜色传递,计算点的位置。 static const char* geomSource = { "#version 120\n" "#extension GL_EXT_geometry_shader4 : enable\n" "uniform float u_anim1;\n" "varying in vec4 v_color[];\n" "varying out vec4 v_color_out;\n" "void main(void)\n" "{\n" " vec4 v = gl_PositionIn[0];\n" " v_color_out = v + v_color[0];\n" "\n" " gl_Position = v + vec4(u_anim1,0.,0.,0.); EmitVertex();\n" " gl_Position = v - vec4(u_anim1,0.,0.,0.); EmitVertex();\n" " EndPrimitive();\n" "\n" " gl_Position = v + vec4(0.,1.0-u_anim1,0.,0.); EmitVertex();\n" " gl_Position = v - vec4(0.,1.0-u_anim1,0.,0.); EmitVertex();\n" " EndPrimitive();\n" "}\n" }; 计算了一下颜色,每两点作为一条线 static const char* fragSource = { "#version 120\n" "#extension GL_EXT_geometry_shader4 : enable\n" "uniform float u_anim1;\n" "varying vec4 v_color_out;\n" "void main(void)\n" "{\n" " gl_FragColor = v_color_out;\n" "}\n" }; 把最终的颜色写入gl_framcolor中 作者:yungis 发表于2013/3/22 6:16:15 原文链接
阅读:485 评论:0 查看评论
|
标签:
原文地址:http://www.cnblogs.com/flylong0204/p/4562587.html