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

运行程序,可得到图一的房屋,及图二的控制台界面显示:

对比上一个例子:几何图像的绘制——四边形,可以看到这个例子在结构和定义上都进行了一定的改变:

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)在几何图像的绘制——四边形中,绘制几何体的步骤是:创建叶节点——创建几何体对象——创建顶点、添加数据、设置为几何体属性————创建其他相关数据并设置为几何体属性——在几何体上添加图元——将几何体添加到叶节点上;

而在这个几何图形中,绘制几何体的步骤是:创建顶点、添加数据——创建其他顶点并添加数据——创建几何体对象——把前面的数据依次设置为几何体属性——在几何体上添加图元;在总函数上创建叶节点——把几何体添加到叶节点上。

从上面两个步骤来看,第二个步骤要更符合结构组成一些,当然,到底使用什么步骤自己决定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值