百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Qt的场景图Scene Graph(qt图形)

cac55 2024-10-04 13:32 26 浏览 0 评论

叫场景树更合适,本质不是图。QML场景中的Qt Quick项目将填充QSGNode实例树。

场景图是Qt Quick 2.0引入的,建立在要绘制的内容是已知的基础上。所有QML项目均使用场景图进行渲染,场景图的默认实现是与OpenGL紧密相关的低级高性能渲染堆栈。

qt的场景图和osg的场景图的组织上有些类似,都是不同节点通过一定关系构建的,但是osg的场景节点更多些,并且还关联了渲染状态。在渲染方面,qt是直接对场景图进行渲染,osg是将场景图转换为渲染树再进行渲染(避免渲染状态的频繁切换)。

qt的场景图是根据界面元素的位置、透明等信息构建出来的,而osg的场景图是直接利用节点构建出来的。也就是用户不直接参与qt场景图的构建,但是直接参与osg场景图的构建。

例如,假设用户界面包含十个项目的列表,其中每个项目都有背景色,图标和文本。使用传统的绘图技术,这将导致30次绘图调用和类似数量的状态更改。另一方面,场景图可以重组原始图元以进行渲染,以便在一次调用中绘制所有背景,然后绘制所有图标,然后绘制所有文本,从而将绘制调用的总数减少到仅3个。批处理和状态更改减少这样可以大大提高某些硬件的性能。

场景图由QQuickWindow类管理和呈现,自定义Item类型可以通过调用QQuickItem :: updatePaintNode()将其图形基元添加到场景图中。

场景图是Item场景的图形表示,它是一个独立的结构,其中包含足以渲染所有项目的信息。设置完成后,就可以独立于项目状态对其进行操作和渲染。在许多平台上,场景图形甚至会在GUI线程准备下一帧状态时在专用渲染线程上进行渲染。

场景图的结构

场景图由许多预定义的节点类型组成,每种类型都有专门的用途。尽管我们将其称为场景图,但更精确的定义是节点树。该树是根据QML场景中的QQuickItem类型构建的,然后在内部由渲染该场景的渲染器处理该场景。节点本身不包含任何活动的绘图代码或虚拟paint()函数。

即使节点树主要由现有的Qt Quick QML类型在内部构建,用户也可以添加具有自己内容的完整子树,包括表示3D模型的子树。

节点

对于用户而言,最重要的节点是QSGGeometryNode。它用于通过定义其几何形状和材质来定义自定义图形。使用QSGGeometry定义几何形状,并描述图形图元的形状或网格。它可以是直线,矩形,多边形,许多不连续的矩形或复杂的3D网格。该材质定义如何填充此形状的像素。

【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击→领取「链接」

一个节点可以有任意数量的子节点,并且将渲染几何节点,以便它们以子顺序出现,并且父级位于其子级之后。

常见的节点有:

QSGClipNode

在场景图中实现裁剪功能的节点

QSGGeometryNode

用于场景图中的所有渲染内容的节点

QSGNode

场景图中所有节点的基类的节点

QSGOpacityNode

用于更改节点的不透明度的节点

QSGTransformNode

在场景图中实现变换的节点

QSGRenderNode

表示一组针对场景所使用的图形API的自定义渲染命令。

通过子类QQuickItem :: updatePaintNode()并设置QQuickItem :: ItemHasContents标志,将自定义节点添加到场景图。

至关重要的是,本机图形(OpenGL,Vulkan,Metal等)操作以及与场景图的交互必须专门在渲染线程上进行,主要是在updatePaintNode()调用期间进行。经验法则是仅在QQuickItem :: updatePaintNode()函数内使用带有“ QSG”前缀的类。

处理过程

节点具有虚拟QSGNode :: preprocess()函数,该函数将在呈现场景图之前被调用,主要用于处理节点要渲染的内容。节点子类可以设置标志QSGNode :: UsePreprocess并重写QSGNode :: preprocess()函数以对其节点进行最终准备。例如,将贝塞尔曲线划分为当前比例因子的正确细节级别或更新纹理的一部分。

节点的所有权

节点的所有权由创建者或场景图通过设置标志QSGNode :: OwnedByParent明确完成。通常,将所有权分配给场景图通常是可取的,因为这样可以简化场景图位于GUI线程之外时的清理操作。

材质

材质描述了如何填充QSGGeometryNode中几何图形的内部。它封装了用于图形管线顶点和片段阶段的图形着色器,并提供了足够的灵活性,尽管大多数Qt Quick项目本身仅使用非常基本的材质,例如纯色和纹理填充。

