【Qwt 7.0 系列】3D 数据可视化 —— OpenGL 高性能三维绘图
本文是 Qwt 7.0 系列介绍和教程,如果你正在寻找一个高性能、协议友好、同时支持 2D 和 3D 绘图的 Qt 数据可视化库,那么这篇文章就是为你准备的。
系列总述文章:Qwt 7.0 —— 基于 Qt 的高性能 2D/3D 绘图库
概述 | 高性能曲线绘制 | 常用图表类型 | 高级科学图表 | 多坐标轴与布局 | 交互功能 | 3D 数据可视化 | 坐标轴与刻度 | 控件与辅助元素 | 总体架构解析 | matplotlib 风格绘图
一、引言:为什么要在 Qwt 里做 3D?
科学计算和工程分析中,三维数据可视化几乎是绕不开的需求——有限元分析的热力分布、地形高程模型、电磁场强度曲面,都需要一张能旋转、能缩放的三维图来把数据看清楚。
在过去,如果用 Qt 做 2D 绘图你会选 Qwt,但要做 3D 又得去引入另一个独立的 QwtPlot3D 库。两个库版本不同步、API 风格不一致、构建配置各搞各的,维护成本很高。
Qwt 7.0 改变了这一点。 这个基于原版 Qwt 6.2.0 的现代化维护分支,从 7.1 起将原 QwtPlot3D 库完整整合进来,实现了 2D/3D 一体化:一个库、一套构建系统、统一的 CMake target,同时支持二维曲线图和三维表面图。不仅如此,7.0 还新增了 3D 主题系统 和 22 种科学 colormap 预设,让三维图表的视觉风格可以一键切换。
本篇就带你从零上手 Qwt 7.0 的 3D 绘图模块。
二、3D 绘图模块概述
2.1 三库结构中的 plot3d
回顾系列的架构介绍,Qwt 7.0 由三个共享库组成:
┌──────────────────────────┐
│ qwt::core │ ← 基础工具库(颜色、数学、数据类型、变换)
└──────────────────────────┘
↗ ↖
┌──────────────┐ ┌───────────────┐
│ qwt::plot │ │ qwt::plot3d │
│ (2D) │ │ (3D) │
└──────────────┘ └───────────────┘
qwt::plot3d 就是 3D 绘图模块,它链接 qwt::core(复用 colormap、数学工具等),但与 qwt::plot(2D)互不依赖。这意味着你只想用 3D 功能时,不必把 2D 也链接进来。
构建时通过 CMake 选项 QWT_CONFIG_QWTPLOT_3D(默认 ON)控制是否编译该模块。
2.2 核心类一览
3D 模块的所有类都位于 Qwt3D 命名空间下,使用时记得加 Qwt3D:: 前缀,或在源文件中写 using namespace Qwt3D;。
| 类名 | 说明 |
|---|---|
Qwt3D::Plot3D | 3D 绘图基类,提供基本框架和交互能力 |
Qwt3D::SurfacePlot | 3D 表面图,显示连续曲面,同时支持网格和单元数据 |
Qwt3D::Function | 3D 函数绘图,根据数学函数 z = f(x, y) 生成曲面 |
Qwt3D::GraphPlot | 图形类 3D 绘图的中间基类 |
Qwt3D::Axis | 3D 坐标轴配置 |
Qwt3D::ColorLegend | 3D 颜色条(图例) |
Qwt3D::Qwt3DTheme | 3D 主题系统,封装背景、网格、colormap、坐标轴、光照等全部视觉属性 |
类的继承关系很清晰:SurfacePlot 继承自 Plot3D,Function 则是一个被 Plot3D 使用的辅助类,负责把数学函数转换成曲面数据。
新手避坑:类名是
Plot3D和SurfacePlot,不要写成Qwt3DPlot3D或Qwt3DSurfacePlot。正确的完整写法是Qwt3D::Plot3D、Qwt3D::SurfacePlot。命名空间Qwt3D和类名之间用::分隔,不要把Qwt3D当成类名前缀拼进去。
2.3 主要特性
- 多种绘图类型:表面图、网格图、参数曲面等
- OpenGL 渲染:利用 GPU 实现高性能三维渲染
- 交互操作:鼠标旋转视角、平移、滚轮缩放
- 光照和材质:支持光照效果和材质参数配置
- 主题系统:一键切换视觉风格,10 种预设主题 + 22 种科学色彩映射
三、基本使用:画出你的第一个 3D 曲面
我们从最经典的例子开始——根据一个数学函数 z = sin(x) * cos(y) 生成三维曲面。对应的示例代码位于 examples/3D/simpleplot3D,效果如下图:

