本文主要讲解OSG中一些枯燥的空间数学方面知识,及其一个重要的数据结构-数组,和其内存管理机制-智能指针
一、基本数学组件
1.1二维与多维向量
构建一个单精度浮点类型的三维向量vector是:osg::Vec3f vector
构建一个双精度浮点类型的四维向量vector是:osg::Vec4d vector
以osg::Vec3f 为例来实现各种类型的向量运算
osg::Vec3 v1;
v1.set(1.0, 1.0, 1.0) //设置向量的值
v1.normalize() //对向量进行归一化处理
float x=v1[0] //获取分量的值,即获得第一个元素的值
osg::Vec3 v2(2.0, 5.0, 8.0) //通过构造函数赋值,直接设置三个分量的值
v2 *= 0.5 //向量的数乘运算
v2 =osg::Vec3(1.0, 0.0, -2.0) -v2 //向量的四则运算中的减法
float distance = (v1 -v2 ).length() //求取两向量的距离
float dotproduct = v1 * v2 //向量点乘
osg::Vec3 crossProduct = v1 ^ v2 //向量叉乘
1.2四元数
四元数的优势在于:它可以表达物体绕任意向量轴的旋转,并且和欧拉角度旋转与旋转矩阵的方法相比,其效率效高,操作也更加灵活
四元数由三个复数和一个实数组成
设置沿x轴逆时针旋转90度的代码
osg::Quat quat(psg::PI_2, osg::Vec3(1.0, 0.0, 0.0);
其中osg::PI_2是OSG中的预定义宏,数学上表示为pi/2
osg::Quat quat(osg::DegreesToRadians(90.0), osg::Vec3(1.0, 0.0, 0.0);
和上一行代码表示的含义相同,其第一个参数直接使用角度值而非弧度值来表达旋转角度,应用到一个函数osg::DegreesToRadians()
osg::Quat quat3 = quat1 * quat2
它表示这两次旋转结果的叠加之和
osg::Vec3 vec;
double angle;
quat.getRotate(angle,vec ); //通过getRotate()函数获取四元数包含的旋转动作内容,即它所依据的旋转轴和旋转角度
double degree = osg::RadiansToDegrees(angle); //将弧度值转换为角度值
1.3矩阵
osg::Matrix mat1; //定义一个单位矩阵
mat1.makeRotate(osg::Quat(osg::PI_2, osg::Vec3(1.0, 0.0, 0.0)); //旋转,沿x轴逆时针旋转90度
mat1.preMultTranslate(osg::Vec3d(0.0, 1.0, 0.0 )); //沿y方向平移
double a11 =mat1 (1,1); // 获取第二行和第二列的元素
osg::Matrix mat2;
mat2.makeScale(osg::Vec3d(1.0, 1.0, 2.0)); // 设置为缩放矩阵
osg::Matrix mat3;
mat3 = mat2 * mat1 ; // 级联两个矩阵
osg::Vec3d vec(0.0, 0.0, 1.0);
vec = vec * mat3; //将矩阵用于向量的空间变换
其最终的效果为几个动作叠加:先执行Z方向的放大,在执行X方向的旋转,最后执行Y方向的平移。
osg::Matrix mat3;
mat3 = osg::Matrixd::scale(osg::Vec3d(1.0, 1.0, 2.0)) * osg::Matrixd::rotate(osg::Quat(osg::PI_2,osg::Vec3(1.0, 0.0, 0.0)) * osg::Matrixd::translate(osg::Vec3d(0.0, 1.0, 0.0));
osg::Vec3d vec(0.0, 0.0, 1.0);
vec = vec * mat3;
这段代码和上面的代码作用是相同的,不同的是其书写方式,以及用到了新的类的函数,新类为osg::Matrixd
1.4包围体
OSG中支持两种类型的包围体,即包围球和轴对称包围盒,它们主要用于场景图形结点层次的实现。包围球和包围盒都是不和绘制的对象,它们值负责记录一些必要的属性信息(如中心,包围半径等),同时用于一些必要的数学运算,并且,包围体中心表达的往往不是世界坐标系下的物体中心,而是取决于它处于场景树中的哪一级局部坐标系。
创建一个单精度的包围球:
osg::BoundingSphere bs(osg::Vec3(0.0, 0.0, 0.0), 1.0f) //第一个获得中心点,第二个获得半径长度
创建一个包围盒
osg::BoundingBox bb(osg::Vec3(0.0, 0.0, 0.0), osg::Vec3(1.0, 2.0, 3.0)) //第一个参数为最小端点,第二个参数为最大端点
二、数组对象
2.1数据数组(基类为Array)
这类数组数据类型为单精度或双精度的浮点数据,包括数值、多维向量等,主要用于OpenGL定点坐标,颜色,纹理坐标等属性数组的设置
为了使Array真正具备保存多种类型的数据的能力,需要定义一个模板类并接受不同的模板参数,同时还需要使用std:::vector向量组来记录数组中的多个元素信息。
下面快速构建一个Vec3类型的数据数组,并使用标准模板库向量组的操作方法对其进行各种操作
//定义三维向量数组
osg::Vec3Array* array = new osg::Vec3Array;
//向数组中传入数据
array->push_back(osg;:Vec3(1.0,0.0,0.0));
array->push_back(osg;:Vec3(1.0,-1.0,2.0));
//这两种方法都可以获取数组的某个元素
osg::Vec3& element1 = (* array)[0];
osg::Vec3 element2=array->at(1);
//使用迭代器遍历数组
for(osg::Vec3Array::iterator itr=array->begin(); itr!=array->end(); ++itr)
{
osg::Vec3& vec =*itr:
...
}
2.2数据索引数组
此类数组的元素一般是整数类型,索引数组在绘制OpenGL集合图元时尤为常见
索引数组类派生自Array类,名为IndexArray,它除了继承Array类的所有成员函数之外还定义了自己的成员。
其基本操作方法与数据数组的操作并无本质区别。
三、内存管理机制
OSG中使用场景树结构来管理整个三维世界,如果需要绘制一个规模宏大的场景,则是必要创建和维护众多的结点对象。二这一场景不在继续渲染时,对于传统的开发流程来说,就需要手动卸载其中所有的节点,释放所有已分配的内存空间,在进行下一步的工作。。如果未能释放所有场景对象,则会产生内存泄漏,OSG提供一种“垃圾收集”机制,通过“内存引用计数”的形式来管理场景中的节点和绘制体,每个场景中的对象都维护着一个引用计数值,当它被其他对象使用时,引用计数值加1,反之减一。当一个对象的引用计数为0时,它将自动从内存中释放。
3.1智能指针
osg::ref_ptr
使用如下方法来定义一个使用智能指针来管理的参照对象
osg::Geode* geode=new osg::Geode;
osg::ref_ptr
osg::Geode* obj1 = *geodePtr; //获取Geode指针
groupNode->addChild(geodePtr);// 获取指针并传递给其他函数
//在这个过程中,如果超出了geodePtr的生命周期范围,南无这个智能指针变量在析构的过程中会自动将Geode对象的引用计数值减1,一共引用了一次,运行结束后减1,则该结点变量会被自动卸载。
3.2参考对象
参考对象是引用计数和对象自动卸载的实际执行者。