轉載自:https://segmentfault.com/a/1190000010506374?utm_source=tag-newest
osg插件原理:https://blog.csdn.net/weitaming1/article/details/88954231
一、 渲染狀態(render state)
osg中,當設置某節點的渲染狀態時,該狀態會賦予當前節點及其子節點
,因此,若要實現多節點多狀態渲染時,一定注意節點之間的父子關系,最好一個節點設置一個自己想要的狀態,除非父節點及其子節點的渲染狀態一樣。
渲染狀態的管理通過osg::StateSet
管理,可以將其添加到任意的節點(node、group、geode等)和DrawAble類。如要設置渲染狀態的值,需要:
- 得到某節點的stateset實例;
- 設置該實例的渲染屬性(attribute)和模式(mode)
例:osg::StateSet *state = obj->getOrCreatStateSet() ;
其中,obj
可以是節點或Drawable實例,且:getOrCreatStateSet()
方法是在osg::Node中聲明的,這就意味着geode或group都可以使用。
state>setMode(GL_LIGHTING,osg::StateAttribute::OFF);//關閉燈光狀態 state->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);//設置紋理 //開啟shader osg::ref_ptr<osg::Program> shaderProg = new osg::Program; shaderProg->addShader(new osg::Shader(osg::Shader::VERTEX,vertexShader)); shaderProg->addShader(new osg::Shader(osg::Shader::FRAGMENT,fragShader)); state->setAttributeAndModes(shaderProg,osg::StateAttribute::ON); //設置渲染屬性和模式 ->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);//管理深度測試 //設置渲染順序,第一個參數越小,渲染越靠前,默認第一個參數為 -1 state->setRenderBinDetails(10, "RenderBin"); //默認渲染排序 state->setRenderBinDetails(100,"DepthSortedBin"); //由遠到近 state->setRenderBinDetails(1000,"TraversalOrderBin"); //按遍歷順序 //開啟混合透明度 state->setMode(GL_BLEND,osg::StateAttribute::ON); //設置渲染模式
等等等等
二、 geometry和geode
顯然,geode是幾何節點,且是葉節點,geometry類管理osg中各種各樣的幾何體。
個人總結:在使用geode畫osg自帶的幾何圖形時,總是:
- 聲明geode節點
- 創建幾何對象
- 設置幾何對象的參數
- 申請一個osg::ShapeDrawable
- geode->addDrawable
列子:
//畫個圓柱 osg::TessellationHints *hins = new osg::TessellationHints; hins->setDetailRatio(1.0f);//設置圓柱的精度為0.1,值越小,精度越小 osg::ref_ptr<osg::Cylinder> cy = new osg::Cylinder; //圓柱 osg::ref_ptr<osg::ShapeDrawable> sd = new osg::ShapeDrawable(cy); //直接用幾何對象初始化shapedrawable實例 cy->setCenter(osg::Vec3(400.0,300,0)); cy->setHeight(0.1); cy->setRadius(150); sd->setTessellationHints(hins); geode->addDrawable(sd); //必不可少(到這里才真正繪制) /*以上啰嗦代碼當然可以這樣寫:*/ geode->addDrawable(new osg::ShapeDrawable(osg::Cylinder(center),Radius,Height),teseel); //畫個盒子 osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; //設置精度 hints->setDetailRatio(0.1); osg::ref_ptr<osg::ShapeDrawable> shape = new osg::ShapeDrawable(new osg::Box(osg::Vec3(x,y,z),長,寬,高),hints.get()); geode->addDrawable(shape);
TessellationHints(精度)參數對圓柱幾何的影響以及shapedrawable繼承關系:!
自定義幾何體時:
- 申請geometry對象,自定義繪制的頂點、法向量、顏色等(如果需要的話)數組
- 將自定的各種數組傳遞給geode,並設置綁定方式。
- 設置各個頂點之間的關聯方式(也就是繪制什么圖形)
- 將geometry對象添加到geode->addDrawable(geometry);
在第三步驟中,geom->addPrimitiveSet();函數需要一個Drawarrays指針和原始點的連接方式
osg::ref_ptr<osg::Geode> geo = new osg::Geode; osg::ref_ptr<osg::Geometry> geom = new osg::Geometry(); osg::ref_ptr<osg::Vec3Array> vex = new osg::Vec3Array; osg::ref_ptr<osg::Vec4Array> color = new osg::Vec4Array; osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array; osg::ref_ptr<osg::LineWidth> width = new osg::LineWidth; //線寬 //設置法向量及其綁定方式 geom->setNormalArray(normal,osg::Array::Binding::BIND_OVERALL); normal->push_back(osg::Vec3(0.0,-1.0,0.0)); //設置頂點 vex->push_back(osg::Vec3(-10.5,5,-10.0)); vex->push_back(osg::Vec3(10.5,5,-10.0)); vex->push_back(osg::Vec3(10.0,5,10.0)); vex->push_back(osg::Vec3(-10.0,5,10.0)); geom->setVertexArray(vex.get()); //設置顏色 color->push_back(osg::Vec4(0.1,0.2,0.3,0.5)); color->push_back(osg::Vec4(1.1,0.9,0.3,0.50)); color->push_back(osg::Vec4(0.2,0.5,0.3,0.50)); color->push_back(osg::Vec4(0.4,0.2,0.7,0.50)); geom->setColorArray(color); geom->setColorBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX); //設置紋理綁定方式 osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; hints->setDetailRatio(0.1); //設置透明度 geom->getOrCreateStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON); /*osg::DrawArrays相當於對opengl中glDrawarray的封裝*/ geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::Mode::LINE_LOOP,0,4)); //設置頂點的關聯方式(此處,以線段的方式連接) //設置線寬 width->setWidth(20.0); geom->getOrCreateStateSet()->setAttributeAndModes(width); geo->addDrawable(geom.get());
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::Mode::LINE_LOOP,0,4));
的基本圖元及drawable繼承方式:
三、Camera
當然,上一篇入門文章viewer::run()多多少少也和camera有關。現在算是進一步吧。
先看看camera的繼承關系圖
很明顯,camera繼承自node、group、transform,也就是說他們具有的屬性,camera也具有。
1. 裁剪面
就像上篇文章所述,osgviewer->run()會自動設置一個場景漫游器,該漫游器包含了透視投影矩陣、視口大小、屏幕寬高比以及遠近裁剪面、攝像機位置等等參數,如果想要修改遠近裁剪面應該首先關閉osg的自動判斷遠近裁剪面的函數:
viewer->getCamera()->setComputeNearFarMode(osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR); viewer->getCamera()->setProjectionMatrixAsPerspective(fovy,aspectRatio,zNear,zFarSurface);
osgUtil::CullVisitor
是osg的場景揀選訪問器。
2. 獲取自己設備的圖形環境、HUD、RTT:
osg::GraphicsContext::WindowingSystemInterface *wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsi) return ; unsigned int height,width; wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0),width,height); //獲取屏幕的長寬 //設置圖形環境特性 osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(); traits->x = 0; traits->y = 0; traits->width = width; traits->height = height; traits->windowDecoration = false; //窗口修飾 關 traits->doubleBuffer = true; //是否支持雙緩存 traits->sharedContext = false; osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits); if (!gc.valid()) { return ; } gc->setClearMask(GL_COLOR_BUFFER_BIT); gc->setClearColor(osg::Vec4(0.5,0.5,0.5,1.0)); //設置全局上下文的背景色 osg::ref_ptr<osg::Camera> master = new osg::Camera; master->setGraphicsContext(gc); viewer->addSlave(master); //將相機添加到場景中。
在HUD和RTT中,創建相機是非常重要的,HUD的相機要最后渲染,防止被覆蓋
HUD相機:
osg::Camera *CreatTextHUD()
{
osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewMatrix(osg::Matrix::identity()); //設置視圖矩陣為單位矩陣 camera->setAllowEventFocus(false); //不響應其他鼠標事件 camera->setRenderOrder(osg::Camera::POST_RENDER);//最后渲染 camera->setClearMask(GL_DEPTH_BUFFER_BIT); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);//設置參考幀 為絕對坐標系 camera->setProjectionMatrixAsOrtho2D(0,1024,0,768); //設置二維正交投影 osg::ref_ptr<osg::Geode> geode = new osg::Geode; geode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);//關閉燈光狀態 osg::ref_ptr<osgText::Text> text = new osgText::Text; geode->addDrawable(text); text->setPosition(osg::Vec3(0,0,0)); text->setFont("simsun.ttc"); //設置為宋體 text->setText(L"宋體 測試"); //一定不要忘記加“L”字符 text->setCharacterSize(50); camera->addChild(geode); return camera.release(); }
RTT相機
void CreatRTT(osgViewer::Viewer *viewer)
{
osg::ref_ptr<osg::Group> group = new osg::Group; osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("nathan.osg"); group->addChild(node); //設置圖形上下文 osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->x = 0; traits->y = 0; traits->width = 800; traits->height = 600; traits->sharedContext = false; traits->doubleBuffer = true; traits->windowDecoration = false; osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits); //創建主相機 osg::ref_ptr<osg::Camera> master = new osg::Camera; master->setGraphicsContext(gc); master->setViewport(0,0,800,600); viewer->addSlave(master); //創建rtt相機 osg::ref_ptr<osg::Camera> rttCamera = new osg::Camera; rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); rttCamera->setGraphicsContext(gc); rttCamera->setViewport(0,0,800,600); rttCamera->addChild(node); //添加相機並設置瞄准鏡的放大倍數為8倍,最后false表示:該添加入的相機不接受主窗口任何內容。 viewer->addSlave(rttCamera,osg::Matrix::scale(8,8,8),osg::Matrix::identity(),false); osg::Texture2D *t2d = new osg::Texture2D; t2d->setInternalFormat(GL_RGBA); rttCamera->attach(osg::Camera::COLOR_BUFFER,t2d); group->addChild(CreatHUD(t2d)); viewer->setSceneData(group); return ; }
上邊的函數其實是創建了一個瞄准鏡的效果:
自我感覺(或許並不對,僅限於目前認知水平)
:osg添加多相機主要功能是為了多窗口多視圖顯示。一個相機能夠渲染多個視圖,一個場景能夠添加多個相機,多個相機能夠實現從不同角度、方位、視角觀察同時觀察同一個模型。
3. 兩個小函數:
viewer->setCameraManipulator(osgGA::CameraMaipulator *)
,和viewer->addEventHandler(osgGA::EventHandler *)
,前者的函數參數是osgGA::CameraMaipulator *
類型,后者參數為osgGA::EventHandler *
類型,且osgGA::CameraMaipulator *
是繼承自osgGA::EventHandler *
的。
對比:
- 前者的主要作用是利用外部設備等影響場景中的主相機位置以及相應事件。事件相應的主要目的是為了修改相機參數矩陣,以實現漫游的目的。
設置漫游的本質就是修改主相機的各種參數矩陣,並將修改后的參數返回場景中的主相機
(影響的最頂層的相機節點). - 后者顧名思義,就是為了相應各種鍵盤、鼠標等外部設備事件事件,當然可以影響主場景中的相機。
其實本質上)
:從類的繼承關系來看,漫游器是從事件處理類中繼承而來,如果事件處理函數個人寫得足夠好、足夠豐富,應該能夠替代漫游器的,這也是為什么兩者在很多時候添加自定義類時程序並不報錯,但就是達不到預想效果的原因。
進一步講,漫游器是對事件處理的更進一步的豐富,其"豐富"的主要內容應該是矩陣的自動返回操作
,從而控制相機,其矩陣自動返回的主要函數是:
virtual void setByMatrix(const osg::Matrixd& matrix) = 0; //設置相機的位置姿態矩陣 virtual void setByInverseMatrix(const osg::Matrixd& matrix) = 0; //設置相機的視圖矩陣 virtual osg::Matrixd getMatrix() const = 0; //獲取相機的姿態矩陣 virtual osg::Matrixd getInverseMatrix() const = 0;
這也就是為什么自定義的漫游器必須重載這四個函數的原因所在。(事件處理不需要重載這幾個函數)
關系:
如果總結為一句話,應該是:CameraManipulator是EventHanler的子類,EventHandler本身也可以實現漫游功能,只不過osg開發者為了更加豐富便捷的控制事件與漫游,從而單獨設計了一個漫游操作器,以便使用者能夠更加輕松方便的控制自己的場景。
清除camera中的深度緩存:camera->setclearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH)
;
注:以上內容只是自己在比較皮毛的層面的記錄,並沒有過多的深入源碼解析,內容或許有誤,懇請指正留言,不勝感激!
另:有做點雲處理研究的朋友也可多多交流。