首页 理论教育 实现主线程等待VP完全退出的技术方案

实现主线程等待VP完全退出的技术方案

时间:2023-06-27 理论教育 版权反馈
【摘要】:为了实现主线程等待VP 完全退出之后再退出, 就需创建VP 退出事件信号, 在主线程退出之前等待VP 退出事件信号。VP 中采用事件触发方式, 当触发一个事件时, 这个事件的回调函数就会被自动执行。为了实现这种效果, 本书提出基于缓冲区通道、 灰度通道的解决方案。

实现主线程等待VP完全退出的技术方案

这里主要讨论目标仿真系统中有关线程刷新、 视景叠加、 灰度视频与叠加、 碰撞检测、 大小视场仿真、 弹道仿真几个主要技术的实现问题。

1.多线程刷新

基于Vega Prim (以下简称“VP”) 实现目标仿真, 需要界面操作、 与硬件通信、 弹道计算等并行任务, 将VP 与其他线程协同工作的最好方法是多线程技术, 这样既能保证三维视景的正常刷新, 又能保证其他工作的正常进行。

VP 本身的示例中没有基于多线刷新实现, 以往有人提出了采用基于定时器和空闲消息的方法, 这些方法虽然实现简单, 但是当有界面操作时不能确保三维视景的正常刷新(抖动或停止), 而采用多线程刷新尽管实现困难, 却能有效避免上述问题。

实现多线程刷新需要处理两个问题: 一是VP 刷新线程与主线程(如MFC 工作线程) 同步的问题; 二是刷新线程暂停(即VP 运行暂停) 的问题。

1) 刷新线程与主线程同步

在主线程中启动刷新线程时, 首先进行VP 初始化, 完成路径设置、 创建通道、 加载物体、 创建场景和加载场景等准备工作, 然后进入循环刷新过程。当主线程准备退出时, 需事先通知VP 线程, 因为VP 运行时内部也启动了其他一些线程(如cull、 draw 等), 接收到主线程准备退出的消息后, VP 终止内部启动的线程, 释放相应资源。 主线程退出之前, 需等待VP 退出事件信号关键代码如下:

2) 刷新线程暂停

刷新线程暂停的实现原理与上述类似, 需用到暂停事件信号, 主要是在刷新线程中等待暂停事件。

刷新线程的关键代码如下:

如果主线程不等到VP 线程退出时就直接退出, 将导致资源释放不彻底。 为了实现主线程等待VP 完全退出之后再退出, 就需创建VP 退出事件信号, 在主线程退出之前等待VP 退出事件信号。 在启动VP 之后, 创建m_hVegaPrimeThreadExitEvent 退出信号。

2.视景叠加

在三维视景上叠加需要的各种图形(如弹道) 是目标仿真的重要内容,如图4 -20 所示。

图4-20 视景叠加

VP 提供的叠加功能有限, 例如, 叠加字符功能仅限于叠加英文字符, 不能叠加汉字。 这个问题的彻底解决方案是实现VP 与OpenGL 混合编程, 即在Vega Prime 中嵌入OpenGL 代码, 实现灵活多变的叠加绘制, 通过分析得知,可解决这个问题。

VP 中采用事件触发方式, 当触发一个事件时, 这个事件的回调函数就会被自动执行。 VP 的功能模块都是由类实现的, 而这个功能中的回调函数也在一个类中实现, 这个类是在功能模块的那个类中定义的, 也就是类中类, 即Subscriber。

为了实现事件回调功能, 需声明一个类, 该类继承于vsChannel 类中的Subscriber 类(因为vsChannel 的回调函数就在这个Subscriber 类中声明)。 这些回调函数在事件发生时就会自动被调用。 为什么要继承这个类呢? 这些回调函数都是虚函数, 里面没有具体的代码, 继承这个类之后将重写这些事件中的虚函数(当然选择所用到的), 由于C ++的多态机制, 当事件发生时,系统就会在调用Subscriber 类中的回调函数时去找到它的派生类的实现方法, 然后去调用它, 从而实现当事件发生时重载代码被自动调用, 完成回调。

Subscriber 类中回调函数的声明是notify(), 根据参数的不同, 它有多种重载形式。 例如, 在VP 中嵌入OpenGL, 使用的就是virtual void notify(vsChannel::Event event,const vsChannel∗channel,vrDrawContext∗context) 这个重载版本的函数。 在重载函数的实现中, 可加入绘制的OpenGL 的代码, 实现各种复杂图形的叠加绘制。

