OSG入坑之路

所謂「入坑」,只不過爲本身的不熟練找藉口而已。
如今學習OSG已經三週,跟着書、視頻作些簡簡單單的小demo而已,有時候真是感受本身無從下手。
不曉得的本身算不算是轉行,以前研究點雲數據處理,有一點點opengl的基礎,雖然說工做進入某高校,項目是某沉浸式VR系統開發,目前爲止也止有我一我的在搞!
。。。。
有些扯遠了,上述純屬瞎扯淡,毫無邏輯。。。。。。node

斷斷續續的用了大半個月的osg,給個人感受就是:出了問題不知如何下手處理!好比對某圖形節點設置狀態,可是可視化出來卻沒有本身想要的效果,程序也不報錯。
之因此會有上述煩惱,可能緣由在於本身對opengl和計算機圖形學並不深刻了解,理論上的匱乏形成了實際問題沒法有效解決,這是其一;其二,或許是我的經驗不夠,思考很少,在學校習慣了被老師同窗指點,本身缺不去深刻考慮,經驗須要積累,在之後工做中要多思考,多感悟,多總結,不怕錯
壞了,又有些扯遠了,仍是回到osg吧!記性很差,就多記錄一下本身的小收穫吧!segmentfault

1、 渲染狀態(render state)

osg中,當設置某節點的渲染狀態時,該狀態會賦予當前節點及其子節點,所以,若要實現多節點多狀態渲染時,必定注意節點之間的父子關係,最好一個節點設置一個本身想要的狀態,除非父節點及其子節點的渲染狀態同樣。
渲染狀態的管理經過osg::StateSet管理,能夠將其添加到任意的節點(node、group、geode等)和DrawAble類。如要設置渲染狀態的值,須要:數組

  1. 獲得某節點的stateset實例;
  2. 設置該實例的渲染屬性(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);  //設置渲染模式

等等等等緩存

2、 geometry和geode

顯然,geode是幾何節點,且是葉節點,geometry類管理osg中各類各樣的幾何體。
我的總結:在使用geode畫osg自帶的幾何圖形時,老是:ide

  1. 聲明geode節點
  2. 建立幾何對象
  3. 設置幾何對象的參數
  4. 申請一個osg::ShapeDrawable
  5. 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繼承關係:
參數爲0.1參數爲1!函數

自定義幾何體時:學習

  1. 申請geometry對象,自定義繪製的頂點、法向量、顏色等(若是須要的話)數組
  2. 將自定的各類數組傳遞給geode,並設置綁定方式。
  3. 設置各個頂點之間的關聯方式(也就是繪製什麼圖形)
  4. 將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繼承方式:
圖片描述圖片描述測試

3、Camera

固然,上一篇入門文章viewer::run()多多少少也和camera有關。如今算是進一步吧。
先看看camera的繼承關係圖
圖片描述spa

很明顯,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 *的。
對比:

  1. 前者的主要做用是利用外部設備等影響場景中的主相機位置以及相應事件。事件相應的主要目的是爲了修改相機參數矩陣,以實現漫遊的目的。設置漫遊的本質就是修改主相機的各類參數矩陣,並將修改後的參數返回場景中的主相機(影響的最頂層的相機節點).
  2. 後者顧名思義,就是爲了相應各類鍵盤、鼠標等外部設備事件事件,固然能夠影響主場景中的相機。其實本質上):從類的繼承關係來看,漫遊器是從事件處理類中繼承而來,若是事件處理函數我的寫得足夠好、足夠豐富,應該可以替代漫遊器的,這也是爲何二者在不少時候添加自定義類時程序並不報錯,但就是達不到預想效果的緣由。
    進一步講,漫遊器是對事件處理的更進一步的豐富,其"豐富"的主要內容應該是矩陣的自動返回操做,從而控制相機,其矩陣自動返回的主要函數是:
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)

:以上內容只是本身在比較皮毛的層面的記錄,並無過多的深刻源碼解析,內容或許有誤,懇請指正留言,不勝感激!
:有作點雲處理研究的朋友也可多多交流。

相關文章
相關標籤/搜索