第40节 指北针实例

本节内容

受网友提问,本节实现一个指北针:
在这里插入图片描述
上面左下角的指北针由两部分组成,一部分是指针,一部分是底盘:
请添加图片描述
请添加图片描述
底盘动,指针在动,默认朝向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
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片