目录
1、场景基本绘图类
OSG 通常由三种处理几何体的手段:
- 使用松散封装的 OpenGL 绘图基元
- 使用 OSG 中的基本几何体
- 从文件中导入场景模型
1.1、Geometry 绘制几何体
使用 Geometry 绘制几何体时,需要了解以下概念:
1.1.1、顶点属性
几何体时由顶点组成的,当顶点为空时,无法绘制几何体;
顶点属性定义每一顶点的信息,包括:
- 顶点坐标(描述顶点位置信息) ----- 必要的
- 顶点颜色(描述顶点的默认颜色值)
- 顶点法线(用于计算光照)
- 纹理坐标(描述顶点使用的纹理位置信息)
- 等等。。。
其中,对于颜色、法线等属性,提供了绑定方式,包括:
- 绑定每个顶点(BING_PER_VERTEX)
该模式下,颜色或法线的数量需要和顶点数量一致,每一顶点对应一个属性值;
- 绑定图元组(BING_PER_PRIMITIVE_SET)
该模式下,颜色或法线的数量需要和图元组数量一致,每一图元组使用一个属性值;
- 绑定所有(BIND_OVERALL)
该模式下,整个绘制体使用一个属性值。
1.1.2、图元
osg::PrimitiveSet 类, 该类主要松散封装了 OpenGL 的绘图基元,通过指定绘图基元来指定几何体顶点将采用哪一种或几种基元绘制。
图元类型:
- POINGS // 绘制点
- LINES // 绘制线
- LINE_STRIP // 绘制多段线
- LINE_LOOP // 绘制封闭线
- TRIANGLES // 绘制三角形
- TRIANGLE_STRIP // 绘制多个三角形
TRIANGLE_FAN // 绘制多个三角扇
- QUADS // 绘制四边形
- QUAD_STRIP // 绘制多个四边形
- POLYGON // 绘制多边形
1.1.3、图元组
图元组告诉 OSG, 在绘制图形时,使用哪些顶点、使用哪个图元对顶点进行结合。
图元由两种:
- DrawArrays
- DrawArrays 直接告诉 OSG 使用的顶点在顶点数组中的起点、终点和使用的图元
- 复杂的莫模型需要传入较多的点且比较麻烦
- 效率高
- DrawElements
- DrawElements 告诉 OSG 使用顶点的索引值数组和图元
- 该方式不需要传入重复的点数据,使用简单
- 由于内部需要复制数据,因此效率较低
2、使用 OSG 预定义的几何体
再 OSG 中,为了简化场景的绘制,它本身预定义了一些常用的几何体。
2.1、osg::Shape 类
- osg::Shape 类直接继承自 osg::Object 基类。
- osg::Shape 类是各种内嵌几何体的基类,它不但可用于碰撞检测,还可用于生成预定义的几何体对象。
常用的内嵌几何体包括以下几种:
- osg::Box // 正方体
- osg::Capsule // 太空舱
- osg::Cone // 锥体
- osg::Cylinder // 柱体
- osg::HeightField // 高度图
- osg::InfinitePlane // 无线平面
- osg::Sphere // 球体
- osg::TriangleMesh // 三角片
2.2、osg::ShapeDrawable 类
如果渲染内嵌的几何体,就必须将其与 osg::Drawable 相关联。实际应用中,可以使用 osg::Drawable 的派生类 osg::ShapeDrawable 来完成这个功能。
在 osg::ShapeDrawable 类的构造方法中提供了关联 osg::Shape 的方法:
// 第一个参数为 shape, 第二个参数默认不细化
ShapeDrawable(Shape* shape, TessellationHints = 0);
同时,由于它继承 osg::Drawable 类,所以 它的实例需要被添加到叶节点中才能被实例绘制。
2.3、网格化类
osg::TessellationHints 类的主要作用是设置预定义几何体对象的精细程度,精细程度越高,表示其细分越详细,但对于不同的预定义几何体对象,它的作用是不一样的。例如:
- Box ( 四棱柱 ):网格化类对四棱柱没有意义
- Capsule ( 太空舱 ):太空舱分为上下半球部分和圆柱侧面部分,默认细化侧面部分
- Cone ( 圆锥 ):直接细分
- Cylinder ( 柱体 ):直接细分
- Sphere ( 球 ):直接细分
3、实战示例
3.1、画五角星 (DrawArrays 方式)
1、先创建五角星的顶点
// 创建五角星的顶点 (五角星需要十个顶点)
osg::ref_ptr<osg::Vec3Array> createFiveStarVertecs()
{
osg::ref_ptr<osg::Vec3Array> vertexs = new osg::Vec3Array();
int n = 10;
double step = 360 / n;
for(int i = 0; i < n; i++)
{
double x = sin(osg::DegressToRadiaus(i * step));
double y = cos(osg::DegressToRadiaus(i * step));
double z = 0.0;
// 五角星内环的五个点
if(i % != 0)
{
x = x * 0.35;
y = y * 0.35;
}
vertex->push_back(osg::Vec3(x, y, z));
}
return vertex.release();
}
2、画五角星
// 画五角星
osg::ref_ptr<osg::MatrixTransform> drawFiveStar()
{
osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
// 顶点
osg::ref_ptr<osg::Vec3Array> vertexs = createFiveStarVertexs();
// 添加顶点
geometry->setVertexArray(veretexs);
geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimiteveSet::LINE_LOOP, 0, 10));
geode->addDrawable(geometry);
trans->addChild(geode);
return trans;
}
3.2、画实心五角星(DrawElements 方式)
画实心五角星与画五角星完全不同,
因为五角星只是画一条封闭的线 LINE_LOOP,
而实心五角星需要以多边形的形式画出来,而多边形只能画凸多边形,不能直接画,
所以,要把实心五角星分为多个凸多边形拼凑起来。
依然使用上述五角星的顶点
// 画实心五角星
osg::ref_ptr<osg::MatrixTransform> drawSolidFiveStar()
{
osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
// 顶点
osg::ref_ptr<osg::Vec3Array> vertexs = createFiveStarVertexs();
// 添加顶点
geometry->setVertexArray(veretexs);
// 先画五角星中间的五边形
osg::ref_ptr<osg::DrawElementsUInt> polygonElement = new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON, 0, 5);
// DrawElements 方式里面加的是 顶点集合的索引值, 可以重复使用
polygonElement->addElement(1);
polygonElement->addElement(3);
polygonElement->addElement(5);
polygonElement->addElement(7);
polygonElement->addElement(9);
geometry->addPrimitiveSet(polyginElement);
// 后绘制五角星外围的五个小五角形
for(int i = 1; i < 10; i +=2)
{
osg::ref_ptr<osg::DrawElementsUInt> triangleElement = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0, 3);
triangleElement->addElement(i);
// 如果超出顶点索引的最大范围,使用索引减去顶点的总数
i + 1 > 9 ? triangleElement->addElement(i + 1 - 10) : triangleElement->addElement(i + 1);
i + 2 > 9 ? triangleElement->addElement(i + 2 - 10) : triangleElement->addElement(i + 2);
geometry->addPeimitiveSet(triangleElement);
}
geometry->addDrawable(geometry);
trans->addChild(geometry);
return trans;
}
3.3、画圆锥
画圆锥需要画一个底部的圆, 和一个圆锥侧面的面
1、创建圆锥的顶点
// 创建圆锥的顶点
osg::ref_ptr<osg::Vec3Array> createConeVertexs()
{
osg::ref_ptr<osg::Vec3Array> vertexs = new osg::Vec3Array;
// 添加圆锥的顶点
vertexs->push_back(osg::Vec3(0, 0, 2));
// 圆锥底部由20个点组成
int n = 20;
double step = 360 / n;
for(int i = 0; i < n; i++)
{
double x = sin(osg::DegressToRadiaus(i * step));
double y = cos(osg::DegressToRadiaus(i * step));
double z = 0.0;
vertexs->push_back(osg::Vec3(x, y, z));
}
return vertexs;
}
2、画圆锥
// 画圆锥
osg::ref_ptr<osg::MatrixTransform> drawCone()
{
osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
// 获取圆锥的顶点
osg::ref_ptr<osg::Vec3Array> vertexs = createConeVertexs();
// 设置顶点
geometry->setVertexArray(vertexs);
// 先画圆锥侧面的面
geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN, 0, 21));
// 后画圆锥底部的圆
geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 1, 20));
geode->addDrawable(geometry);
trans->addChild(geode);
return trans;
}