summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/datavisualization/volumetric/main.cpp41
-rw-r--r--examples/datavisualization/volumetric/volumetric.cpp44
-rw-r--r--examples/datavisualization/volumetric/volumetric.h4
-rw-r--r--src/datavisualization/data/customrenderitem.cpp8
-rw-r--r--src/datavisualization/data/customrenderitem_p.h12
-rw-r--r--src/datavisualization/data/qcustom3dvolume.cpp251
-rw-r--r--src/datavisualization/data/qcustom3dvolume.h10
-rw-r--r--src/datavisualization/data/qcustom3dvolume_p.h10
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp10
-rw-r--r--src/datavisualization/engine/shaders/texture3d.frag14
-rw-r--r--src/datavisualization/engine/shaders/texture3dslice.frag54
-rw-r--r--src/datavisualization/utils/shaderhelper.cpp16
-rw-r--r--src/datavisualization/utils/shaderhelper_p.h4
-rw-r--r--tests/volumetrictest/volumetrictest.cpp4
14 files changed, 372 insertions, 110 deletions
diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp
index 5a90070e..84062969 100644
--- a/examples/datavisualization/volumetric/main.cpp
+++ b/examples/datavisualization/volumetric/main.cpp
@@ -36,16 +36,18 @@ int main(int argc, char **argv)
QWidget *container = QWidget::createWindowContainer(graph);
QSize screenSize = graph->screen()->size();
- container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
+ container->setMinimumSize(QSize(screenSize.width() / 3, screenSize.height() / 3));
container->setMaximumSize(screenSize);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
container->setFocusPolicy(Qt::StrongFocus);
- QWidget *widget = new QWidget;
+ QWidget *widget = new QWidget();
QHBoxLayout *hLayout = new QHBoxLayout(widget);
QVBoxLayout *vLayout = new QVBoxLayout();
+ QVBoxLayout *vLayout2 = new QVBoxLayout();
hLayout->addWidget(container, 1);
hLayout->addLayout(vLayout);
+ hLayout->addLayout(vLayout2);
widget->setWindowTitle(QStringLiteral("Volumetric Object Example"));
@@ -122,10 +124,17 @@ int main(int argc, char **argv)
sliceImageYLabel->setScaledContents(true);
sliceImageZLabel->setScaledContents(true);
- vLayout->addWidget(fpsCheckBox);
- vLayout->addWidget(fpsLabel);
- vLayout->addWidget(textureDetailGroupBox);
- vLayout->addWidget(colorTableCheckBox);
+ QSlider *alphaMultiplierSlider = new QSlider(Qt::Horizontal, widget);
+ alphaMultiplierSlider->setMinimum(0);
+ alphaMultiplierSlider->setMaximum(139);
+ alphaMultiplierSlider->setValue(100);
+ alphaMultiplierSlider->setEnabled(true);
+ QLabel *alphaMultiplierLabel = new QLabel(QStringLiteral("Alpha multiplier: 1.0"));
+
+ QCheckBox *preserveOpacityCheckBox = new QCheckBox(widget);
+ preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity"));
+ preserveOpacityCheckBox->setChecked(true);
+
vLayout->addWidget(sliceXCheckBox);
vLayout->addWidget(sliceXSlider);
vLayout->addWidget(sliceImageXLabel);
@@ -135,6 +144,13 @@ int main(int argc, char **argv)
vLayout->addWidget(sliceZCheckBox);
vLayout->addWidget(sliceZSlider);
vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop);
+ vLayout2->addWidget(fpsCheckBox);
+ vLayout2->addWidget(fpsLabel);
+ vLayout2->addWidget(textureDetailGroupBox);
+ vLayout2->addWidget(colorTableCheckBox);
+ vLayout2->addWidget(alphaMultiplierLabel);
+ vLayout2->addWidget(alphaMultiplierSlider);
+ vLayout2->addWidget(preserveOpacityCheckBox, 1, Qt::AlignTop);
VolumetricModifier *modifier = new VolumetricModifier(graph);
modifier->setFpsLabel(fpsLabel);
@@ -142,6 +158,7 @@ int main(int argc, char **argv)
modifier->setHighDetailRB(highDetailRB);
modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider);
modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel);
+ modifier->setAlphaMultiplierLabel(alphaMultiplierLabel);
QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier,
&VolumetricModifier::setFpsMeasurement);
@@ -157,14 +174,18 @@ int main(int argc, char **argv)
&VolumetricModifier::adjustSliceY);
QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
&VolumetricModifier::adjustSliceZ);
- QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier,
+ QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier,
&VolumetricModifier::toggleLowDetail);
- QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier,
+ QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier,
&VolumetricModifier::toggleMediumDetail);
- QObject::connect(highDetailRB, &QRadioButton::toggled, modifier,
+ QObject::connect(highDetailRB, &QRadioButton::toggled, modifier,
&VolumetricModifier::toggleHighDetail);
- QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier,
+ QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier,
&VolumetricModifier::changeColorTable);
+ QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setPreserveOpacity);
+ QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustAlphaMultiplier);
widget->show();
return app.exec();
diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp
index a553ccf8..56f02dcb 100644
--- a/examples/datavisualization/volumetric/volumetric.cpp
+++ b/examples/datavisualization/volumetric/volumetric.cpp
@@ -80,7 +80,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
// Both tables have a fully transparent colors to fill outer portions of the volume.
// The primary color table.
- // The first visible layer, red, is somewhat transparent. Rest of to colors are opaque.
+ // The top two layers are transparent.
m_colorTable1.resize(colorTableSize);
m_colorTable2.resize(colorTableSize);
@@ -88,9 +88,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
if (i < cutOffColorIndex)
m_colorTable1[i] = qRgba(0, 0, 0, 0);
else if (i < 60)
- m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 20);
+ m_colorTable1[i] = qRgba((i * 2) + 120, 0, 0, 15);
else if (i < 120)
- m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255);
+ m_colorTable1[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 50);
else if (i < 180)
m_colorTable1[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255);
else
@@ -98,14 +98,15 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
}
// The alternate color table.
- // The first visible layer is a thin single color, and rest of the volume uses a smooth gradient.
+ // The first visible layer is a thin opaque color, and rest of the volume uses a smooth
+ // transparent gradient.
for (int i = 1; i < colorTableSize; i++) {
if (i < cutOffColorIndex)
m_colorTable2[i] = qRgba(0, 0, 0, 0);
else if (i < cutOffColorIndex + 4)
m_colorTable2[i] = qRgba(75, 150, 0, 255);
else
- m_colorTable2[i] = qRgba(i, 0, 255 - i, 255);
+ m_colorTable2[i] = qRgba(i, 0, 255 - i, i);
}
m_volumeItem->setColorTable(m_colorTable1);
@@ -167,6 +168,11 @@ void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *
adjustSliceZ(m_sliceSliderZ->value());
}
+void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label)
+{
+ m_alphaMultiplierLabel = label;
+}
+
void VolumetricModifier::sliceX(int enabled)
{
if (m_volumeItem)
@@ -322,6 +328,34 @@ void VolumetricModifier::changeColorTable(int enabled)
adjustSliceZ(m_sliceSliderZ->value());
}
+void VolumetricModifier::setPreserveOpacity(bool enabled)
+{
+ m_volumeItem->setPreserveOpacity(enabled);
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+}
+
+void VolumetricModifier::adjustAlphaMultiplier(int value)
+{
+ float mult;
+ if (value > 100)
+ mult = float(value - 99) / 2.0f;
+ else
+ mult = float(value) / float(500 - value * 4);
+ m_volumeItem->setAlphaMultiplier(mult);
+ QString labelFormat = QStringLiteral("Alpha multiplier: %1");
+ m_alphaMultiplierLabel->setText(labelFormat.arg(
+ QString::number(m_volumeItem->alphaMultiplier(), 'f', 3)));
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+}
+
int VolumetricModifier::createVolume(int textureSize, int startIndex, int count,
QVector<uchar> *textureData)
{
diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h
index eb8a4172..497506ad 100644
--- a/examples/datavisualization/volumetric/volumetric.h
+++ b/examples/datavisualization/volumetric/volumetric.h
@@ -41,6 +41,7 @@ public:
void setMediumDetailRB(QRadioButton *button);
void setHighDetailRB(QRadioButton *button);
void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel);
+ void setAlphaMultiplierLabel(QLabel *label);
public slots:
void sliceX(int enabled);
@@ -58,6 +59,8 @@ public slots:
void setFpsMeasurement(bool enabled);
void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ);
void changeColorTable(int enabled);
+ void setPreserveOpacity(bool enabled);
+ void adjustAlphaMultiplier(int value);
private:
@@ -86,6 +89,7 @@ private:
QLabel *m_sliceLabelX;
QLabel *m_sliceLabelY;
QLabel *m_sliceLabelZ;
+ QLabel *m_alphaMultiplierLabel;
};
#endif
diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp
index 3eb68845..c316fd38 100644
--- a/src/datavisualization/data/customrenderitem.cpp
+++ b/src/datavisualization/data/customrenderitem.cpp
@@ -36,7 +36,13 @@ CustomRenderItem::CustomRenderItem()
m_textureWidth(0),
m_textureHeight(0),
m_textureDepth(0),
- m_isVolume(false)
+ m_isVolume(false),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true)
{
}
diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h
index e21b6f39..5428ce43 100644
--- a/src/datavisualization/data/customrenderitem_p.h
+++ b/src/datavisualization/data/customrenderitem_p.h
@@ -89,9 +89,13 @@ public:
inline void setSliceIndexX(int index) { m_sliceIndexX = index; }
inline void setSliceIndexY(int index) { m_sliceIndexY = index; }
inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; }
- int sliceIndexX() const { return m_sliceIndexX; }
- int sliceIndexY() const { return m_sliceIndexY; }
- int sliceIndexZ() const { return m_sliceIndexZ; }
+ inline int sliceIndexX() const { return m_sliceIndexX; }
+ inline int sliceIndexY() const { return m_sliceIndexY; }
+ inline int sliceIndexZ() const { return m_sliceIndexZ; }
+ inline void setAlphaMultiplier(float mult) { m_alphaMultiplier = mult; }
+ inline float alphaMultiplier() const { return m_alphaMultiplier; }
+ inline void setPreserveOpacity(bool enable) { m_preserveOpacity = enable; }
+ inline bool preserveOpacity() const { return m_preserveOpacity; }
private:
Q_DISABLE_COPY(CustomRenderItem)
@@ -120,6 +124,8 @@ private:
int m_sliceIndexX;
int m_sliceIndexY;
int m_sliceIndexZ;
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
};
typedef QHash<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray;
diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp
index cab79ac0..c1a77dba 100644
--- a/src/datavisualization/data/qcustom3dvolume.cpp
+++ b/src/datavisualization/data/qcustom3dvolume.cpp
@@ -112,6 +112,30 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty real Custom3DVolume::alphaMultiplier
+ *
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0}.
+ *
+ * \sa preserveOpacity
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+
+/*!
* Constructs QCustom3DVolume with given \a parent.
*/
QCustom3DVolume::QCustom3DVolume(QObject *parent) :
@@ -578,84 +602,72 @@ QImage::Format QCustom3DVolume::textureFormat() const
}
/*!
- * Renders the slice specified by \a index along \a axis into an image.
- * The texture format of this object is used.
+ * \property QCustom3DVolume::alphaMultiplier
*
- * \return the rendered image of the slice, or a null image if invalid index is specified.
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0f}.
*
- * \sa textureFormat
+ * \sa preserveOpacity, textureData
*/
-QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index)
+void QCustom3DVolume::setAlphaMultiplier(float mult)
{
- if (index < 0)
- return QImage();
-
- int x;
- int y;
- if (axis == Qt::XAxis) {
- if (index >= textureWidth())
- return QImage();
- x = textureDepth();
- y = textureHeight();
- } else if (axis == Qt::YAxis) {
- if (index >= textureHeight())
- return QImage();
- x = textureWidth();
- y = textureDepth();
+ if (mult >= 0.0f) {
+ if (dptr()->m_alphaMultiplier != mult) {
+ dptr()->m_alphaMultiplier = mult;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit alphaMultiplierChanged(mult);
+ emit dptr()->needUpdate();
+ }
} else {
- if (index >= textureDepth())
- return QImage();
- x = textureWidth();
- y = textureHeight();
+ qWarning() << __FUNCTION__ << "Attempted to set negative multiplier.";
}
+}
- int padding = 0;
- int pixelWidth = 4;
- if (textureFormat() == QImage::Format_Indexed8) {
- padding = x % 4;
- pixelWidth = 1;
- }
- QVector<uchar> data((x + padding) * y * pixelWidth);
- int frameSize = textureDataWidth() * textureHeight();
+float QCustom3DVolume::alphaMultiplier() const
+{
+ return dptrc()->m_alphaMultiplier;
+}
- int dataIndex = 0;
- if (axis == Qt::XAxis) {
- for (int i = 0; i < y; i++) {
- const uchar *p = textureData()->constData()
- + (index * pixelWidth) + (textureDataWidth() * i);
- for (int j = 0; j < x; j++) {
- data[dataIndex++] = *p;
- for (int k = 1; k < pixelWidth; k++)
- data[dataIndex++] = *(p + k);
- p += frameSize;
- }
- }
- } else if (axis == Qt::YAxis) {
- for (int i = 0; i < y; i++) {
- const uchar *p = textureData()->constData() + (index * textureDataWidth())
- + (frameSize * i);
- for (int j = 0; j < (x * pixelWidth); j++) {
- data[dataIndex++] = *p;
- p++;
- }
- }
- } else {
- for (int i = 0; i < y; i++) {
- const uchar *p = textureData()->constData() + (index * frameSize)
- + (textureDataWidth() * i);
- for (int j = 0; j < (x * pixelWidth); j++) {
- data[dataIndex++] = *p;
- p++;
- }
- }
+/*!
+ * \property QCustom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+void QCustom3DVolume::setPreserveOpacity(bool enable)
+{
+ if (dptr()->m_preserveOpacity != enable) {
+ dptr()->m_preserveOpacity = enable;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit preserveOpacityChanged(enable);
+ emit dptr()->needUpdate();
}
+}
- QImage image(data.constData(), x, y, x * pixelWidth, textureFormat());
- image.bits(); // Call bits() to detach the new image from local data
- if (textureFormat() == QImage::Format_Indexed8)
- image.setColorTable(colorTable());
+bool QCustom3DVolume::preserveOpacity() const
+{
+ return dptrc()->m_preserveOpacity;
+}
- return image;
+/*!
+ * Renders the slice specified by \a index along \a axis into an image.
+ * The texture format of this object is used.
+ *
+ * \return the rendered image of the slice, or a null image if invalid index is specified.
+ *
+ * \sa textureFormat
+ */
+QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index)
+{
+ return dptr()->renderSlice(axis, index);
}
/*!
@@ -683,7 +695,9 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) :
m_textureHeight(0),
m_textureDepth(0),
m_textureFormat(QImage::Format_ARGB32),
- m_textureData(0)
+ m_textureData(0),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true)
{
m_isVolumeItem = true;
m_meshFile = QStringLiteral(":/defaultMeshes/barFull");
@@ -705,7 +719,9 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector
m_textureDepth(textureDepth),
m_textureFormat(textureFormat),
m_colorTable(colorTable),
- m_textureData(textureData)
+ m_textureData(textureData),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true)
{
m_isVolumeItem = true;
m_shadowCasting = false;
@@ -738,6 +754,103 @@ void QCustom3DVolumePrivate::resetDirtyBits()
m_dirtyBitsVolume.textureFormatDirty = false;
}
+QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index)
+{
+ if (index < 0)
+ return QImage();
+
+ int x;
+ int y;
+ if (axis == Qt::XAxis) {
+ if (index >= m_textureWidth)
+ return QImage();
+ x = m_textureDepth;
+ y = m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ if (index >= m_textureHeight)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureDepth;
+ } else {
+ if (index >= m_textureDepth)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureHeight;
+ }
+
+ int padding = 0;
+ int pixelWidth = 4;
+ int dataWidth = qptr()->textureDataWidth();
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ padding = x % 4;
+ pixelWidth = 1;
+ }
+ QVector<uchar> data((x + padding) * y * pixelWidth);
+ int frameSize = qptr()->textureDataWidth() * m_textureHeight;
+
+ int dataIndex = 0;
+ if (axis == Qt::XAxis) {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData()
+ + (index * pixelWidth) + (dataWidth * i);
+ for (int j = 0; j < x; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ data[dataIndex++] = *(p + k);
+ p += frameSize;
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData() + (index * dataWidth)
+ + (frameSize * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ } else {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData() + (index * frameSize) + (dataWidth * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ }
+
+ if (m_textureFormat != QImage::Format_Indexed8 && m_alphaMultiplier != 1.0f) {
+ for (int i = pixelWidth - 1; i < data.size(); i += pixelWidth)
+ data[i] = static_cast<uchar>(multipliedAlphaValue(data.at(i)));
+ }
+
+ QImage image(data.constData(), x, y, x * pixelWidth, m_textureFormat);
+ image.bits(); // Call bits() to detach the new image from local data
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ QVector<QRgb> colorTable = m_colorTable;
+ if (m_alphaMultiplier != 1.0f) {
+ for (int i = 0; i < colorTable.size(); i++) {
+ QRgb curCol = colorTable.at(i);
+ int alpha = multipliedAlphaValue(qAlpha(curCol));
+ if (alpha != qAlpha(curCol))
+ colorTable[i] = qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), alpha);
+ }
+ }
+ image.setColorTable(colorTable);
+ }
+
+ return image;
+}
+
+int QCustom3DVolumePrivate::multipliedAlphaValue(int alpha)
+{
+ int modifiedAlpha = alpha;
+ if (!m_preserveOpacity || alpha != 255) {
+ modifiedAlpha = int(m_alphaMultiplier * float(alpha));
+ modifiedAlpha = qMin(modifiedAlpha, 255);
+ }
+ return modifiedAlpha;
+}
+
QCustom3DVolume *QCustom3DVolumePrivate::qptr()
{
return static_cast<QCustom3DVolume *>(q_ptr);
diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h
index 00733d17..2f95fa5d 100644
--- a/src/datavisualization/data/qcustom3dvolume.h
+++ b/src/datavisualization/data/qcustom3dvolume.h
@@ -40,7 +40,8 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem
Q_PROPERTY(QVector<QRgb> colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged)
Q_PROPERTY(QVector<uchar> *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged)
Q_PROPERTY(QImage::Format textureFormat READ textureFormat WRITE setTextureFormat NOTIFY textureFormatChanged)
-
+ Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged)
+ Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged)
public:
explicit QCustom3DVolume(QObject *parent = 0);
@@ -80,6 +81,11 @@ public:
void setTextureFormat(QImage::Format format);
QImage::Format textureFormat() const;
+ void setAlphaMultiplier(float mult);
+ float alphaMultiplier() const;
+ void setPreserveOpacity(bool enable);
+ bool preserveOpacity() const;
+
QImage renderSlice(Qt::Axis axis, int index);
signals:
@@ -92,6 +98,8 @@ signals:
void colorTableChanged();
void textureDataChanged(QVector<uchar> *data);
void textureFormatChanged(QImage::Format format);
+ void alphaMultiplierChanged(float mult);
+ void preserveOpacityChanged(bool enabled);
protected:
QCustom3DVolumePrivate *dptr();
diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h
index 69dd1eb2..b83e27fb 100644
--- a/src/datavisualization/data/qcustom3dvolume_p.h
+++ b/src/datavisualization/data/qcustom3dvolume_p.h
@@ -40,13 +40,15 @@ struct QCustomVolumeDirtyBitField {
bool colorTableDirty : 1;
bool textureDataDirty : 1;
bool textureFormatDirty : 1;
+ bool alphaDirty : 1;
QCustomVolumeDirtyBitField()
: textureDimensionsDirty(false),
sliceIndicesDirty(false),
colorTableDirty(false),
textureDataDirty(false),
- textureFormatDirty(false)
+ textureFormatDirty(false),
+ alphaDirty(false)
{
}
};
@@ -64,6 +66,7 @@ public:
virtual ~QCustom3DVolumePrivate();
void resetDirtyBits();
+ QImage renderSlice(Qt::Axis axis, int index);
QCustom3DVolume *qptr();
@@ -79,9 +82,14 @@ public:
QVector<QRgb> m_colorTable;
QVector<uchar> *m_textureData;
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
+
QCustomVolumeDirtyBitField m_dirtyBitsVolume;
private:
+ int multipliedAlphaValue(int alpha);
+
friend class QCustom3DVolume;
};
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 8eb1d2ce..37688beb 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -967,6 +967,8 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
newItem->setSliceIndexX(volumeItem->sliceIndexX());
newItem->setSliceIndexY(volumeItem->sliceIndexY());
newItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ newItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ newItem->setPreserveOpacity(volumeItem->preserveOpacity());
#endif
}
newItem->setScaling(scaling);
@@ -1114,6 +1116,11 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setSliceIndexZ(volumeItem->sliceIndexZ());
volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false;
}
+ if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) {
+ renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ renderItem->setPreserveOpacity(volumeItem->preserveOpacity());
+ volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false;
+ }
#endif
}
}
@@ -1292,6 +1299,9 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
item->colorTable().constData(), 256);
}
shader->setUniformValue(shader->color8Bit(), color8Bit);
+ shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier());
+ shader->setUniformValue(shader->preserveOpacity(),
+ item->preserveOpacity() ? 1 : 0);
if (shader == volumeSliceShader) {
QVector3D slices((float(item->sliceIndexX()) + 0.5f)
/ float(item->textureWidth()) * 2.0 - 1.0,
diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag
index 90876596..1192ae85 100644
--- a/src/datavisualization/engine/shaders/texture3d.frag
+++ b/src/datavisualization/engine/shaders/texture3d.frag
@@ -8,6 +8,8 @@ uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
uniform highp vec3 textureDimensions;
uniform highp int sampleCount; // This is the maximum sample count
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
const highp float alphaThreshold = 0.0001;
@@ -69,7 +71,7 @@ void main() {
// Adjust alpha multiplier according to the step size to get uniform alpha effect
// regardless of the ray angle.
- highp float alphaMultiplier = stepSize / (1.0 / sampleCount);
+ highp float totalAlphaMultiplier = (stepSize / (1.0 / sampleCount)) * alphaMultiplier;
highp vec4 curColor = vec4(0, 0, 0, 0);
highp vec3 curRgb = vec3(0, 0, 0);
@@ -81,10 +83,12 @@ void main() {
if (color8Bit != 0)
curColor = colorIndex[int(curColor.r * 255.0)];
- if (curColor.a == 1.0)
+ // Unless we have explicit alpha multiplier, we want to preserve opacity anyway
+ if (curColor.a == 1.0 && (preserveOpacity != 0 || alphaMultiplier == 1.0))
curAlpha = 1.0;
else
- curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curAlpha = clamp(curColor.a * totalAlphaMultiplier, 0.0, 1.0);
+
if (curAlpha > alphaThreshold) {
curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
destColor.rgb += curRgb;
@@ -96,6 +100,10 @@ void main() {
}
}
+ // Brighten up the final color if there is some transparency left
+ if (totalAlpha > alphaThreshold && totalAlpha < 1.0)
+ destColor *= 1.0 / totalAlpha;
+
destColor.a = totalAlpha;
gl_FragColor = clamp(destColor, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag
index 8870b26d..3d4c9030 100644
--- a/src/datavisualization/engine/shaders/texture3dslice.frag
+++ b/src/datavisualization/engine/shaders/texture3dslice.frag
@@ -7,6 +7,8 @@ uniform highp vec3 cameraPositionRelativeToModel;
uniform highp vec3 volumeSliceIndices;
uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
const highp vec3 xPlaneNormal = vec3(1.0, 0, 0);
const highp vec3 yPlaneNormal = vec3(0, 1.0, 0);
@@ -76,47 +78,67 @@ void main() {
}
highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0);
+ highp vec4 curColor = vec4(0.0, 0.0, 0.0, 0.0);
highp float totalAlpha = 0.0;
highp vec3 curRgb = vec3(0, 0, 0);
+ highp float curAlpha = 0.0;
// Convert intersection to texture coords
if (firstD <= tFar) {
highp vec3 firstTex = rayStart + rayDir * firstD;
firstTex = 0.5 * (firstTex + 1.0);
- highp vec4 firstColor = texture3D(textureSampler, firstTex);
+ curColor = texture3D(textureSampler, firstTex);
if (color8Bit != 0)
- firstColor = colorIndex[int(firstColor.r * 255.0)];
+ curColor = colorIndex[int(curColor.r * 255.0)];
- if (firstColor.a > alphaThreshold) {
- destColor.rgb = firstColor.rgb * firstColor.a;
- totalAlpha = firstColor.a;
+ if (curColor.a > alphaThreshold) {
+ curAlpha = curColor.a;
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ destColor.rgb = curColor.rgb * curAlpha;
+ totalAlpha = curAlpha;
}
if (secondD <= tFar && totalAlpha < 1.0) {
highp vec3 secondTex = rayStart + rayDir * secondD;
secondTex = 0.5 * (secondTex + 1.0);
- highp vec4 secondColor = texture3D(textureSampler, secondTex);
+ curColor = texture3D(textureSampler, secondTex);
if (color8Bit != 0)
- secondColor = colorIndex[int(secondColor.r * 255.0)];
- if (secondColor.a > alphaThreshold) {
- curRgb = secondColor.rgb * secondColor.a * (1.0 - totalAlpha);
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a > alphaThreshold) {
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
destColor.rgb += curRgb;
- totalAlpha += secondColor.a;
+ totalAlpha += curAlpha;
}
if (thirdD <= tFar && totalAlpha < 1.0) {
highp vec3 thirdTex = rayStart + rayDir * thirdD;
thirdTex = 0.5 * (thirdTex + 1.0);
- highp vec4 thirdColor = texture3D(textureSampler, thirdTex);
- if (color8Bit != 0)
- thirdColor = colorIndex[int(thirdColor.r * 255.0)];
- if (thirdColor.a > alphaThreshold) {
- curRgb = thirdColor.rgb * thirdColor.a * (1.0 - totalAlpha);
+ curColor = texture3D(textureSampler, thirdTex);
+ if (curColor.a > alphaThreshold) {
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
destColor.rgb += curRgb;
- totalAlpha += thirdColor.a;
+ totalAlpha += curAlpha;
}
}
}
}
+
+ // Brighten up the final color if there is some transparency left
+ if (totalAlpha > alphaThreshold && totalAlpha < 1.0)
+ destColor *= 1.0 / totalAlpha;
+
destColor.a = totalAlpha;
gl_FragColor = clamp(destColor, 0.0, 1.0);
}
diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp
index 3361638a..9d1ad0d9 100644
--- a/src/datavisualization/utils/shaderhelper.cpp
+++ b/src/datavisualization/utils/shaderhelper.cpp
@@ -99,6 +99,8 @@ void ShaderHelper::initialize()
m_color8BitUniform = m_program->uniformLocation("color8Bit");
m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions");
m_sampleCountUniform = m_program->uniformLocation("sampleCount");
+ m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier");
+ m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity");
m_initialized = true;
}
@@ -308,6 +310,20 @@ GLuint ShaderHelper::sampleCount()
return m_sampleCountUniform;
}
+GLuint ShaderHelper::alphaMultiplier()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_alphaMultiplierUniform;
+}
+
+GLuint ShaderHelper::preserveOpacity()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_preserveOpacityUniform;
+}
+
GLuint ShaderHelper::posAtt()
{
if (!m_initialized)
diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h
index bc7609bb..ac815447 100644
--- a/src/datavisualization/utils/shaderhelper_p.h
+++ b/src/datavisualization/utils/shaderhelper_p.h
@@ -80,6 +80,8 @@ class ShaderHelper
GLuint color8Bit();
GLuint textureDimensions();
GLuint sampleCount();
+ GLuint alphaMultiplier();
+ GLuint preserveOpacity();
GLuint posAtt();
GLuint uvAtt();
@@ -120,6 +122,8 @@ class ShaderHelper
GLuint m_color8BitUniform;
GLuint m_textureDimensionsUniform;
GLuint m_sampleCountUniform;
+ GLuint m_alphaMultiplierUniform;
+ GLuint m_preserveOpacityUniform;
GLboolean m_initialized;
};
diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp
index 3485dd24..554373cb 100644
--- a/tests/volumetrictest/volumetrictest.cpp
+++ b/tests/volumetrictest/volumetrictest.cpp
@@ -317,7 +317,7 @@ void VolumetricModifier::createAnotherVolume()
QImage logo;
logo.load(QStringLiteral(":/logo.png"));
- //logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied);
+ logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied);
qDebug() << "second image dimensions:" << logo.width() << logo.height()
<< logo.byteCount() << (logo.width() * logo.height())
<< logo.bytesPerLine();
@@ -347,6 +347,8 @@ void VolumetricModifier::createAnotherVolume()
// Change one picture using subtexture replacement
QImage flipped = logo.mirrored();
m_volumeItem2->setSubTextureData(100, flipped);
+ m_volumeItem2->setAlphaMultiplier(0.2f);
+ m_volumeItem2->setPreserveOpacity(false);
}
void VolumetricModifier::createYetAnotherVolume()