diff options
Diffstat (limited to 'src/datavisualization/engine')
12 files changed, 336 insertions, 147 deletions
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 18384872..305d3df0 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -960,7 +960,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setRenderer(this); newItem->setItemPointer(item); // Store pointer for render item updates newItem->setMesh(item->meshFile()); - newItem->setPosition(item->position()); + newItem->setOrigPosition(item->position()); newItem->setOrigScaling(item->scaling()); newItem->setScalingAbsolute(item->isScalingAbsolute()); newItem->setPositionAbsolute(item->isPositionAbsolute()); @@ -1013,7 +1013,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setUseHighDefShader(volumeItem->useHighDefShader()); #endif } - recalculateCustomItemScaling(newItem); + recalculateCustomItemScalingAndPos(newItem); newItem->setRotation(item->rotation()); #if !defined(QT_OPENGL_ES_2) // In OpenGL ES we simply draw volumes as regular custom item placeholders. @@ -1025,9 +1025,6 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) } newItem->setTexture(texture); item->d_ptr->clearTextureImage(); - QVector3D translation = convertPositionToTranslation(newItem->position(), - newItem->isPositionAbsolute()); - newItem->setTranslation(translation); newItem->setVisible(item->isVisible()); newItem->setShadowCasting(item->isShadowCasting()); newItem->setFacingCamera(facingCamera); @@ -1035,25 +1032,71 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) return newItem; } -void Abstract3DRenderer::recalculateCustomItemScaling(CustomRenderItem *item) +void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item) { if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute() && !item->isPositionAbsolute()) { QVector3D scale = item->origScaling() / 2.0f; - QVector3D pos = item->position(); + QVector3D pos = item->origPosition(); QVector3D minBounds(pos.x() - scale.x(), pos.y() - scale.y(), - pos.z() - scale.z()); + pos.z() + scale.z()); QVector3D maxBounds(pos.x() + scale.x(), pos.y() + scale.y(), - pos.z() + scale.z()); - QVector3D min = convertPositionToTranslation(minBounds, false); - QVector3D max = convertPositionToTranslation(maxBounds, false); - item->setScaling(QVector3D(qAbs(max.x() - min.x()), qAbs(max.y() - min.y()), - qAbs(max.z() - min.z())) / 2.0f); + pos.z() - scale.z()); + QVector3D minCorner = convertPositionToTranslation(minBounds, false); + QVector3D maxCorner = convertPositionToTranslation(maxBounds, false); + scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()), + qAbs(maxCorner.y() - minCorner.y()), + qAbs(maxCorner.z() - minCorner.z())) / 2.0f; + if (item->isVolume()) { + // Only volume items need to scale and reposition according to bounds + QVector3D minBoundsNormal = minCorner; + QVector3D maxBoundsNormal = maxCorner; + // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1] + // Y and Z are also flipped. + getVisibleItemBounds(minBoundsNormal, maxBoundsNormal); + item->setMinBounds(minBoundsNormal); + item->setMaxBounds(maxBoundsNormal); + // For scaling calculations, we want [0,1] normalized values + minBoundsNormal = item->minBoundsNormal(); + maxBoundsNormal = item->maxBoundsNormal(); + + // Rescale and reposition the item so that it doesn't go over the edges + QVector3D adjScaling = + QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()), + scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()), + scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z())); + + item->setScaling(adjScaling); + + QVector3D adjPos = item->origPosition(); + QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(), + maxBounds.y() - minBounds.y(), + maxBounds.z() - minBounds.z()) / 2.0f; + adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x()) + - (dataExtents.x() * (1.0f - maxBoundsNormal.x()))); + adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y()) + - (dataExtents.y() * (1.0f - maxBoundsNormal.y()))); + adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z()) + - (dataExtents.z() * (1.0f - maxBoundsNormal.z()))); + item->setPosition(adjPos); + } else { + // Only scale for non-volume items, and do not readjust position + item->setScaling(scale); + item->setPosition(item->origPosition()); + } } else { item->setScaling(item->origScaling()); + item->setPosition(item->origPosition()); + if (item->isVolume()) { + item->setMinBounds(-1.0f * zeroVector); + item->setMaxBounds(oneVector); + } } + QVector3D translation = convertPositionToTranslation(item->position(), + item->isPositionAbsolute()); + item->setTranslation(translation); } void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) @@ -1064,11 +1107,10 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) item->d_ptr->m_dirtyBits.meshDirty = false; } if (item->d_ptr->m_dirtyBits.positionDirty) { - renderItem->setPosition(item->position()); + renderItem->setOrigPosition(item->position()); renderItem->setPositionAbsolute(item->isPositionAbsolute()); - QVector3D translation = convertPositionToTranslation(renderItem->position(), - renderItem->isPositionAbsolute()); - renderItem->setTranslation(translation); + if (!item->d_ptr->m_dirtyBits.scalingDirty) + recalculateCustomItemScalingAndPos(renderItem); item->d_ptr->m_dirtyBits.positionDirty = false; } if (item->d_ptr->m_dirtyBits.scalingDirty) { @@ -1099,7 +1141,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) item->d_ptr->clearTextureImage(); renderItem->setOrigScaling(scaling); } - recalculateCustomItemScaling(renderItem); + recalculateCustomItemScalingAndPos(renderItem); item->d_ptr->m_dirtyBits.scalingDirty = false; } if (item->d_ptr->m_dirtyBits.rotationDirty) { @@ -1195,10 +1237,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) void Abstract3DRenderer::updateCustomItemPositions() { foreach (CustomRenderItem *renderItem, m_customRenderCache) { - recalculateCustomItemScaling(renderItem); - QVector3D translation = convertPositionToTranslation(renderItem->position(), - renderItem->isPositionAbsolute()); - renderItem->setTranslation(translation); + recalculateCustomItemScalingAndPos(renderItem); } } @@ -1243,7 +1282,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, continue; } - // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is + // If the render item is in data coordinates and not within axis ranges, skip it if (!item->isPositionAbsolute() && (item->position().x() < m_axisCacheX.min() || item->position().x() > m_axisCacheX.max() @@ -1353,9 +1392,12 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, // Set shadowless shader bindings #if !defined(QT_OPENGL_ES_2) if (item->isVolume()) { - // Volume shaders repurpose light position for camera position relative to item QVector3D cameraPos = m_cachedScene->activeCamera()->position(); cameraPos = MVPMatrix.inverted().map(cameraPos); + // Adjust camera position according to min/max bounds + cameraPos = cameraPos + + ((oneVector - cameraPos) * item->minBoundsNormal()) + - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())); shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; if (color8Bit) { @@ -1366,6 +1408,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); shader->setUniformValue(shader->preserveOpacity(), item->preserveOpacity() ? 1 : 0); + + shader->setUniformValue(shader->minBounds(), item->minBounds()); + shader->setUniformValue(shader->maxBounds(), item->maxBounds()); + if (shader == m_volumeTextureSliceShader) { QVector3D slices((float(item->sliceIndexX()) + 0.5f) / float(item->textureWidth()) * 2.0 - 1.0, diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 8152e0c9..c8bfa7af 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -205,7 +205,8 @@ protected: virtual void fixCameraTarget(QVector3D &target) = 0; void updateCameraViewport(); - void recalculateCustomItemScaling(CustomRenderItem *item); + void recalculateCustomItemScalingAndPos(CustomRenderItem *item); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 1614b563..3e83a830 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -138,6 +138,46 @@ void Bars3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_zScaleFactor); } +void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_xScaleFactor) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -1.0f + m_backgroundAdjustment) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_zScaleFactor) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_xScaleFactor) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > 1.0f + m_backgroundAdjustment) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_zScaleFactor) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Bars3DRenderer::updateData() { int minRow = m_axisCacheZ.min(); @@ -1042,7 +1082,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } if (m_reflectionEnabled && ((m_yFlipped && item.height() > 0.0) - || (!m_yFlipped && item.height() < 0.0))) { + || (!m_yFlipped && item.height() < 0.0))) { continue; } @@ -1564,7 +1604,7 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, && (reflection == 1.0f || (reflection != 1.0f && ((m_yFlipped && item.height() < 0.0) - || (!m_yFlipped && item.height() > 0.0))))) + || (!m_yFlipped && item.height() > 0.0))))) || !m_reflectionEnabled) { // Skip drawing of 0-height bars and reflections of bars on the "wrong side" // Set shader bindings @@ -2775,10 +2815,10 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position float zTrans = 0.0f; if (!isAbsolute) { // Convert row and column to translation on graph - xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth) - / m_scaleFactor; - zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height())) - / m_scaleFactor; + xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width()) + - m_rowWidth) / m_scaleFactor; + zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f) + * m_cachedBarSpacing.height())) / m_scaleFactor; yTrans = m_axisCacheY.positionAt(position.y()); } else { xTrans = position.x() * m_xScaleFactor; diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 44837ba2..7f1e83bb 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -120,6 +120,7 @@ public: protected: virtual void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); public slots: void updateMultiSeriesScaling(bool uniform); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index ba1019ad..34386ca6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -141,6 +141,46 @@ void Scatter3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_scaleZ); } +void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_scaleX) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -m_scaleY) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_scaleZ) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_scaleX) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > m_scaleY) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_scaleZ) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Scatter3DRenderer::updateData() { calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 3fc517d0..0852f262 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -114,6 +114,7 @@ public slots: protected: virtual void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag index 460cbcc6..3f9c42ff 100644 --- a/src/datavisualization/engine/shaders/texture3d.frag +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -1,15 +1,17 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -uniform highp vec3 cameraPositionRelativeToModel; 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; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; // Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. // Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over @@ -17,51 +19,38 @@ uniform highp int preserveOpacity; const highp float alphaThicknesses = 32.0; void main() { - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayDir.yz = -rayDir.yz; - rayStart.yz = -rayStart.yz; - // Calculate ray intersection endpoint - vec3 rayStop; - if (rayDir.x == 0.0) { - rayStop.yz = rayStart.yz; - rayStop.x = -rayStart.x; - } else if (rayDir.y == 0.0) { - rayStop.xz = rayStart.xz; - rayStop.y = -rayStart.y; - } else if (rayDir.z == 0.0) { - rayStop.xy = rayStart.xy; - rayStop.z = -rayStart.z; - } else { - highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) - boxBounds.x = -1.0; - if (rayDir.y < 0) - boxBounds.y = -1.0; - if (rayDir.z < 0) - boxBounds.z = -1.0; - highp vec3 t = (boxBounds - rayStart) * invRayDir; - highp float minT = min(t.x, min(t.y, t.z)); - rayStop = rayStart + minT * rayDir; + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; } + // Calculate ray intersection endpoint + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + rayStop = rayStart + endT * rayDir; + if (endT <= 0.0) + discard; + // Convert intersections to texture coords rayStart = 0.5 * (rayStart + 1.0); rayStop = 0.5 * (rayStop + 1.0); highp vec3 ray = rayStop - rayStart; - // Avoid artifacts from divisions by zero - if (ray.x == 0) - ray.x = 0.000000001; - if (ray.y == 0) - ray.y = 0.000000001; - if (ray.z == 0) - ray.z = 0.000000001; - highp vec3 absRay = abs(ray); highp vec3 invAbsRay = 1.0 / absRay; highp float fullDist = length(ray); @@ -154,6 +143,9 @@ void main() { break; } + if (totalOpacity == 1.0) + discard; + // Brighten up the final color if there is some transparency left if (totalOpacity >= 0.0 && totalOpacity < 1.0) destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert index cad1ce06..ef3f1b25 100644 --- a/src/datavisualization/engine/shaders/texture3d.vert +++ b/src/datavisualization/engine/shaders/texture3d.vert @@ -1,12 +1,36 @@ uniform highp mat4 MVP; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; +uniform highp vec3 cameraPositionRelativeToModel; attribute highp vec3 vertexPosition_mdl; attribute highp vec2 vertexUV; attribute highp vec3 vertexNormal_mdl; varying highp vec3 pos; +varying highp vec3 rayDir; void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - pos = vertexPosition_mdl; + + highp vec3 minBoundsNorm = minBounds; + highp vec3 maxBoundsNorm = maxBounds; + + // Y and Z are flipped in bounds to be directly usable in texture calculations, + // so flip them back to normal for position calculations + minBoundsNorm.yz = -minBoundsNorm.yz; + maxBoundsNorm.yz = -maxBoundsNorm.yz; + + minBoundsNorm = 0.5 * (minBoundsNorm + 1.0); + maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0); + + pos = vertexPosition_mdl + + ((1.0 - vertexPosition_mdl) * minBoundsNorm) + - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm)); + + rayDir = -(cameraPositionRelativeToModel - pos); + + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayDir.yz = -rayDir.yz; + pos.yz = -pos.yz; } diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag index 72b959fc..ed0d41ce 100644 --- a/src/datavisualization/engine/shaders/texture3dlowdef.frag +++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag @@ -1,15 +1,17 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -uniform highp vec3 cameraPositionRelativeToModel; 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; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; // Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. // Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over @@ -18,36 +20,30 @@ const highp float alphaThicknesses = 32.0; const highp float SQRT3 = 1.73205081; void main() { - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayDir.yz = -rayDir.yz; - rayStart.yz = -rayStart.yz; + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; + } // Calculate ray intersection endpoint - vec3 rayStop; - if (rayDir.x == 0.0) { - rayStop.yz = rayStart.yz; - rayStop.x = -rayStart.x; - } else if (rayDir.y == 0.0) { - rayStop.xz = rayStart.xz; - rayStop.y = -rayStart.y; - } else if (rayDir.z == 0.0) { - rayStop.xy = rayStart.xy; - rayStop.z = -rayStart.z; - } else { - highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) - boxBounds.x = -1.0; - if (rayDir.y < 0) - boxBounds.y = -1.0; - if (rayDir.z < 0) - boxBounds.z = -1.0; - highp vec3 t = (boxBounds - rayStart) * invRayDir; - highp float minT = min(t.x, min(t.y, t.z)); - rayStop = rayStart + minT * rayDir; - } + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + if (endT <= 0.0) + discard; + rayStop = rayStart + endT * rayDir; // Convert intersections to texture coords rayStart = 0.5 * (rayStart + 1.0); @@ -55,15 +51,6 @@ void main() { highp vec3 ray = rayStop - rayStart; - // Avoid artifacts from divisions by zero - if (ray.x == 0) - ray.x = 0.000000001; - if (ray.y == 0) - ray.y = 0.000000001; - if (ray.z == 0) - ray.z = 0.000000001; - - highp float fullDist = length(ray); highp float stepSize = SQRT3 / sampleCount; highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount; @@ -110,6 +97,9 @@ void main() { break; } + if (totalOpacity == 1.0) + discard; + // Brighten up the final color if there is some transparency left if (totalOpacity >= 0.0 && totalOpacity < 1.0) destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 00584744..c555af98 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -1,14 +1,16 @@ #version 120 varying highp vec3 pos; +varying highp vec3 rayDir; uniform highp sampler3D textureSampler; -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; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); @@ -16,21 +18,17 @@ const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); void main() { // Find out where ray intersects the slice planes - highp vec3 rayDir = -(cameraPositionRelativeToModel - pos); - rayDir = normalize(rayDir); + vec3 normRayDir = normalize(rayDir); highp vec3 rayStart = pos; - // Flip Y and Z so QImage bits work directly for texture and first image is in the front - rayStart.yz = -rayStart.yz; - rayDir.yz = -rayDir.yz; highp float minT = 2.0f; - if (rayDir.x != 0.0 && rayDir.y != 0.0 && rayDir.z != 0.0) { + if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) { highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); - highp vec3 invRayDir = 1.0 / rayDir; - if (rayDir.x < 0) + highp vec3 invRayDir = 1.0 / normRayDir; + if (normRayDir.x < 0) boxBounds.x = -1.0; - if (rayDir.y < 0) + if (normRayDir.y < 0) boxBounds.y = -1.0; - if (rayDir.z < 0) + if (normRayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; minT = min(t.x, min(t.y, t.z)); @@ -43,12 +41,12 @@ void main() { highp float secondD = firstD; highp float thirdD = firstD; if (volumeSliceIndices.x >= -1.0) { - highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal); + highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal); if (dx >= 0.0 && dx <= minT) firstD = min(dx, firstD); } if (volumeSliceIndices.y >= -1.0) { - highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal); + highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal); if (dy >= 0.0 && dy <= minT) { if (dy < firstD) { secondD = firstD; @@ -59,7 +57,7 @@ void main() { } } if (volumeSliceIndices.z >= -1.0) { - highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal); + highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal); if (dz >= 0.0) { if (dz < firstD && dz <= minT) { thirdD = secondD; @@ -83,43 +81,35 @@ void main() { // Convert intersection to texture coords if (firstD <= minT) { - highp vec3 firstTex = rayStart + rayDir * firstD; - firstTex = 0.5 * (firstTex + 1.0); - curColor = texture3D(textureSampler, firstTex); - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; - - if (curColor.a > 0.0) { - 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 <= minT && totalAlpha < 1.0) { - highp vec3 secondTex = rayStart + rayDir * secondD; - secondTex = 0.5 * (secondTex + 1.0); - curColor = texture3D(textureSampler, secondTex); + highp vec3 texelVec = rayStart + normRayDir * firstD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); if (color8Bit != 0) curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a > 0.0) { + curAlpha = curColor.a; 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 += curAlpha; + destColor.rgb = curColor.rgb * curAlpha; + totalAlpha = curAlpha; } - if (thirdD <= minT && totalAlpha < 1.0) { - highp vec3 thirdTex = rayStart + rayDir * thirdD; - thirdTex = 0.5 * (thirdTex + 1.0); - curColor = texture3D(textureSampler, thirdTex); + } + if (secondD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * secondD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; if (curColor.a > 0.0) { - if (color8Bit != 0) - curColor = colorIndex[int(curColor.r * 255.0)]; if (curColor.a == 1.0 && preserveOpacity != 0) curAlpha = 1.0; else @@ -129,9 +119,32 @@ void main() { totalAlpha += curAlpha; } } + if (thirdD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * thirdD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (curColor.a > 0.0) { + 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 += curAlpha; + } + } + } } } + if (totalAlpha == 0.0) + discard; + // Brighten up the final color if there is some transparency left if (totalAlpha > 0.0 && totalAlpha < 1.0) destColor *= 1.0 / totalAlpha; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index ced4c789..fb7322cc 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -158,6 +158,46 @@ void Surface3DRenderer::fixCameraTarget(QVector3D &target) target.setZ(target.z() * -m_scaleZ); } +void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) +{ + // The inputs are the item bounds in OpenGL coordinates. + // The outputs limit these bounds to visible ranges, normalized to range [-1, 1] + // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those + float itemRangeX = (maxBounds.x() - minBounds.x()); + float itemRangeY = (maxBounds.y() - minBounds.y()); + float itemRangeZ = (maxBounds.z() - minBounds.z()); + + if (minBounds.x() < -m_scaleX) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -m_scaleY) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_scaleZ) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_scaleX) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > m_scaleY) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_scaleZ) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Surface3DRenderer::updateData() { calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 9c53757d..623951d4 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -111,6 +111,7 @@ public: protected: void initializeOpenGL(); virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); signals: void flatShadingSupportedChanged(bool supported); |