3.1 完整代码示例
#include <qwt3d_surfaceplot.h>
#include <qwt3d_function.h>
using namespace Qwt3D;
// 1. 定义数学函数:继承 Function,重载 operator()
class MyFunction : public Function
{
public:
// z = f(x, y)
double operator()(double x, double y) override
{
return std::sin(x) * std::cos(y);
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
// 2. 创建表面图控件
SurfacePlot* plot = new SurfacePlot();
// 3. 创建函数对象并绑定到绘图
MyFunction* func = new MyFunction(*plot);
// 4. 设置数据范围(x 和 y 的区间)和网格分辨率
func->setDomain(-5, 5, -5, 5); // x ∈ [-5, 5], y ∈ [-5, 5]
func->setMesh(50, 50); // 50 x 50 网格
// 5. 生成曲面数据
func->create();
// 6. 设置视角:X、Y、Z 轴旋转角度(单位:度)
plot->setRotation(30, 0, 45);
// 7. 启用鼠标交互
plot->enableMouse(true);
// 8. 显示
plot->show();
return app.exec();
}
3.2 代码解读
这段代码的逻辑分为四步:
第一步:定义函数。 继承 Qwt3D::Function,重载 operator()(double x, double y),返回值就是曲面在该点的 Z 坐标。想画什么样的曲面,改这一个函数就行。
第二步:创建绘图控件。 SurfacePlot 是 Plot3D 的子类,专门用于显示连续曲面。它同时支持网格数据和单元数据两种模式。
第三步:配置数据范围和分辨率。 setDomain() 决定函数在 X、Y 方向的取值区间;setMesh() 决定采样网格的密度(这里是 50×50,共 2500 个采样点)。网格越密,曲面越平滑,但渲染开销也越大。create() 负责真正执行采样并把数据附加到绘图上。
第四步:设置视角并显示。 setRotation(30, 0, 45) 表示绕 X 轴旋转 30°、Y 轴 0°、Z 轴 45°。enableMouse(true) 开启鼠标交互后,你可以用左键拖动旋转、中键拖动平移、滚轮缩放。
3.3 从数据数组加载
除了用数学函数生成,你也可以直接喂一组已有的 Z 值数据:
#include <qwt3d_surfaceplot.h>
using namespace Qwt3D;
SurfacePlot* plot = new SurfacePlot();
// 分配 100x100 的 Z 值数组
double* zData[100];
for (int i = 0; i < 100; ++i)
zData[i] = new double[100];
// ... 在此处填充你的实际数据 ...
// 加载 Z 值数据,需显式指定 X/Y 范围
plot->loadFromData(zData, 100, 100, 0.0, 100.0, 0.0, 100.0);
// setResolution 控制下采样:1 表示用全部数据,值越大下采样越强
plot->setResolution(1);
setResolution() 是个性能调节开关:当数据量很大时,调大这个值会让绘图只用部分数据来渲染,牺牲一点精度换取流畅度。
3.4 交互与视角控制
// 启用鼠标交互
plot->enableMouse(true);
// 鼠标操作说明:
// 左键拖动 → 旋转视角
// 中键拖动 → 平移
// 滚轮 → 缩放
// 用代码设置缩放比例(X、Y、Z 三个方向)
plot->setScale(1.0, 1.0, 1.0);
// 用代码设置旋转角度
plot->setRotation(45, 30, 60);
四、3D 主题系统:一键切换视觉风格
如果你用过 matplotlib 的样式表(plt.style.use('dark')),那对 Qwt 7.0 的 3D 主题系统会感到很亲切。Qwt3D::Qwt3DTheme 把 3D 绘图的全部视觉属性——背景色、网格色与线宽、数据色彩映射、坐标轴颜色、标题样式、光照预设、着色模式、材质参数——打包成一个对象,一行代码就能整体切换。
这是 Qwt 7.0 新增的能力,原版 Qwt 6.x 并没有这套主题系统。
4.1 十种内置预设主题
| 预设名称 | 说明 |
|---|---|
Default | 白底 + jet 色彩映射 + 无光照 |
Dark | 深灰底 + viridis + 柔和光照 |
Scientific | 白底 + jet + 工作室光照 |
Warm | 暖色底 + hot 色彩映射 |
Cool | 冷色底 + cool 色彩映射 |
Matplotlib | matplotlib 风格(viridis + 柔和光照) |
EarthTones | 大地色调 + autumn 色彩映射 |
Ocean | 海洋色调 + winter 色彩映射 |
HighContrast | 黑底白线,高对比度 |
Presentation | 大字体 + 粗线条,适合演示投屏 |
4.2 三种使用方式
#include <qwt3d_theme.h>
// 方式 1:用枚举值应用预设主题(推荐)
plot->applyTheme(Qwt3D::Qwt3DTheme::Dark);
// 方式 2:用字符串名称应用主题
plot->applyTheme("Scientific");
// 方式 3:基于预设做自定义修改后再应用
Qwt3D::Qwt3DTheme theme(Qwt3D::Qwt3DTheme::Scientific);
theme.setDataColorPreset("plasma"); // 换成 plasma 色彩映射
theme.setShininess(20.0); // 调整材质光泽度
theme.setLightingPreset(Qwt3D::Qwt3DTheme::Studio); // 换光照
theme.apply(plot); // plot 是 Qwt3D::Plot3D* 指针
方式 3 最灵活:以一个预设为起点,只改你关心的那几个属性,其余沿用预设值。
4.3 二十二种科学 colormap 预设
主题系统里的数据色彩映射,底层复用的是 core 模块的 QwtColorMapPreset,提供 22 种科学可视化标准色彩映射。这是 Qwt 7.0 的 colormap 预设系统,2D 和 3D 共用同一套:
| 类别 | 预设名称 |
|---|---|
| 感知均匀(推荐用于顺序型数据) | viridis、plasma、inferno、magma、cividis |
| 经典 | jet、hot、cool、spring、summer、autumn、winter |
| 灰度 | gray、bone、copper |
| 彩虹 | rainbow、hsv、turbo |
| 发散(适合有正负的数据) | coolwarm、rdylbu、rdylgn、spectral |
// 切换色彩映射
theme.setDataColorPreset("viridis");
// 查询所有可用预设
QStringList presets = QwtColorMapPreset::availablePresets();
选色建议:
viridis、plasma这类感知均匀的色彩映射对色盲友好,且在打印成灰度时仍能保持信息层次,是科研论文的首选。jet虽然经典,但在某些区段会有"假边界"现象,慎用于精确读数场景。
4.4 五种光照预设
| 预设 | 说明 |
|---|---|
NoLighting | 无光照,纯色平面渲染 |
FlatLight | 均匀环境光,无强阴影 |
Studio | 经典三点照明(主光 + 辅光 + 轮廓光) |
Outdoor | 强方向光 + 环境光,阴影明显 |
Soft | 柔和漫射光,过渡自然 |
光照让曲面有了立体感。NoLighting 适合需要精确辨识颜色的场景(比如热力图),Studio 和 Soft 适合展示和汇报。
4.5 直接使用 colormap(不通过主题)
如果你不想动整套主题,只想给数据上个色:
#include <qwt3d_colormap_color.h>
using namespace Qwt3D;
// 显示颜色条(图例)
plot->showColorLegend(true);
// 用 core 模块的 colormap 预设,按 Z 值映射颜色
plot->setDataColor(new ColorMapColor(plot, "viridis"));
ColorMapColor 是一个适配器,把 core 模块的 QwtColorMap 桥接到 3D 模块的 Qwt3D::Color 接口,所以那 22 种预设可以直接用在 3D 表面图上。
五、3D 绘图增强:坐标轴、颜色条与标注
光有曲面还不够,一张合格的工程图表需要清晰的坐标轴、图例和标注。Qwt 7.0 的 3D 模块提供了这些增强能力。
5.1 坐标轴配置
3D 绘图有三条坐标轴,通过 Qwt3D::Axis 可以配置刻度、标签、标题等。对应示例位于 examples/3D/axes:

using namespace Qwt3D;
// 获取某条坐标轴(以底面 X 轴为例)
Axis* xAxis = plot->axis(Plot3D::AxisX1);
// 设置轴标题
xAxis->setLabelString("X Axis (mm)");
// 设置刻度数和数值格式
xAxis->setMajors(5); // 主刻度数
xAxis->setMinors(3); // 次刻度数
5.2 颜色条与注释标注
颜色条(color legend)告诉读者"这个颜色对应什么数值",是热力图和表面图的标配。examples/3D/enrichments 演示了更丰富的增强效果,包括自定义标注:

// 显示颜色条
plot->showColorLegend(true);
5.3 自动切换与动态曲面
examples/3D/autoswitch 展示了在不同绘图风格间自动切换的效果:

而 examples/3D/figureSurface3D 则展示了 3D 曲面与 QwtFigure(类似 matplotlib Figure 的多绘图布局容器)的集成,可以在一个窗口里管理多个 3D 子图并支持拖动、缩放:

六、OpenGL 渲染:为什么 3D 需要 GPU 加速
3D 绘图天然依赖 OpenGL——旋转一个有 2500 个顶点的曲面,每秒要重绘几十帧,CPU 软件渲染很难扛住。Qwt 7.0 的 3D 模块直接基于 OpenGL 和 GLU 实现,GPU 渲染是它的底座。
有意思的是,Qwt 的 OpenGL 加速能力并不局限于 3D。即使是 2D 绘图,也提供了 OpenGL 画布选项来应对大数据量场景。
6.1 CMake 配置
使用 OpenGL 需要在 CMake 中引入 Qt 的 OpenGL 模块:
find_package(Qt${QT_VERSION_MAJOR} ${QWT_MIN_QT_VERSION} COMPONENTS
OpenGL
REQUIRED
)
target_link_libraries(${QWT_APP_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::OpenGL
)
# Qt6 还需要 OpenGLWidgets 模块
if(${QT_VERSION_MAJOR} EQUAL 6)
find_package(Qt${QT_VERSION_MAJOR} ${QWT_MIN_QT_VERSION} COMPONENTS
OpenGLWidgets
REQUIRED
)
target_link_libraries(${QWT_APP_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
)
endif()
6.2 QwtPlotOpenGLCanvas(2D 的 OpenGL 加速)
QwtPlotOpenGLCanvas 继承自 QOpenGLWidget,直接在 GPU 上渲染 2D 绘图内容。相比默认的 QwtPlotCanvas,在处理大数据集和实时更新时性能显著提升:
#include <qwt_plot.h>
#include <qwt_plot_opengl_canvas.h>
#include <qwt_plot_curve.h>
auto* plot = new QwtPlot("OpenGL Plot");
// 用 OpenGL 画布替换默认画布
auto* canvas = new QwtPlotOpenGLCanvas(plot);
canvas->setFrameStyle(QFrame::Box | QFrame::Plain);
canvas->setLineWidth(1);
plot->setCanvas(canvas);
// 照常添加曲线
auto* curve = new QwtPlotCurve("Data");
// ... 设置数据 ...
curve->attach(plot);
还可以传入自定义 QSurfaceFormat 做高级配置,比如开启 MSAA 抗锯齿:
QSurfaceFormat format;
format.setSamples(4); // 4 倍 MSAA 抗锯齿
format.setDepthBufferSize(24);
auto* canvas = new QwtPlotOpenGLCanvas(format, plot);
plot->setCanvas(canvas);
6.3 两种加速方案怎么选
除了 QwtPlotOpenGLCanvas,还有一种是给标准 QwtPlotCanvas 开启 OpenGLBuffer 属性:
auto* canvas = new QwtPlotCanvas(plot);
canvas->setPaintAttribute(QwtPlotCanvas::OpenGLBuffer, true);
plot->setCanvas(canvas);
两者各有适用场景:
| 场景 | 推荐方案 |
|---|---|
| 静态或小数据集(<1 万点) | 默认 QwtPlotCanvas 即可 |
| 大数据集(>10 万点) | QwtPlotOpenGLCanvas,交互更流畅 |
| 实时流式数据 | QwtPlotOpenGLCanvas,帧率更稳定 |
| 复杂布局的混合控件 UI | QwtPlotCanvas + OpenGLBuffer,集成更友好 |
简单说:QwtPlotOpenGLCanvas 直接渲染到 QOpenGLWidget,性能更好;OpenGLBuffer 方案在包含复杂混合排布的桌面应用中集成更顺畅。
3D 模块的 OpenGL 依赖:3D 绘图模块(
qwt::plot3d)本身就依赖 OpenGL 和 GLU 库,使用前请确保系统已安装 OpenGL 驱动和 GLU。这一点和 2D 可选 OpenGL 不同——3D 是必须的。
七、与旧版本的区别
如果你之前用过原版 Qwt 6.x,这里有几个关键变化值得注意:
| 对比项 | 原版 Qwt 6.x | Qwt 7.0 |
|---|---|---|
| 3D 绘图 | 需单独引入独立的 QwtPlot3D 库,版本不同步、构建配置独立 | 从 7.1 起整合为 qwt::plot3d,与 2D 同库同构建系统 |
| 主题系统 | 无,视觉属性需逐项手动设置 | 新增 Qwt3D::Qwt3DTheme,10 种预设一键切换 |
| 色彩映射 | 3D 自有色彩逻辑,与 2D 不互通 | 新增 QwtColorMapPreset,22 种科学 colormap,2D/3D 共用 |
| colormap 适配 | 需自行桥接 | 提供 ColorMapColor 适配器,自动桥接 core 模块 |
| 光照预设 | 无系统化方案 | 5 种光照预设(NoLighting/FlatLight/Studio/Outdoor/Soft) |
| CMake target | 无统一 target | qwt::plot3d,target_link_libraries 一行搞定 |
对老项目迁移来说,最大的好处是不用再维护一个游离的 QwtPlot3D 依赖了——升级到 Qwt 7.0 后,2D 和 3D 用同一个库版本、同一套构建配置,省心很多。
八、核心方法速查表
最后整理一张常用方法速查表,方便查阅:
| 方法 | 所属类 | 说明 |
|---|---|---|
setDomain() | Function / GridMapping | 设置 X/Y 数据范围 |
setMesh() | Function / GridMapping | 设置网格分辨率(列、行) |
setResolution() | SurfacePlot | 设置数据分辨率(1 = 用全部数据,值越大下采样越强) |
loadFromData() | SurfacePlot | 加载数据数组到绘图 |
create() | Function | 生成并附加曲面数据 |
setRotation() | Plot3D | 设置 X/Y/Z 旋转角度(度) |
setScale() | Plot3D | 设置 X/Y/Z 缩放比例 |
enableMouse() | Plot3D | 启用/禁用鼠标交互 |
showColorLegend() | Plot3D | 显示/隐藏颜色条 |
setDataColor() | Plot3D | 设置数据颜色映射函数 |
updateData() | Plot3D | 重新计算并更新数据 |
applyTheme() | Plot3D | 应用主题(枚举或名称) |
九、实践建议与总结
写到这里,关于 Qwt 7.0 的 3D 数据可视化就介绍完了。最后给几条实践建议:
- 控制数据规模:3D 渲染对数据量比 2D 敏感,推荐 100×100 网格以下。数据太大时用
setResolution()降采样。 - 善用主题系统:与其逐项手动设颜色,不如先
applyTheme()选一个接近的预设,再做微调,代码量少很多。 - 色彩映射选 viridis 系:感知均匀、色盲友好、打印安全,是科研场景的稳妥选择。
- 2D 大数据也别忘了 OpenGL:即使不做 3D,2D 绘图超过 10 万点时,换
QwtPlotOpenGLCanvas能明显提升交互流畅度。
系列文章
- 第 1 篇:快速入门与核心新特性概览
- 第 2 篇:曲线绘图详解 —— 从基础到百万级数据性能优化
- 第 3 篇:常用图表类型实战 —— 柱状图、散点图、箱线图与直方图
- 第 4 篇:高级科学图表 —— 光谱图、向量场、K线图与极坐标绘图
- 第 5 篇:多坐标轴与多绘图布局 —— 寄生绘图与 QwtFigure 容器
- 第 6 篇:交互功能详解 —— 平移、缩放、坐标轴交互与数据拾取
- 第 7 篇:3D 数据可视化 —— OpenGL 高性能三维绘图
- 第 8 篇:坐标轴与刻度系统 —— 刻度引擎、网格、图例与刻度朝内
- 第 9 篇:控件与辅助元素 —— 滑块旋钮、标记与装饰
- 第 10 篇:总体架构解析 —— 从单体到三库模块化的演进
- 第 11 篇:matplotlib 风格绘图 —— QwtPyPlot 接口详解
相关链接
- 项目地址:https://github.com/czyt1988/QWT
- Gitee 镜像:https://gitee.com/czyt1988/QWT
- 在线文档:https://czyt1988.github.io/QWT/zh/
- 系列总述:https://blog.csdn.net/czyt1988/article/details/160193393
65

被折叠的 条评论
为什么被折叠?



