以下内容來自:
1、《OpenSceneGraph三維渲染引擎程式設計指南》肖鵬 劉更代 徐明亮 清華大學出版社
2、《OpenSceneGraph三維渲染引擎設計與實踐》王銳 錢學雷 清華大學出版社
3、自己的總結
OSG中類的繼承關系等見OSG學習:OSG組成(二)——場景樹。
下載下傳完整工程OSG_4_GeometryHouse
建立C++項目後,首先需要配置OSG環境,具體步驟看OSG學習:WIN10系統下OSG+VS2017編譯及運作第六步:建立OSG項目測試。
繪制一個人字頂的簡易房屋:
// stdafx.h
#include <osg/Geode>
#include <osg/Geometry>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
//osgUtil工具類庫,提供通用的共用類,用于操作場景圖形及内容,如更新、裁剪、周遊、資料統計及場景圖的一些優化。包括Delaunay三角面繪制功能、法線生成功能等。
//SmoothingVisitor 生成法線
#include <osgUtil/SmoothingVisitor>
#include <osg/Texture2D>
//.cpp
#include "stdafx.h"
/*建立房屋牆體部分
由于房屋為人字頂,是以由10個頂點組成,每個頂點都有對應的法線和紋理坐标,以便正确地實作光照和紋理貼圖效果
使用QUAD_STRIP的方式将頂點連接配接為四邊形條帶圖元
*/
osg::Drawable *createHouseWall()
{
//建立頂點數組,逆時針添加
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
//添加資料
vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); //0
vertices->push_back(osg::Vec3(0.0, 0.0, 0.0)); //1
vertices->push_back(osg::Vec3(6.0, 0.0, 4.0)); //2
vertices->push_back(osg::Vec3(6.0, 0.0, 0.0)); //3
vertices->push_back(osg::Vec3(6.0, 4.0, 4.0)); //4
vertices->push_back(osg::Vec3(6.0, 4.0, 0.0)); //5
vertices->push_back(osg::Vec3(0.0, 4.0, 4.0)); //6
vertices->push_back(osg::Vec3(0.0, 4.0, 0.0)); //7
vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); //8
vertices->push_back(osg::Vec3(0.0, 0.0, 0.0)); //9
//建立頂點法線數組
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(10);
//添加資料
(*normals)[0].set(osg::Vec3(-0.707, -0.707, 0.0)); //0
(*normals)[1].set(osg::Vec3(-0.707, -0.707, 0.0)); //1
(*normals)[2].set(osg::Vec3(0.707, -0.707, 0.0)); //2
(*normals)[3].set(osg::Vec3(0.707, -0.707, 0.0)); //3
(*normals)[4].set(osg::Vec3(0.707, 0.707, 0.0)); //4
(*normals)[5].set(osg::Vec3(0.707, 0.707, 0.0)); //5
(*normals)[6].set(osg::Vec3(-0.707, 0.707, 0.0)); //6
(*normals)[7].set(osg::Vec3(-0.707, 0.707, 0.0)); //7
(*normals)[8].set(osg::Vec3(-0.707, -0.707, 0.0)); //8
(*normals)[9].set(osg::Vec3(-0.707, -0.707, 0.0)); //9
//或者按下面方式添加資料 set重載
//(*normals)[0].set(-0.707, -0.707, 0.0); //0
//(*normals)[1].set(-0.707, -0.707, 0.0); //1
//(*normals)[2].set(0.707, -0.707, 0.0); //2
//(*normals)[3].set(0.707, -0.707, 0.0); //3
//(*normals)[4].set(0.707, 0.707, 0.0); //4
//(*normals)[5].set(0.707, 0.707, 0.0); //5
//(*normals)[6].set(-0.707, 0.707, 0.0); //6
//(*normals)[7].set(-0.707, 0.707, 0.0); //7
//(*normals)[8].set(-0.707, -0.707, 0.0); //8
//(*normals)[9].set(-0.707, -0.707, 0.0); //9
//或者按下面的方式進行定義并添加資料 動态數組
//osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
添加資料
//normals->push_back(osg::Vec3(-0.707, -0.707, 0.0)); //0
//normals->push_back(osg::Vec3(-0.707, -0.707, 0.0)); //1
//normals->push_back(osg::Vec3(0.707, -0.707, 0.0)); //2
//normals->push_back(osg::Vec3(0.707, -0.707, 0.0)); //3
//normals->push_back(osg::Vec3(0.707, 0.707, 0.0)); //4
//normals->push_back(osg::Vec3(0.707, 0.707, 0.0)); //5
//normals->push_back(osg::Vec3(-0.707, 0.707, 0.0)); //6
//normals->push_back(osg::Vec3(-0.707, 0.707, 0.0)); //7
//normals->push_back(osg::Vec3(-0.707, -0.707, 0.0)); //8
//normals->push_back(osg::Vec3(-0.707, -0.707, 0.0)); //9
//建立紋理坐标 也可以用和上面相同的方式定義
osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array(10);
//添加資料
(*texcoords)[0].set(0.0, 1.0);
(*texcoords)[1].set(0.0, 0.0);
(*texcoords)[2].set(0.3, 1.0);
(*texcoords)[3].set(0.3, .0);
(*texcoords)[4].set(0.5, 1.0);
(*texcoords)[5].set(0.5, 0.0);
(*texcoords)[6].set(0.8, 1.0);
(*texcoords)[7].set(0.8, 0.0);
(*texcoords)[8].set(1.0, 1.0);
(*texcoords)[9].set(1.0, 0.0);
//建立一個幾何對象
osg::ref_ptr<osg::Geometry> houseWall = new osg::Geometry;
//設定頂點資料、紋理坐标、法線數組
houseWall->setVertexArray(vertices.get());
houseWall->setTexCoordArray(0, texcoords.get());
houseWall->setNormalArray(normals.get());
//設定法線的綁定方式為每個屬性與一個圖元組相綁定,該方法自動設定使用glBegin()/glEnd()的慢速通道進行繪制
houseWall->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
//添加圖元,多段四邊形條帶,即一系列四邊形
houseWall->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::QUAD_STRIP, 0, 10));
//設定紋理貼圖
//C++項目的目前目錄為vcproj工程檔案目錄
houseWall->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile("../wall.bmp")));
return houseWall.release();
}
/*建立人字頂部分
人字頂由6個頂點組成
使用顔色數組替代紋理,表達頂面的繪制效果
*/
osg::Drawable *createHouseRoof()
{
//建立頂點數組,逆時針添加
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
//添加資料
vertices->push_back(osg::Vec3(-0.2, -0.5, 3.5)); //0
vertices->push_back(osg::Vec3(6.2, -0.5, 3.5)); //1
vertices->push_back(osg::Vec3(0.8, 2.0, 6.0)); //2
vertices->push_back(osg::Vec3(5.2, 2.0, 6.0)); //3
vertices->push_back(osg::Vec3(-0.2, 4.5, 3.5)); //4
vertices->push_back(osg::Vec3(6.2, 4.5, 3.5)); //5
//繪圖基元為多段四邊形條帶
osg::ref_ptr<osg::DrawArrays> roof = new osg::DrawArrays(osg::DrawArrays::QUAD_STRIP, 0, 6);
//繪圖基元為三角形
osg::ref_ptr<osg::DrawElementsUInt> roofSide = new osg::DrawElementsUInt(osg::DrawElementsUInt::TRIANGLES, 6);
(*roofSide)[0] = 0;
(*roofSide)[1] = 2;
(*roofSide)[2] = 4;
(*roofSide)[3] = 5;
(*roofSide)[4] = 3;
(*roofSide)[5] = 1;
//建立屋頂顔色數組
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
//添加資料
colors->push_back(osg::Vec4(0.25, 0.0, 0.0, 1.0));
//建立一個幾何體對象
osg::ref_ptr<osg::Geometry> houseRoof = new osg::Geometry;
//設定頂點資料、顔色數組
houseRoof->setVertexArray(vertices.get());
houseRoof->setColorArray(colors.get());
//設定顔色的綁定方式為一個屬性與所有頂點綁定
houseRoof->setColorBinding(osg::Geometry::BIND_OVERALL);
//添加圖元
houseRoof->addPrimitiveSet(roof.get());
houseRoof->addPrimitiveSet(roofSide.get());
//由于頂面的法線計算比較複雜,這裡使用OSG自帶的快速法線生成工具osgUtil::SmoothingVisitor
osgUtil::SmoothingVisitor smv;
smv.smooth(*houseRoof);
return houseRoof.release();
}
int main(int argc, char **argv)
{
//将房屋和牆體整合到一個葉節點中進行渲染
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(createHouseWall());
geode->addDrawable(createHouseRoof());
osgViewer::Viewer viewer;
viewer.setSceneData(geode.get());
return viewer.run();
}
運作程式,可得到圖一的房屋,及圖二的控制台界面顯示:

對比上一個例子:幾何圖像的繪制——四邊形,可以看到這個例子在結構和定義上都進行了一定的改變:
1)定義數組資料和添加資料的方法有兩種:
動态數組:
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
靜态數組:
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(10);
它們對應的添加資料的方法也不同:
動态數組添加方式有兩種,set具有函數重載的屬性:
(*normals)[0].set(osg::Vec3(-0.707, -0.707, 0.0));
(*normals)[0].set(-0.707, -0.707, 0.0);
靜态數組的添加方式為:
normals->push_back(osg::Vec3(0.707, -0.707, 0.0));
對于動态數組與靜态數組的優缺點,我不懂C,但結合我對C#的了解,我認為在C中:
對于已知長度,且長度不會改變的資料來說,建立靜态數組更好,因為靜态數組建立友善,引用簡單,不需要釋放;
而對于動态數組,雖然使用靈活,能根據需要動态配置設定大小,但建立麻煩,未避免記憶體洩漏必須由程式員自己釋放(C#有自動回收機制CC,但并不是用完立即回收,而是它覺得該回收時才回收,是以建議也手動釋放),如果是固定長度則沒必要使用它。
2)在幾何圖像的繪制——四邊形中,繪制幾何體的步驟是:建立葉節點——建立幾何體對象——建立頂點、添加資料、設定為幾何體屬性————建立其他相關資料并設定為幾何體屬性——在幾何體上添加圖元——将幾何體添加到葉節點上;
而在這個幾何圖形中,繪制幾何體的步驟是:建立頂點、添加資料——建立其他頂點并添加資料——建立幾何體對象——把前面的資料依次設定為幾何體屬性——在幾何體上添加圖元;在總函數上建立葉節點——把幾何體添加到葉節點上。
從上面兩個步驟來看,第二個步驟要更符合結構組成一些,當然,到底使用什麼步驟自己決定。