对于只想将自定义阴影应用于QML Item类型的用户,可以使用ShaderEffect类型在QML中直接执行此操作。

以下是材质类别的完整列表:

QSGFlatColorMaterial

在场景图中渲染纯色几何的便捷方法

QSGMaterial

封装着色器程序的渲染状态

QSGMaterialRhiShader

表示独立于图形API的着色器程序

QSGMaterialShader

表示渲染器中的OpenGL着色器程序

QSGMaterialType

与QSGMaterial结合用作唯一类型令牌

QSGOpaqueTextureMaterial

在场景图中渲染纹理几何的便捷方法

QSGTextureMaterial

在场景图中渲染纹理几何的便捷方法

QSGVertexColorMaterial

Convenient way of rendering per-vertex colored geometry in the scene graph

便利节点

场景图API是低级的,专注于性能而不是便利。从头开始编写自定义的几何图形和材质,即使是最基本的几何图形和材质,也需要大量的代码。因此,API包含一些便利类,以使最常见的自定义节点易于使用。

QSGSimpleRectNode-QSGGeometryNode子类,它使用纯色材质定义矩形几何。

QSGSimpleTextureNode-QSGGeometryNode子类,它使用纹理材质定义矩形几何形状。

场景图与渲染

场景图的呈现发生在QQuickWindow类的内部,并且没有公共API可以访问它。但是,呈现管道中有一些地方可供用户附加应用程序代码。可通过直接调用场景图使用的图形API(OpenGL,Vulkan,Metal等)来添加自定义场景图内容或插入任意渲染命令。这个集成点由渲染循环定义。

共有三种渲染循环变体:基本,窗口和线程。其中,基本和窗口是单线程的,而线程在专用线程上执行场景图渲染。 Qt尝试根据平台以及可能使用的图形驱动程序选择合适的循环。如果这不令人满意,或者出于测试目的,则可以使用环境变量QSG_RENDER_LOOP强制使用给定的循环。要验证使用哪个渲染循环,请启用qt.scenegraph.general日志记录类别。

线程和Windows渲染循环依赖于图形API实现来进行节流,例如,在OpenGL的情况下,通过请求交换间隔为1。一些图形驱动程序允许用户忽略此设置并将其关闭,而忽略Qt的请求。在不阻塞交换缓冲区操作(或其他位置)的情况下,渲染循环将以太快的速度运行动画并使CPU旋转100%。如果已知系统无法提供基于vsync的限制,请使用基本渲染循环,而不是在环境中设置QSG_RENDER_LOOP = basic。

基于线程的渲染循环

在许多配置中,场景图渲染将在专用渲染线程上进行。这样做是为了增加多核处理器的并行度,并更好地利用停顿时间,例如等待阻塞交换缓冲区调用。这可以显着提高性能,但是对与场景图进行交互的位置和时间施加了某些限制。

【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击→领取「链接」

以下是有关如何使用线程渲染循环和OpenGL渲染帧的简单概述。除了OpenGL上下文的特定要求外,其他图形API的步骤也相同。

1、QML场景中发生更改,导致调用QQuickItem :: update()。例如,这可能是动画或用户输入的结果。事件被发布到渲染线程以启动新帧。

2、渲染线程准备绘制新帧。

3、在渲染线程准备新帧时,GUI线程调用QQuickItem :: updatePolish()对项目进行最终修饰,然后再渲染它们。

4、阻塞GUI线程。

5、发出QQuickWindow :: beforeSynchronizing()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection),以进行调用QQuickItem :: updatePaintNode()之前所需的任何准备工作。

6、将QML状态同步到场景图中。这是通过在自上一帧以来已更改的所有项目上调用QQuickItem :: updatePaintNode()函数来完成的。这是QML项与场景图中的节点唯一的交互。

7、释放GUI线程。

8、渲染场景图

8.1、发出QQuickWindow :: beforeRendering()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection),以使用自定义图形API调用,然后将其可视化地堆叠在QML场景下。

8.2、指定了QSGNode :: UsePreprocess的项目将调用其QSGNode :: preprocess()函数。

8.3、渲染器处理节点。

8.4、渲染器生成状态并记录使用中的图形API的绘制调用。

8.5、发出QQuickWindow :: afterRendering()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection)以发出自定义图形API调用,然后将这些调用可视化地堆叠在QML场景上。

8.6、现在帧已准备就绪。交换缓冲区(OpenGL),或记录当前命令,然后将命令缓冲区提交到图形队列(Vulkan,Metal)。 QQuickWindow :: frameSwapped()被发射。

