体积渲染

渲染体积对象

体积渲染展示了如何使用QCustom3DVolume 显示体积数据。

运行示例

要运行来自 Qt Creator,打开Welcome 模式,然后从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行

初始化卷项目

QCustom3DVolume 项是特殊的自定义项(请参阅QCustom3DItem ),可用于显示体积数据。体积项仅支持正投影,因此首先要确保图形使用了正投影:

m_graph->setOrthoProjection(true);

创建一个与坐标轴数据范围绑定的体积项:

m_volumeItem = new QCustom3DVolume;
// Adjust water level to zero with a minor tweak to y-coordinate position and scaling
m_volumeItem->setScaling(
            QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(),
                      (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f,
                      m_graph->axisZ()->max() - m_graph->axisZ()->min()));
m_volumeItem->setPosition(
            QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f,
                      -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) +
                      (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f,
                      (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f));
m_volumeItem->setScalingAbsolute(false);

通过将QCustom3DItem::scalingAbsolute 属性设置为false ,表示体积的缩放应随数据范围的变化而变化。 接下来,定义体积的内部内容:

m_volumeItem->setTextureWidth(lowDetailSize);
m_volumeItem->setTextureHeight(lowDetailSize / 2);
m_volumeItem->setTextureDepth(lowDetailSize);
m_volumeItem->setTextureFormat(QImage::Format_Indexed8);
m_volumeItem->setTextureData(new QList<uchar>(*m_lowDetailData));

纹理使用八位索引颜色,因为它结构紧凑,而且无需重置整个纹理即可轻松调整颜色。对于纹理数据,请使用之前根据高度贴图创建的数据。通常情况下,体积项目的数据是以图像堆栈的形式预先生成的,因此可以跳过数据生成细节。有关实际数据生成过程的更多信息,请参阅示例代码。

由于使用的是八位索引颜色,因此需要一个颜色表来将八位颜色索引映射到实际颜色。在典型的使用案例中,你会从源图像中获取颜色表,而不是使用手动定义的颜色表:

m_volumeItem->setColorTable(m_colorTable1);

要选择在体积周围显示切片框架,请初始化它们的属性。最初,框架将被隐藏:

m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f));
m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f));
m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f));
m_volumeItem->setDrawSliceFrames(false);

最后,将体积作为自定义项添加到图表中以显示它:

m_graph->addCustomItem(m_volumeItem);

切入体积

除非体积在很大程度上是透明的,否则您只能看到它的表面,这通常不会有太大帮助。QCustom3DVolume 提供了两种显示切片的方法。第一种是显示所选的切片来代替卷。例如,要指定一个垂直于 X 轴的切片,请使用以下方法:

m_volumeItem->setSliceIndexX(m_sliceIndexX);

要显示上述指定的切片,还必须设置QCustom3DVolume::drawSlices 属性:

m_volumeItem->setDrawSlices(true);

查看切片的第二种方法是使用QCustom3DVolume::renderSlice() 方法,该方法可从指定切片生成QImage 。然后,该图像可以显示在另一个 widget 上,如QLabel

m_sliceLabelX->setPixmap(
            QPixmap::fromImage(m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX)));

调整体积透明度

有时,只查看切片并不能很好地了解卷的内部结构。QCustom3DVolume 提供了两个属性,可用于调整卷的透明度:

m_volumeItem->setAlphaMultiplier(mult);
    ...
m_volumeItem->setPreserveOpacity(enabled);

QCustom3DVolume::alphaMultiplier 是一个通用乘数,应用于体积每个体素的 alpha 值。它可以为体积中已经有些透明的部分增加均匀的透明度,以显示内部不透明的细节。除非QCustom3DVolume::preserveOpacity 属性设置为false ,否则该乘数不会影响完全不透明的颜色。

调整体积透明度的另一种方法是直接调整体素的 alpha 值。对于八位索引纹理,只需修改和重置色彩表即可实现:

int newAlpha = enabled ? terrainTransparency : 255;
for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) {
    QRgb oldColor1 = m_colorTable1.at(i);
    QRgb oldColor2 = m_colorTable2.at(i);
    m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha);
    m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha);
}
if (m_usingPrimaryTable)
    m_volumeItem->setColorTable(m_colorTable1);
else
    m_volumeItem->setColorTable(m_colorTable2);

高清与低俗着色器

默认情况下,体积渲染使用高清着色器。在对体积内容进行光线追踪时,它会以正确的权重计算体积的每个体素,从而精确呈现体积的更多细节。然而,这样做的计算成本非常高,因此帧频会受到影响。如果渲染速度比体积内容的像素精确度更重要,可通过设置QCustom3DVolume::useHighDefShader 属性false ,使用速度更快的低清晰度着色器。低清晰度着色器通过牺牲精度来提高速度,因此它不能保证对体积的每个体素都进行采样。这可能会导致在较精细的体绘制细节上出现闪烁或其他渲染伪像。

m_volumeItem->setUseHighDefShader(enabled);

示例内容

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.