目录

  • 本节内容
  • 本节代码
  • 思路
  • 以下为全部代码

本节内容

受网友提问,本节实现一个指北针:

上面左下角的指北针由两部分组成,一部分是指针,一部分是底盘:


底盘动,指针在动,默认朝向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;//添加设置HUDosg::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();
}

第40节 指北针实例相关推荐

  1. Android零基础入门第40节:自定义ArrayAdapter

    原文:Android零基础入门第40节:自定义ArrayAdapter ListView用起来还是比较简单的,也是Android应用程序中最重要的一个组件,但其他ListView可以随你所愿,能够完成 ...

  2. java arrayadapter_Android零基础入门第40节:自定义ArrayAdapter

    ListView用起来还是比较简单的,也是Android应用程序中最重要的一个组件,但其他ListView可以随你所愿,能够完成很多想要的精美列表,而这正是我们接下来要学习的内容. 一.自定义Arra ...

  3. SaaS 产品客户调研问题清单: 6种角色,15个场景,40+调研问题实例帮你了解你的客户

    对于 SaaS 来说,了解客户是改善产品和发展业务的关键.想做到这一点,就需要在客户旅程的正确阶段提出正确的调研问题. 本篇文章将对客户调研问题进行一个全面的概述,介绍你可以使用的调研方式,以及如何选 ...

  4. [生存志] 第40节 管仲经济纵横

     管仲经济纵横 家住山西临汾的曲沃武公死磕翼城,有条不紊地实施着"成师小宗"巅覆"仇大宗"的逆袭行动时,家住山东淄博的管仲也正将自己的经济学理论付诸实践.管仲是 ...

  5. 第7章第40节:多图排版:经典的九宫格布局法 [PowerPoint精美幻灯片实战教程]

    当需要往版面中摆放九张图片时,那么推荐使用九宫格的布局方式.要制作九宫格的布局,推荐使用表格工具. 在此处按下并向右下方拖动,以创建一个三行三列的表格. 在此处按下并向下方拖动,避免表格遮挡幻灯片的标 ...

  6. CAD 二次开发再blockTableRecord中添加hatch,绘制指北针实例

    [CommandMethod("9999")]public static void zhiBeiZhen2(){

  7. sql server与java实例_Origin数据处理实例教程50节02040101

    本期小电分享50节Origin数据处理实例教程 链接:https://pan.baidu.com/s/1y-5wrJ6PEswKV_UMrJ_hYw 提取码:8suu 第1节- Origin图表中如何 ...

  8. Android零基础入门第77节:Activity任务栈和启动模式

    2019独角兽企业重金招聘Python工程师标准>>> 通过前面的学习,Activity的基本使用都已掌握,接下来一起来学习更高级的一些内容. Android采用任务栈(Task)的 ...

  9. Android零基础入门第89节:Fragment回退栈及弹出方法

    2019独角兽企业重金招聘Python工程师标准>>> 在上一期分享的文章末尾留了一个课后作业,有去思考如何解决吗?如果已经会了那么恭喜你,如果还不会也没关系,本期一起来学习. 一. ...

最新文章

  1. day_06、面向对象
  2. linux php 调用exec() 中,svn遇到的问题
  3. 【Tools】git操作总结
  4. 转载、Python的编码处理(二)
  5. NO.10章 图(遍历、最短路、生成树、拓扑、关键路径)
  6. 【ES6(2015)】Reflect
  7. 操作系统教程答案(谢旭升,朱明华版)
  8. C语言读取wav文件中特定内容6,c读取wav文件,头文件后面的所有数据
  9. 研究Google maps及51ditu的图片切割及存储方法(转)
  10. 鸡啄米:C++编程入门系列之目录和总结(再学习路标,大牛见解深刻,真正容易入门)
  11. C#招行支付流程(一网通支付-PC扫码支付)
  12. ORA-00933:SQL 命令未正确结束(1)
  13. npm 清理vue项目缓存
  14. MegaCli命令设置raid磁盘阵列为直通模式
  15. 贴片陶瓷电容材质NPO、C0G、X7R、X5R、Y5V、Z5U区别
  16. 你们的哔哩哔哩终于要上市了
  17. H5U PLC斜坡函数功能块(ST代码)
  18. 一份完整的PyCharm图解教程
  19. Red Hat Linux硬盘分区教程
  20. 【读书】听“樊登读书会”的要点和实践

热门文章

  1. [转]中国最大的Webshell后门箱子调查,所有公开大马全军覆没
  2. MATLAB学习第五章矩阵
  3. Mac教程macOS教程 苹果电脑教程
  4. 经典答案:下水道井盖为什么是圆的——让面试官无语
  5. 15分钟看完:悉尼科技大学入选 CVPR 2021 的 9 篇论文,都研究什么?
  6. 【四舍六入五单双法】
  7. Python分析香港26281套在售二手房数据!寸土寸金啊!
  8. 从育碧、工业光魔归来,做原创国漫!20年动画人徐健的CG路
  9. 学习永不止步---考后总结
  10. SCSI/iSCSI及SAS、NAS、SAN的基本介绍