9、在渲染线程正在渲染时,GUI可以自由地进行动画,处理事件等。

当前,默认情况下,线程渲染器可以在具有opengl32.dll的Windows平台、不包括Mesa llvmpipe的Linux平台、具有Metal的macOS平台、移动平台、具有EGLFS的嵌入式Linux平台以及所有Vulkan平台上使用,但这可能会有所更改。通过在环境中设置QSG_RENDER_LOOP = threaded,始终可以强制使用线程渲染器。

有关frameSwapped信号

当帧已排队等待呈现时,将发出此信号。启用垂直同步后,在连续动画场景中,每个vsync间隔最多发射一次信号。该信号将从场景图形渲染线程中发出。

渲染线程的渲染代码:

qtdeclarative\src\quick\scenegraph\qsgthreadedrenderloop.cpp
void QSGRenderThread::syncAndRender()
{
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
if (profileFrames) {
sinceLastTime = threadTimer.nsecsElapsed();
threadTimer.start();
}
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
QElapsedTimer waitTimer;
waitTimer.start();
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "syncAndRender()");
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage;
bool syncRequested = pendingUpdate & SyncRequest;
bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
sync(exposeRequested);//从这里调用各个QQuickItem(包括其继承类)的updatePaintNode函数
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
syncTime = threadTimer.nsecsElapsed();
#endif
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
int waitTime = vsyncDelta - (int) waitTimer.elapsed();
if (waitTime > 0)
msleep(waitTime);
return;
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started");
if (animatorDriver->isRunning()) {
d->animationController->lock();
animatorDriver->advance();
d->animationController->unlock();
}
bool current = false;
if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0)
current = gl->makeCurrent(window);
// Check for context loss.
if (!current && !gl->isValid()) {
// Cannot do anything here because gui is not locked. Request a new
// sync+render round on the gui thread and let the sync handle it.
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
}
if (current) {
d->renderSceneGraph(windowSize);//执行场景图的渲染操作,本质上是调用opengl命令
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopRender);
if (!d->customRenderStage || !d->customRenderStage->swap())
gl->swapBuffers(window);//交换前后缓冲区,将渲染的内容显示出来
d->fireFrameSwapped();
} else {
Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync, 1);
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window not ready, skipping render");
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering done");
// Though it would be more correct to put this block directly after
// fireFrameSwapped in the if (current) branch above, we don't do
// that to avoid blocking the GUI thread in the case where it
// has started rendering with a bad window, causing makeCurrent to
// fail or if the window has a bad size.
if (exposeRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- wake Gui after initial expose");
waitCondition.wakeOne();
mutex.unlock();
}
qCDebug(QSG_LOG_TIME_RENDERLOOP,
"Frame rendered with 'threaded' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)",
int(threadTimer.elapsed()),
int((syncTime/1000000)),
int((renderTime - syncTime) / 1000000),
int(threadTimer.elapsed() - renderTime / 1000000));
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSwap);
}

非线程的渲染循环(basic或windows)

当前,默认情况下,非线程渲染循环在具有ANGLE或非默认opengl32实现的Windows,具有OpenGL的macOS和具有某些驱动程序的Linux上使用。对于后者,这主要是一种预防措施,因为并非所有OpenGL驱动程序和窗口系统的组合都已经过测试。同时,诸如ANGLE或Mesa llvmpipe之类的实现根本无法在线程渲染中正常运行,因此,对于这些线程,必须不要使用线程渲染。

【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击→领取「链接」

在macOS和OpenGL上,使用XCode 10(10.14 SDK)或更高版本进行构建时,不支持线程渲染循环,因为这会选择在macOS 10.14上使用基于图层的视图。您可以使用Xcode 9(10.13 SDK)进行构建,以选择不支持图层支持,在这种情况下,线程渲染循环可用并且默认情况下使用。 Metal没有这样的限制。

默认情况下,Windows用于具有ANGLE的Windows上的非线程渲染,而当需要非线程渲染时,basic用于所有其他平台。

即使在使用非线程渲染循环时,也应该像使用线程渲染器一样编写代码,否则将使代码不可移植。

以下是非线程渲染器中帧渲染序列的简化图示。

使用QQuickRenderControl自定义渲染控制

使用QQuickRenderControl时,将驱动渲染循环的责任转移到应用程序中。在这种情况下,不使用内置的渲染循环。取而代之的是,应由应用程序在适当的时候调用抛光,同步和渲染步骤。可以实现类似于上述行为的线程行为或非线程行为。

混合场景图和本机图形API

