【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)

前言

在上一篇【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(一)-CSDN博客的基础上增加点迹显示功能,点迹显示在地图上。

效果图

代码结构变化

新增SituationCore

新增一个文件SituationCore.h,用于管理数据,当前包含五角星航线数据和雷达数据,其中五角星航线数据是从负责3D 态势图层的SituationScene的构造函数中挪出来。

成员 类型 一句话职责 谁调用 调用频率
starVertex(i) 函数 算第 i 个五角星顶点坐标 truthPos/truthVel/画航线 SituationScene画航线折线 SituationScene画顶点标记 初始化+每帧
starSeq(k) 函数 查星形飞行顺序表 truthPos/truthVel/画航线 SituationScene画航线折线 同上
truthPos(t) 函数 真值位置 = 时间→坐标 显示层、RadarSim 每帧30Hz
truthVel(t) 函数 真值方向 = 时间→单位向量 显示层(对机头) 每帧30Hz
gaussRand() 函数 产生一个高斯随机数 RadarSim 每个点迹2次
Plot 结构体 一次雷达测量的结果 全链路传递 每0.5s一个
polarToWorld(p) 函数 极坐标→直角坐标 显示层 每个点迹1次
RadarSim::step() 类方法 按节拍产生带噪测量 MainWindow 每帧调用

SceneUtil.h

增加了4个与点迹相关的函数:

复制代码
// ------------------------------------------------------------
// setupDynamic:让 Geometry 可以每帧更新的"三件套"。
// OSG 默认用显示列表缓存 GPU 命令------CPU 侧改了顶点,缓存不知道,
// 表现为"加了点但屏幕没反应"。三件套 = 改用实时 VBO 提交。
// ------------------------------------------------------------
inline void setupDynamic(osg::Geometry* g)
{
    g->setDataVariance(osg::Object::DYNAMIC);   // ① 声明:数据每帧会变
    g->setUseDisplayList(false);                // ② 关闭显示列表缓存
    g->setUseVertexBufferObjects(true);         // ③ 用 VBO,dirty() 即上传
}

// ------------------------------------------------------------
// makePoints:点迹图层(动态点集)
//   cap = 最多保留多少个点。点迹会无限到来,不设上限内存和顶点数
//   无限涨------行业做法是只留最近 N 个(FIFO),即"航迹余辉"长度。
// ------------------------------------------------------------
inline osg::Geometry* makePoints(float pixelSize, const osg::Vec4& color, unsigned cap)
{
    osg::ref_ptr<osg::Geometry> g = new osg::Geometry;
    setupDynamic(g.get());

    osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
    v->reserve(cap + 1);                        // 预分配,避免频繁扩容
    g->setVertexArray(v.get());
    g->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, 0));  // 初始 0 个点

    osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
    c->push_back(color);                        // 所有点共用一个颜色
    g->setColorArray(c.get(), osg::Array::BIND_OVERALL);

    osg::StateSet* ss = g->getOrCreateStateSet();
    ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    ss->setAttributeAndModes(new osg::Point(pixelSize));          // 点直径(像素)
    ss->setAttributeAndModes(new osg::Depth(osg::Depth::ALWAYS)); // 信息层不被遮挡
    return g.release();
}

// ------------------------------------------------------------
// appendPoint:尾部加点,超 cap 从头部淘汰(FIFO)。
//   改完必须 dirty(),否则数据只在 CPU 内存里,GPU 看不见。
// ------------------------------------------------------------
inline void appendPoint(osg::Geometry* g, const osg::Vec3& p, unsigned cap)
{
    auto* v = static_cast<osg::Vec3Array*>(g->getVertexArray());
    if (v->size() >= cap)
        v->erase(v->begin());                   // 淘汰最老的点

    v->push_back(p);

    // 顶点数量变了 → 图元描述同步更新
    if (auto* da = dynamic_cast<osg::DrawArrays*>(g->getPrimitiveSet(0)))
        da->set(GL_POINTS, 0, GLsizei(v->size()));

    v->dirty();                                 // ★ 通知 OSG 重新上传
}

// ------------------------------------------------------------
// rewritePoint:就地改第 i 个顶点(过程3航迹线的预热------
//   航迹线是"顶点数固定、位置每帧更新",正是它的用武之地)
// ------------------------------------------------------------
inline void rewritePoint(osg::Geometry* g, unsigned i, const osg::Vec3& p)
{
    auto* v = static_cast<osg::Vec3Array*>(g->getVertexArray());
    if (i < v->size()) { (*v)[i] = p; v->dirty(); }
}

SituationScene变更

增加点迹图层,把五角星数据移出,新增与点迹有关的buildPlots、addPlot、updateTruth

复制代码
#include "SituationCore.h"   // ★ 视图层依赖数据层:addPlot 需要 Plot 类型


    // ★ 喂给态势一个雷达点迹(MainWindow 调用)
    void addPlot(const situ::Plot& plot);
    void buildPlots();      // ★ 点迹图层
    osg::ref_ptr<osg::Geometry> m_plotGeom;       // 点迹动态几何体
    unsigned                    m_plotCap = 240;  // 内存上限:240点≈2分钟历史

