本节内容
受网友提问,本节实现一个指北针:
上面左下角的指北针由两部分组成,一部分是指针,一部分是底盘:
底盘动,指针在动,默认朝向Z轴负方向是北,朝向X轴正方向是东。你可以感受一下。其它的方向就是朝向Z正是南,朝向X轴负是西。有人问那Y轴呢?东西南北本来就是一个平面的概念,没有Y轴的事情,Y轴就像人站在平地上,头顶上的方向,相当于朝向天空。
本节代码
本节资源在如下链接/文件中的附件/按章节编号目录中:
【击此打开网盘资源链接】
也可以在本文末获取
思路
首先要实现HUD,在HUD的相机中添加表盘和指标,要注意以下几点:
- HUD的相机的观察矩阵默认是单位矩阵,这样观察矩阵默认就是朝向Z轴负方向。
...
//设置HUD的关键步骤,其中设置了观察矩阵是单位矩阵
//以下是设置HUD的七步,正交投影,观察矩阵是单位矩阵,最后渲染,矩阵没有参考帧,不接受事件,只清除深度缓存保持透明
//设置视口大小
camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 128, 0, 128));
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewMatrix(osg::Matrix::identity());
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
camera->setRenderOrder(osg::Camera::POST_RENDER);
camera->setAllowEventFocus(false);
camera->setViewport(0, 0, 128, 128);
...
- 表盘要在远处,指标要在近处,因此我们给表盘的四个顶点的z值设置的是-0.2,给指针的四个顶点z轴值的是-0.1,这样指针离视点更近
...
//表盘的顶点
osg::Vec3Array* vertices = new osg::Vec3Array;
geom->setVertexArray(vertices);
vertices->push_back(osg::Vec3(0, 0, -0.2));
vertices->push_back(osg::Vec3(128, 0, -0.2));
vertices->push_back(osg::Vec3(128, 128, -0.2));
vertices->push_back(osg::Vec3(0, 128, -0.2));
...
//指针的顶点
osg::Vec3Array* vertices = new osg::Vec3Array;
geom->setVertexArray(vertices);
vertices->push_back(osg::Vec3(0, 0, -0.1));
vertices->push_back(osg::Vec3(128, 0, -0.1));
vertices->push_back(osg::Vec3(128, 128, -0.1));
vertices->push_back(osg::Vec3(0, 128, -0.1));
关于旋转的部分,只需要给指针外面套上一个矩阵即可:
osg::MatrixTransform* mt = new osg::MatrixTransform();
osg::Geode* geode = new osg::Geode();
mt->addChild(geode);
下面就是最关键的旋转部分了,mt矩阵显然是要绕着Z轴转的,这样指针才是和表盘保持平行,在转动,因为汇报的时候顶点左下角是00,右上角是128, 128,因此比如我们要将指针转45度,其实是绕64,64这个位置的z轴转45度,要先平移,将6464移到圆点转,转完再移过去。这个操作没有点基本功估计是理解不了为什么要这样做:
mt->setMatrix(osg::Matrix::translate(osg::Vec3(-64, -64, 0))*osg::Matrix::rotate(osg::inDegrees(45.0), osg::Z_AXIS)*osg::Matrix::translate(osg::Vec3(64, 64, 0)));
把上面的45度换成你想要旋转的角度就实现了一个指北针的功能性的东西了。
现在就是这个角度到底是多少呢?
首先观察矩阵是单位矩阵的情况下,我们朝向z轴负方向,也就是北方。其次是我们得到主视口的观察矩阵,去掉它的移动量,只看旋转量,看看z轴负方向经过观察矩阵的旋转之后,转到了哪里?
osg::Matrix vm = viewer.getCamera()->getViewMatrix();
vm.setTrans(osg::Vec3());
//只考虚vm的旋转
//接着vm改变了,就看其绕y轴转了多少角度,其它的旋转都可以忽略
osg::Vec3 chaoXiang = (-osg::Z_AXIS)*vm;
chaoXiang.y() = 0.0;
chaoXiang.normalize();
我们发现旋转到了chaoXiang ,然后将头顶的方向也就是y的量置为0,再计算chaoXiang和 (-osg::Z_AXIS)的夹角就得到了需要旋转的角度。
两个向量的夹角我们直接用数量积来计算:
//接下来看(-osg::Z_AXIS)和chaoXiang的夹角就是指针应该转多少度
float angle = acos((-osg::Z_AXIS)*chaoXiang);
```
但是数量积有个缺点,acos只能表示[0, PI],无法表达[0, 2*PI]的范围,因此再用叉乘判断一下两个向量的夹角是正还是负,是负的话再特殊处理一下:
```cpp
···
//因为acos的范围是[0, PI],但是东西南北的旋转是360度的,因此要判断夹角的正负,使用叉乘来判断
osg::Vec3f axis = (-osg::Z_AXIS) ^ chaoXiang;
if (axis*osg::Y_AXIS < 0)
{
angle = 2 * osg::PI - angle;
}
mt->setMatrix(osg::Matrix::translate(osg::Vec3(-64, -64, 0))*osg::Matrix::rotate(angle, osg::Z_AXIS)*osg::Matrix::translate(osg::Vec3(64, 64, 0)));
最后如上就和到了角度。
要是把这篇理解了,基本上对于视口中的向量等等理解有一定深度了。
以下为全部代码
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Camera>
#include <osg/Texture2D>
#include <osg/MatrixTransform>
osgViewer::Viewer viewer;
osg::MatrixTransform* mt = new osg::MatrixTransform();
class UpdateCallback : public osg::NodeCallback
{
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osg::Matrix vm = viewer.getCamera()->getViewMatrix();
vm.setTrans(osg::Vec3());
//只考虚vm的旋转
//观察矩阵是单位矩阵的情况下是朝向Z轴负方向的,假如我们认为Z轴的负方向是朝北,则经过了vm之后,现在朝chaoXiang
//X轴正方向是朝东
//接着vm改变了,就看其绕y轴转了多少角度,其它的旋转都可以忽略
osg::Vec3 chaoXiang = (-osg::Z_AXIS)*vm;
chaoXiang.y() = 0.0;
chaoXiang.normalize();
//接下来看(-osg::Z_AXIS)和chaoXiang的夹角就是指针应该转多少度
float angle = acos((-osg::Z_AXIS)*chaoXiang);
//因为acos的范围是[0, PI],但是东西南北的旋转是360度的,因此要判断夹角的正负,使用叉乘来判断
osg::Vec3f axis = (-osg::Z_AXIS) ^ chaoXiang;
if (axis*osg::Y_AXIS < 0)
{
angle = 2 * osg::PI - angle;
}
mt->setMatrix(osg::Matrix::translate(osg::Vec3(-64, -64, 0))*osg::Matrix::rotate(angle, osg::Z_AXIS)*osg::Matrix::translate(osg::Vec3(64, 64, 0)));
traverse(node, nv);
}
};
osg::Node* CreateCompass()
{
osg::Group* root = new osg::Group;
//添加设置HUD
osg::Camera* camera = new osg::Camera;
root->addChild(camera);
//以下是设置HUD的七步,正交投影,观察矩阵是单位矩阵,最后渲染,矩阵没有参考帧,不接受事件,只清除深度缓存保持透明
//设置视口大小
camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 128, 0, 128));
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewMatrix(osg::Matrix::identity());
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
camera->setRenderOrder(osg::Camera::POST_RENDER);
camera->setAllowEventFocus(false);
camera->setViewport(0, 0, 128, 128);
{
//添加底盘
osg::Geode* geode = new osg::Geode();
camera->addChild(geode);
//关闭灯光
geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::Geometry* geom = new osg::Geometry;
geode->addDrawable(geom);
osg::Vec3Array* vertices = new osg::Vec3Array;
geom->setVertexArray(vertices);
vertices->push_back(osg::Vec3(0, 0, -0.2));
vertices->push_back(osg::Vec3(128, 0, -0.2));
vertices->push_back(osg::Vec3(128, 128, -0.2));
vertices->push_back(osg::Vec3(0, 128, -0.2));
osg::Vec3Array* normals = new osg::Vec3Array;
normals->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
geom->setNormalArray(normals, osg::Array::BIND_OVERALL);
//设置纹理坐标,这个比较重要,保证贴图的正确性
osg::Vec2Array* coord = new osg::Vec2Array;
geom->setTexCoordArray(0, coord);
coord->push_back(osg::Vec2(0, 0));
coord->push_back(osg::Vec2(1.0, 0));
coord->push_back(osg::Vec2(1.0, 1.0));
coord->push_back(osg::Vec2(0, 1.0));
geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
osg::StateSet* ss = geom->getOrCreateStateSet();
ss->setMode(GL_BLEND, osg::StateAttribute::ON);
ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
//设置纹理
osg::Texture2D* texture = new osg::Texture2D;
texture->setImage(osgDB::readImageFile("panzi.png"));
osg::StateSet* stateset = geom->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
}
{
//添加指针
//添加底盘
osg::Geode* geode = new osg::Geode();
mt->addChild(geode);
mt->setMatrix(osg::Matrix::translate(osg::Vec3(-64, -64, 0))*osg::Matrix::rotate(osg::inDegrees(45.0), osg::Z_AXIS)*osg::Matrix::translate(osg::Vec3(64, 64, 0)));
camera->addChild(mt);
//关闭灯光
geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::Geometry* geom = new osg::Geometry;
geode->addDrawable(geom);
osg::Vec3Array* vertices = new osg::Vec3Array;
geom->setVertexArray(vertices);
vertices->push_back(osg::Vec3(0, 0, -0.1));
vertices->push_back(osg::Vec3(128, 0, -0.1));
vertices->push_back(osg::Vec3(128, 128, -0.1));
vertices->push_back(osg::Vec3(0, 128, -0.1));
osg::Vec3Array* normals = new osg::Vec3Array;
normals->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
geom->setNormalArray(normals, osg::Array::BIND_OVERALL);
//设置纹理坐标,这个比较重要,保证贴图的正确性
osg::Vec2Array* coord = new osg::Vec2Array;
geom->setTexCoordArray(0, coord);
coord->push_back(osg::Vec2(0, 0));
coord->push_back(osg::Vec2(1.0, 0));
coord->push_back(osg::Vec2(1.0, 1.0));
coord->push_back(osg::Vec2(0, 1.0));
geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
osg::StateSet* ss = geom->getOrCreateStateSet();
ss->setMode(GL_BLEND, osg::StateAttribute::ON);
ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
//设置纹理
osg::Texture2D* texture = new osg::Texture2D;
texture->setImage(osgDB::readImageFile("noddle.png"));
osg::StateSet* stateset = geom->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
}
return root;
}
int main()
{
osg::Group* root = new osg::Group;
root->addChild(osgDB::readNodeFile("axes.osgt"));
root->addChild(CreateCompass());
root->setUpdateCallback(new UpdateCallback());
viewer.setSceneData(root);
return viewer.run();
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容