场景图提供了两种方法来集成应用程序提供的图形命令:通过直接发出OpenGL,Vulkan,Metal等命令,以及在场景图中创建纹理化节点。

通过连接到QQuickWindow :: beforeRendering()和QQuickWindow :: afterRendering()信号,应用程序可以直接在场景图渲染到的同一上下文中进行OpenGL调用。使用Vulkan或Metal之类的API,应用程序可以通过QSGRendererInterface查询本机对象,例如场景图的命令缓冲区,并在认为合适的情况下向其记录命令。如信号名称所示,用户随后可以在Qt Quick场景下或上方渲染内容。以这种方式集成的好处是不需要额外的帧缓冲区或内存来执行渲染,并且消除了可能昂贵的纹理化步骤。缺点是Qt Quick决定何时调用信号,这是唯一允许OpenGL应用程序绘制的时间。

另一个方法(当前仅适用于OpenGL)是创建一个QQuickFramebufferObject,将其渲染到其中,然后将其作为纹理显示在场景图中。 “场景图-渲染FBO”示例显示了如何完成此操作。还可以组合多个渲染上下文和多个线程以创建要在场景图中显示的内容。场景图-线程示例中的渲染FBO显示了如何完成此操作。

即使QQuickFramebufferObject当前不支持,除OpenGL之外的其他图形API也可以采用这种方法。 “场景图-金属纹理导入”示例中演示了直接使用基础API创建和渲染纹理,然后在自定义QQuickItem中的Qt Quick场景中包装和使用此资源。该示例使用了Metal,但是概念也适用于所有其他图形API。

警告:将OpenGL内容与场景图形渲染混合时,重要的是应用程序不要使OpenGL上下文处于缓冲区绑定,启用属性,z缓冲区或模版缓冲区中的特殊值或类似状态。这样做可能导致无法预测的行为。

警告:自定义渲染代码应该意识到是在线程中执行,而不是在应用程序的GUI(主)线程上执行。

使用QPainter的自定义Item

QQuickItem提供了一个子类QQuickPaintedItem,它允许用户使用QPainter渲染内容。

警告:使用QQuickPaintedItem通过软件光栅化或OpenGL帧缓冲对象(FBO)使用间接2D表面来渲染其内容,因此渲染是一个两步操作。首先栅格化表面,然后绘制表面。直接使用场景图API总是非常快。

日志功能

场景图支持许多日志记录类别。除了对Qt贡献者有所帮助之外,这些还可用于跟踪性能问题和错误。

qt.scenegraph.time.texture-记录进行纹理上传所花费的时间

qt.scenegraph.time.compilation-记录进行着色器编译所花费的时间

qt.scenegraph.time.renderer-记录渲染器各个步骤所花费的时间

qt.scenegraph.time.renderloop-记录渲染循环各个步骤所花费的时间

qt.scenegraph.time.glyph-记录准备距离场字形所花费的时间

qt.scenegraph.general-记录有关场景图和图形堆栈各个部分的常规信息

qt.scenegraph.renderloop-创建渲染所涉及的各个阶段的详细日志。此日志模式主要对使用Qt的开发人员有用。

旧版QSG_INFO环境变量也可用。将其设置为非零值将启用qt.scenegraph.general类别。

注意:遇到图形问题时,或不确定正在使用哪个渲染循环或图形API时,请始终在至少启用qt.scenegraph.general和qt.rhi。*或设置QSG_INFO = 1的情况下启动应用程序。然后,这将在初始化期间将一些基本信息打印到调试输出上。

除公共API外,场景图还具有适应层,该适应层打开实现以进行硬件特定的适应。这是一个未公开的内部和专用插件API,可让硬件适应小组充分利用其硬件。这包括:

自定义纹理:特别是QQuickWindow :: createTextureFromImage的实现以及Image和BorderImage类型使用的纹理的内部表示。

自定义渲染器:适配层使插件可以决定如何遍历和渲染场景图,从而有可能针对特定硬件优化渲染算法或使用可提高性能的扩展。

许多默认QML类型的自定义场景图实现,包括其文本和字体渲染。

自定义动画驱动程序:允许动画系统连接到低级显示设备的垂直刷新中,以获得平滑的渲染。

自定义渲染循环:可以更好地控制QML如何处理多个窗口。

相关推荐

小车五位自动循环往返控制_小车自动往返控制系统

需求描述:用三相异步电动机拖动一辆小车在A、B、C、D、E五点之间自动循环往返运行,小车初始在A点,按下启动按钮,小车依次前进到B、C、D、E点,并分别停止2s返回到A点停止。按下停止...

自动灌溉系统_自动灌溉系统by

