天天看點

OSG學習:幾何對象的繪制(二)——簡易房屋

以下内容來自: 

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();
}
           

運作程式,可得到圖一的房屋,及圖二的控制台界面顯示:

OSG學習:幾何對象的繪制(二)——簡易房屋
OSG學習:幾何對象的繪制(二)——簡易房屋

對比上一個例子:幾何圖像的繪制——四邊形,可以看到這個例子在結構和定義上都進行了一定的改變:

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)在幾何圖像的繪制——四邊形中,繪制幾何體的步驟是:建立葉節點——建立幾何體對象——建立頂點、添加資料、設定為幾何體屬性————建立其他相關資料并設定為幾何體屬性——在幾何體上添加圖元——将幾何體添加到葉節點上;

而在這個幾何圖形中,繪制幾何體的步驟是:建立頂點、添加資料——建立其他頂點并添加資料——建立幾何體對象——把前面的資料依次設定為幾何體屬性——在幾何體上添加圖元;在總函數上建立葉節點——把幾何體添加到葉節點上。

從上面兩個步驟來看,第二個步驟要更符合結構組成一些,當然,到底使用什麼步驟自己決定。