本页内容

体积渲染

体素对象的渲染。

“体积渲染”演示了如何使用 QCustom3DVolume 来显示体积数据。

包含水域和陆地层的3D地形体积渲染

运行示例

您可以通过以下方式运行示例:

初始化体积项

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));

纹理请使用 8 位索引色彩,因为这种格式体积小,且便于调整颜色而无需重置整个纹理。纹理数据请使用之前基于高度图生成的数据。通常,体积项的数据是以图像堆栈的形式预先生成的,因此可以跳过数据生成的详细步骤。 有关实际数据生成过程的更多信息,请参阅示例代码。

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

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 `。然后,该图像可以在另一个小部件(例如 `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 值。对于 8 位索引纹理,只需修改并重置颜色表即可:

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

© 2026 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.