需求描述:PLC时钟设定每日6:00、18:00自动启动灌溉系统,每次运行15分钟后停止;非定时时段按下手动灌溉按钮,立即启动并运行15分钟;土壤湿度传感器检测到湿润时,跳过本次定时灌溉...

主板ERP开启还是关闭好_主板设置erp是什么

主板功能的开启与关闭,本质是在“节能环保”和“使用便利”之间做选择。为帮你快速决策,先给出直接结论,再深入解析原理、影响及操作步骤,让你根据自身需求精准设置。一、直接结论:ERP功能如何选?...

新电脑必做5项设置!做完再玩,流畅安全多用三年

刚拿到新电脑,兴奋之余先别急着开机畅玩!做好以下这5大设置,能让你的爱机长期保持流畅如新,安全又省心。尤其是最后一招,很多老用户都不知道!1关闭隐私常规,杜绝数据偷跑新电脑首次开机进行系统初始化时,...

属于 PHP 开发者的 Supervisor 实用指南

属于PHP开发者的Supervisor实用指南在PHP开发中,我们经常需要运行一些后台进程:队列处理、长时间运行的脚本、WebSocket服务器等。这些进程可能会因为各种原因意外退出,手...

领导半夜12点微信派活?三句高情商回复,反手拿捏让他不敢再烦

友友们大家来啦!今天来和大家一起分享精彩话题老规矩先点赞再看文!0102别在这里害人了,现在能保住工作就烧高香了,再得瑟,明天早上去办离职0304很简单,把他一起拉上,每半小时打电话或语音汇报,一两次...

"零点黑科技!硬盘自动备份+离线神操作,服务器数据安全躺赢"

公司有一台服务器,数据库需要每天零点进行数据库备份,要求在本机备份一次,再在移动硬盘上异地备份一次。备份完成后硬盘自动离线。具体思路如下:数据库自动备份时间为每天0点,备份过程约需1分钟。0点时开启硬...

峰谷电:白天贵、晚上便宜,你家真的适合开通吗?

电费单又超预算了?别急着关掉空调,其实你可能错过了一个"电费打折"的机会——峰谷电。它就像电影院的日场和夜场票,白天贵、晚上便宜,聪明利用,电费真的能省下来。一、峰谷电是什么?峰谷电把...

电脑开机密码设置全指南:从基础到进阶的安全防护

在数字化时代,电脑存储着大量个人隐私和重要数据,设置开机密码是保护信息安全的第一道防线。本文将系统介绍Windows、macOS、Linux三大主流操作系统及BIOS层面的密码设置方法,同时涵盖密码管...

自动喷香机_香薰机自动喷香机

需求描述:PLC时钟设定每日9:00、14:00、18:00自动启动喷雾,每次喷雾3秒后停止;非定时时段按下手动喷雾按钮,立即喷雾3秒;香薰液缺液传感器检测到液位过低时,停止喷雾并亮报警...

macbook系统自动启动项在哪里查看

了解和管理MacBook的开机自动启动项,是优化系统启动速度和运行效率的好方法。下面我来为你介绍几种查看和管理这些启动项的方法。查看和管理MacBook启动项1.通过系统设置(最简单直接的方法)...

想让电脑自己到点开机和关机?这4个超实用的设置方法快收好!

嘿,你是不是也经常忙到忘记关电脑?或者早上想用电脑时发现还没开机?别慌,今天我就跟你分享几个超实用的方法,帮你轻松搞定电脑的定时开关机设置。不管你是电脑小白还是有点基础,这篇教程都能让你秒懂操作,省时...

定时关机这样操作小白也会 一招设定工作日关机 指定时间关机

在日常使用电脑的过程中,我们常常会遇到这样的情况:晚上睡觉前忘记手动关机,导致电脑整夜运行,既浪费电又缩短硬件寿命;或者在下载大文件时,需要等待很长时间才能完成,却不能一直守在电脑前,下载完成后也无法...

日本无线电操作证试题,这些问题你能答的上来吗?

一直以来,我们对于日本的业余无线电的印象都停留在“操作能力强,爱好者数目众多”上,然而我们对于他们的业余无线电体系所知甚少。日本业余无线电操作证的等级分作四级,最基本的四级操作证书具有8MHz以下、2...

你知道吗?单边带信号就像DNA分子一样!

我们在准备B级操作证书的过程中,避免不了的要接触到一个新的名词——SSB。单边带是传统AM模式的一种特殊的形式,在传送相同的信息的过程中,其占用的带宽仅为AM模式的一半,那么SSB模式到底是怎样的一种...

取消回复欢迎 发表评论: