OSG之几何体类

264 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 30 天

1 几何体类

在前面我们已经简单介绍了几何体(osg::Geometry)类,它继承自osg::Drawable类。它的继承关系图如下图所示。

2.png

如果读者是一个熟练的OpenGL程序员的话,相信osg::Geometry类的定义和作用已经在读者心中有一个完美的定义。它的主要作用是对指定绘制几何体的顶点数及对数据的解析,主要提供了如下3大类方法:

(1)指定向量数据。就是以前所涉及的顶点数据、纹理坐标及颜色等一系列向量数据,可以通过下面的几个函数来实现:

void setVertexArray(Array*array) //设置顶点数组

void setVertexData(const ArrayData& arrayData) //设置项点数组数据

void setVertexIndices(IndexArray*array) //设置顶点索引数组

void setNormalArray(Array*array) //设置法线数组

void setNormalData(const ArayData& arrayData) //设置法线数组数据

void setNormalIndices(IndexArray*array) //设置法线索引数组

void setColorArray(Array*array) //设置颜色数组

void setColorData(const ArrayData& arrayData) //设置颜色数组数据

void setColorIndices(IndexArray*array) //设置颜色索引数组

void setTexCoordArray(unsigned int unit,Array*) //设置纹理坐标数组,第一个参数是纹理单元,第二个是纹理坐标数组

void setTexCoordData(unsigned int index,const ArrayData &arrayData) //设置纹理坐标数组数据,第一个参数是纹理单元,第二个是纹理坐标数组数据

void setTexCoordIndices(unsigned int unit,IndexArray*) //设置纹理坐标索引数组,第一个参数是纹理单元,第二个是纹理索引坐标数组

(2)设置绑定方式。数据绑定主要有两项,即法线及颜色,可以通过下面的两个函数来实现:

void setNormalBinding(AttributeBinding ab) //设置法线绑定方式

void setColorBinding(AttributeBinding ab) //设置颜色绑定方式

绑定方式主要有下面几种:

BIND_OFF //不启用绑定

BIND OVERALL //绑定全部的顶点

BIND PER PRIMITIVE_SET //单个绘图基元绑定

BIND_PER_PRIMITIVE //单个独立的绘图基元绑定

BINID PER VERTEX //单个顶点绑定

(3)数据解析。当在指定了各种向量数据和绑定方式之后,采用何种方式来渲染几何体就是最为关键的。不同的方式下,渲染出来的图形是不一样的,即使效果一样,可能面数或内部机制等也是有区别的。

数据解析主要通过如下函数来指定:

bool addPrimitiveSet(PrimitiveSet*primitiveset)

参数说明:osg:PrimitiveSet是无法初始化的虚基类,因此这里主要是调用它的子类来指定数据渲染,最常用的就是前面介绍的osg::DrawArrays,用法比较简单,初始化一个对象实例,参数说明见前面 osg:DrawArrays类

通过前面的讲述可知,绘制并渲染几何体主要有如下3大步骤:

(1)创建各种向量数据,如顶点、纹理坐标、颜色和法线等。需要注意的是,添加顶点数据时主要按照逆时针顺序添加,以确保背面剔除(backface culling)的正确(后面还会有介绍)。

(2)实例化一个几何体对象(osg::Geometry),设置顶点坐标数组、纹理坐标数组、颜色数组、法线数组、绑定方式及数据解析。

(3)加入叶节点绘制并渲染。

通过这么多的介绍,相信读者已经完全明白了。下面的小节中会提供例子来说明如何绘制基本的几何体对象,要仔细理解。

2 基本几何体绘制示例

基本几何体绘制(osg::Geometry)示例演示了创建一个几何体的过程,示例中创建了最简单的四边形。通过该示例读者将学会如何创建简单的几何体。

代码如程序清单所示。

#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>

#include <iostream>

#include "utility.h"

// 创建一个四边形点
osg::ref_ptr<osg::Node> createQuad()
{
	// 创建一个叶子节点对象
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	// 创建一个几何体对象
	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

	// 创建顶点数组,注意顶点的添加顺序是逆时针的
	osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
	// 添加数据
	v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
	v->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
	v->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
	v->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));

	// 设置顶点数据
	geom->setVertexArray(v.get());

	// 添加纹理坐标
	osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
	// 添加数据
	vt->push_back(osg::Vec2(0.0f, 0.0f));
	vt->push_back(osg::Vec2(1.0f, 0.0f));
	vt->push_back(osg::Vec2(1.0f, 1.0f));
	vt->push_back(osg::Vec2(0.0f, 1.0f));

	// 设置纹理坐标
	geom->setTexCoordArray(0, vt.get());

	// 创建颜色数组
	osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();
	// 添加数据
	vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
	vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
	vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
	vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));

	// 设置颜色数组
	geom->setColorArray(vc.get());
	//设置颜色的绑定方式为单个顶点
	geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

	// 创建发现数组
	osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
	// 添加法线
	nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));

	// 设置法线数组
	geom->setNormalArray(nc.get());
	// 设置法线的绑定方式为全部顶点
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

	// 添加图元,绘制基元为四边形
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));

	// 添加到叶子节点
	geode->addDrawable(geom.get());

	return geode.get();
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	// 创建一个多父节点,共享一个子节点的场景
	osg::ref_ptr<osg::Group> rootnode = new osg::Group;

	// 添加到场景
	rootnode->addChild(createQuad());

	// 优化场景数据
	osgUtil::Optimizer optimzer;
	optimzer.optimize(rootnode.get());

	setWindowSize(viewer.get(), 600, 400, "Geometry");

	viewer->setSceneData(rootnode.get());

	viewer->realize();

	viewer->run();

	return 0;
}

效果图

3.PNG

动态效果图

1.gif

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 30 天