SituationScene::SituationScene(osg::Group* root, osg::Node* model)
    : m_root(root)
{
    ... ...
    buildPlots();      // ★ 新增图层
    ... ...
}

由调用本地函数,改成调用SituationCore的starVertex和starSeq
void SituationScene::buildStarRoute(){
    // a) 航线折线
    for (int i = 0; i < 5; ++i)              // LOOP 模式自动回到起点闭合
            //v->push_back(osg::Vec3(m_star[m_seq[i]]));
            v->push_back(osg::Vec3(situ::starVertex(situ::starSeq(i))));

    // b) 顶点标记
    for (int i = 0; i < 5; ++i)//绘制五角星
            //v->push_back(osg::Vec3(m_star[i]));
            v->push_back(osg::Vec3(situ::starVertex(i)));
}

// ------------------------------------------------------------
// 图层3: ★ 新增点迹
// ------------------------------------------------------------
void SituationScene::buildPlots()
{
    // 6 像素红点,上限 240 个。返回的 Geometry 是"活的":
    // 之后每来一个点迹就 appendPoint 一次,图形自动增长
    m_plotGeom = su::makePoints(6.f, osg::Vec4(1.f, 0.25f, 0.2f, 1.f), m_plotCap);

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(m_plotGeom.get());
    m_root->addChild(geode.get());
}

void SituationScene::addPlot(const situ::Plot& plot)
{
    // 极坐标 → 直角坐标(数据层的换算函数,z=0)
    const osg::Vec3d p = situ::polarToWorld(plot);

    // z 强制 0.6:二坐标雷达测不到高度,点迹只能落在地面。
    // 所以红点云贴地、飞机在 800m 高空------两者之间靠白色垂线联系。
    su::appendPoint(m_plotGeom.get(),
                    osg::Vec3(float(p.x()), float(p.y()), 0.6f),
                    m_plotCap);
}

// ------------------------------------------------------------
// ★★ 本过程核心算法★★
// 每帧驱动:"调数据层 + 摆矩阵"。
// ------------------------------------------------------------
void SituationScene::updateTruth(double t)
{
    const osg::Vec3d pos = situ::truthPos(t);
    const osg::Vec3d dir = situ::truthVel(t);
    osg::Quat rot;  rot.makeRotate(osg::Vec3d(0, -1, 0), dir);
    m_airMt->setMatrix(osg::Matrix::rotate(rot) * osg::Matrix::translate(pos));
}

MainWindow更新

复制代码
situ::RadarSim  m_radar;      // ★ 雷达模拟器(过程2新增)

    m_radar.setNoise(80.0, 0.004);      // 测量误差:距离±80m,方位±0.23°(可调,见实验)

// 仿真节拍:唯一的职责是"推时钟 + 驱动场景"。
// 注意数据循环(30Hz)与渲染循环(60Hz)是两条独立的线,
// 渲染线程每帧画的都是当前最新的场景状态------这就是"数据与显示解耦"。
void MainWindow::onTick()
{
    m_simTime += m_dt;                  // 暂停时不进这里,时钟冻结
    // ① 雷达节拍:本拍可能出 0 个或 1 个点迹(扫描周期限制)
    situ::Plot plot;
    if (m_radar.step(m_simTime, plot)) {
        // qDebug("plot t=%.1f R=%.0f", plot.t, plot.range);  // 调雷达时打开
        m_scene->addPlot(plot);        // 数据层 → 显示层,只传"测量结果"
    }

    // ② 飞机按真值飞
    m_scene->updateTruth(m_simTime);
}

数据流全图

MainWindow::onTick(30Hz 仿真时钟)

│

├─① m_radar.step(t, plot) 【数据层】

│ truthPos(t) 取真值

│ → 算 R/θ → 叠加高斯噪声 → 填 Plot

│ (没到 0.5s 间隔就返回 false,本拍没有点迹)

│

├─② m_scene->addPlot(plot) 【场景层】

│ polarToWorld 极→直角

│ → appendPoint 追加顶点 + FIFO

│ → dirty() → 下一帧 GPU 画出红点

│

└─③ m_scene->updateTruth(t) 【场景层调数据层函数】

situ::truthPos → m_airMt->setMatrix

相关推荐
Python图像识别3 小时前
43-【2027毕设】YOLO11暴力检测识别系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计
扶尔魔ocy4 小时前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
bug和崩溃我都要13 小时前
QT 开发的专业截图录屏工具 - macOS版 / Windows版
windows·qt·mac·录屏·截屏
Python图像识别16 小时前
40-【2027毕设】YOLO11面部口罩检测识别系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计
Python图像识别16 小时前
39-【2027毕设】YOLO11行人摔倒检测系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计
Quz1 天前
QML PathView:直线路径与曲线路径
qt·动画·qml
扶尔魔ocy1 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
西飘客2 天前
vs 怎么根据dll 生成lib文件
c++·qt