在VP 中绘制OpenGL 代码, 还必须注意两者之间观视方式差异如何统一的问题, VP 的默认视线是沿着y 轴的, OpenGL 的默认视线是z 轴, 需要进行相应转换。

3.灰度视频与叠加(www.xing528.com)

通过弹丸摄像机观察到的画面是灰度画面, 而不是彩色画面, 在此基础上叠加的字符和各种简单图形是彩色的。

然而, VP 本身并不直接支持这种效果。 为了实现这种效果, 本书提出基于缓冲区通道、 灰度通道的解决方案。 该方案支持三维场景绘制叠加、 灰度处理、 叠加彩色字符和图形。 该方案的主要思路是: 将三维场景绘制在缓冲区通道中, 通过调用三维底层功能得到缓冲区通道中帧缓冲区数据, 该数据就是刷新每帧后的三维场景, 对该场景数据进行实时灰度化处理即可得到灰度三维场景, 即灰度视频。 这种处理需在每帧绘制完成之后进行灰度化处理,经试验, 能够达到实时灰度化, 过程如图4 -21 所示。

图4-21 灰度视频与叠加的过程

1) 缓冲区通道

缓冲区通道主要用于三维场景的绘制与叠加, 该通道不直接输出到屏幕上, 而在内存中进行处理, 通过定义绘制事件, 得到三维场景数据。

缓冲通道的关键代码如下:

3) 叠加

叠加包括两个层次——缓冲区通道叠加和灰度通道叠加, 不同的需求有不同的实现方法。 对于需要在三维视景上进行的叠加, 可在缓冲区通道vpChannel_Buffer 中的notify() 开始处插入绘制代码, 这样叠加灰度化之后最后的效果也是灰度的(例如, 在三维场景中叠加导弹尾焰, 叠加后也是灰度的); 至于进行彩色叠加则需要在灰度通道vpChannel_Monochrome 中的notify()结束处添加绘制代码, 需要注意的是, 在绘制之前必须恢复各种状态, 这样叠加的图形就是彩色的(如字符、 简单图形)。

这种设计还支持在单台计算机上实现弹丸屏幕的仿真, 只需要定义一个缓冲区通道, 其他多个输出只是从缓冲区通道中复制灰度化之后的三维场景数据, 能有效减少三维场景的绘制数量, 因为弹丸视点三维场景的数据源相同, 只是叠加画面可能不一样。

4.碰撞检测

Vege Prime 定义了抽象类vpIsector 来描述检测器, 并且派生出了多个具体的检测器, 如检测海拔高度的vpIscetorHAT、 检测瞄准线的vpIsectorLOS 等。每种检测器都依靠线段来进行检测。 检测器自其中心向周围延伸出一些线段(line segment), 当这些线段和目标物体发生相交时就认为碰撞发生。 进行碰撞检测需要设置isector mask, 每个检测器都可以设置一个mask, 进行检测的目标物体也可以设置一个mask。 在该系统中, 检测器安放在弹丸模型上, 进行检测时, 系统要将检测器的mask 和场景内所有物体的mask 进行逻辑与运算, 只有结果不为0 的才进行检测。 此外, 在检测时还可以设置消息机制,根据需要随时捕获此消息, 就知道什么时候碰撞发生, 什么时候碰撞消失。

5.大小视场仿真

虚拟视频背景没有物理变焦镜头, 需通过软件来实现大、 小视场的变化。要模拟视场变化, 就需要修改通道的水平视场角和垂直视场角, 即setFOVSymmetric(float hfov,float vfov), 同时调用setLODVisibilityRangeScale(double scale) 修改LOD (层次细节), 可见距离与视场角匹配。 大、 小视场效果如图4 -22 所示。

图4-22 大、 小视场效果

(a) 小视场; (b) 大视场

6.弹道仿真

考虑到该平台上可能仿真到的弹道, 在此事先预定了很多弹道拟合算法,主要有一元全区间不等距插值算法(拉格朗日(Lagrange) 插值公式)、 一元全区间等距插值算法(拉格朗日插值公式)、 一元三点不等距插值、 一元三点等距插值、 连分式不等距插值、 连分式等距插值、 埃特金不等距逐步插值、埃特金等距逐步插值、 光滑不等距插值算法(阿克玛(Akima))、 光滑等距插值算法(阿克玛), 第一种边界条件的三次样条函数插值、 微商与积分, 第二种边界条件的三次样条函数插值、 微商与积分, 第三种边界条件的三次样条函数插值、 微商与积分。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