开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 30 天
1 几何体类
在前面我们已经简单介绍了几何体(osg::Geometry)类,它继承自osg::Drawable类。它的继承关系图如下图所示。
如果读者是一个熟练的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;
}
效果图
动态效果图