playing with shadows

This commit is contained in:
Uwe Rathmann 2021-09-18 14:48:25 +02:00
parent a2475abbe6
commit 3e263598c8
9 changed files with 145 additions and 149 deletions

View File

@ -5,6 +5,7 @@ SUBDIRS += \
dialogbuttons \ dialogbuttons \
invoker \ invoker \
inputpanel \ inputpanel \
shadows \
images images
qtHaveModule(webengine) { qtHaveModule(webengine) {

View File

@ -27,13 +27,12 @@ namespace
QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override; QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override;
private: private:
int m_matrixLocation = -1; int m_matrixId = -1;
int m_opacityLocation = -1; int m_opacityId = -1;
int m_aspectLocation = -1; int m_aspectId = -1;
int m_extentLocation = -1; int m_blurExtentId = -1;
int m_radiusLocation = -1; int m_radiusId = -1;
int m_colorLocation = -1; int m_colorId = -1;
int m_offsetLocation = -1;
}; };
class Material final : public QSGMaterial class Material final : public QSGMaterial
@ -48,10 +47,9 @@ namespace
int compare( const QSGMaterial* other ) const override; int compare( const QSGMaterial* other ) const override;
QVector2D aspect = QVector2D{1.0, 1.0}; QVector2D aspect = QVector2D{1.0, 1.0};
float extent = 0.0; float blurExtent = 0.0;
QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0}; QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0};
QColor color = Qt::black; QColor color = Qt::black;
QVector2D offset;
}; };
Shader::Shader() Shader::Shader()
@ -74,13 +72,12 @@ namespace
auto p = program(); auto p = program();
m_matrixLocation = p->uniformLocation( "matrix" ); m_matrixId = p->uniformLocation( "matrix" );
m_aspectLocation = p->uniformLocation( "aspect" ); m_aspectId = p->uniformLocation( "aspect" );
m_opacityLocation = p->uniformLocation( "opacity" ); m_opacityId = p->uniformLocation( "opacity" );
m_extentLocation = p->uniformLocation( "extent" ); m_blurExtentId = p->uniformLocation( "blurExtent" );
m_offsetLocation = p->uniformLocation( "offset" ); m_radiusId = p->uniformLocation( "radius" );
m_radiusLocation = p->uniformLocation( "radius" ); m_colorId = p->uniformLocation( "color" );
m_colorLocation = p->uniformLocation( "color" );
} }
void Shader::updateState( const QSGMaterialShader::RenderState& state, void Shader::updateState( const QSGMaterialShader::RenderState& state,
@ -89,20 +86,20 @@ namespace
auto p = program(); auto p = program();
if ( state.isMatrixDirty() ) if ( state.isMatrixDirty() )
p->setUniformValue(m_matrixLocation, state.combinedMatrix() ); p->setUniformValue( m_matrixId, state.combinedMatrix() );
if ( state.isOpacityDirty() ) if ( state.isOpacityDirty() )
p->setUniformValue(m_opacityLocation, state.opacity() ); p->setUniformValue( m_opacityId, state.opacity() );
if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0 ) if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0
|| state.isCachedMaterialDataDirty( ))
{ {
auto material = static_cast< const Material* >( newMaterial ); auto material = static_cast< const Material* >( newMaterial );
p->setUniformValue( m_aspectLocation, material->aspect ); p->setUniformValue( m_aspectId, material->aspect );
p->setUniformValue( m_extentLocation, material->extent ); p->setUniformValue( m_blurExtentId, material->blurExtent);
p->setUniformValue( m_radiusLocation, material->radius ); p->setUniformValue( m_radiusId, material->radius );
p->setUniformValue( m_colorLocation, material->color ); p->setUniformValue( m_colorId, material->color );
p->setUniformValue( m_offsetLocation, material->offset );
} }
} }
@ -127,9 +124,8 @@ namespace
auto material = static_cast< const Material* >( other ); auto material = static_cast< const Material* >( other );
if ( material->color == color if ( material->color == color
&& material->offset == offset
&& material->aspect == aspect && material->aspect == aspect
&& qFuzzyCompare(material->extent, extent) && qFuzzyCompare(material->blurExtent, blurExtent)
&& qFuzzyCompare(material->radius, radius) ) && qFuzzyCompare(material->radius, radius) )
{ {
return 0; return 0;
@ -175,16 +171,16 @@ void BoxShadowNode::setRect( const QRectF& rect )
d->rect = rect; d->rect = rect;
QVector2D newAspect( 1.0, 1.0 ); QVector2D aspect( 1.0, 1.0 );
if ( rect.width() >= rect.height() ) if ( rect.width() >= rect.height() )
newAspect.setX( rect.width() / rect.height() ); aspect.setX( rect.width() / rect.height() );
else else
newAspect.setY( rect.height() / rect.width() ); aspect.setY( rect.height() / rect.width() );
if ( d->material.aspect != newAspect) if ( d->material.aspect != aspect )
{ {
d->material.aspect = newAspect; d->material.aspect = aspect;
markDirty( QSGNode::DirtyMaterial ); markDirty( QSGNode::DirtyMaterial );
} }
} }
@ -193,7 +189,7 @@ void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape )
{ {
Q_D( BoxShadowNode ); Q_D( BoxShadowNode );
const float t = 0.5 * std::min( d->rect.width(), d->rect.height() ); const float t = std::min( d->rect.width(), d->rect.height() );
const float r1 = shape.radius( Qt::BottomRightCorner ).width(); const float r1 = shape.radius( Qt::BottomRightCorner ).width();
const float r2 = shape.radius( Qt::TopRightCorner ).width(); const float r2 = shape.radius( Qt::TopRightCorner ).width();
@ -224,55 +220,38 @@ void BoxShadowNode::setColor( const QColor& color )
if ( d->material.color != c ) if ( d->material.color != c )
{ {
d->material.color = c; d->material.color = c;
markDirty(QSGNode::DirtyMaterial); markDirty( QSGNode::DirtyMaterial );
} }
} }
void BoxShadowNode::setShadow( qreal extent, qreal dx, qreal dy ) void BoxShadowNode::setBlurRadius( qreal blurRadius )
{ {
Q_D( BoxShadowNode ); Q_D( BoxShadowNode );
if ( extent <= 0.0 ) if ( blurRadius <= 0.0 )
extent = 0.0; blurRadius = 0.0;
const auto minDimension = std::min( d->rect.width(), d->rect.height() ); const float t = 0.5 * std::min( d->rect.width(), d->rect.height() );
const float uniformExtent = blurRadius / t;
const float uniformExtent = ( extent / minDimension ) * 2.0; if ( !qFuzzyCompare( d->material.blurExtent, uniformExtent ) )
if ( !qFuzzyCompare( d->material.extent, uniformExtent ) )
{ {
d->material.extent = uniformExtent; d->material.blurExtent = uniformExtent;
markDirty(QSGNode::DirtyMaterial);
}
const auto uniformOffset = QVector2D( dx, dy ) / minDimension;
if ( d->material.offset != uniformOffset)
{
d->material.offset = uniformOffset;
markDirty( QSGNode::DirtyMaterial ); markDirty( QSGNode::DirtyMaterial );
} }
} }
void BoxShadowNode::setClipRect( const QRectF& rect )
{
Q_UNUSED( rect )
}
void BoxShadowNode::updateGeometry() void BoxShadowNode::updateGeometry()
{ {
Q_D( BoxShadowNode ); Q_D( BoxShadowNode );
const auto sz = d->material.extent;
const auto aspect = d->material.aspect;
auto rect = d->rect.adjusted(
-sz * aspect.x(), -sz * aspect.y(),
sz * aspect.x(), sz * aspect.y() );
auto offsetLength = d->material.offset.length();
rect = rect.adjusted(
-offsetLength * aspect.x(), -offsetLength * aspect.y(),
offsetLength * aspect.x(), offsetLength * aspect.y() );
QSGGeometry::updateTexturedRectGeometry( QSGGeometry::updateTexturedRectGeometry(
&d->geometry, rect, QRectF( 0.0, 0.0, 1.0, 1.0 ) ); &d->geometry, d->rect, QRectF( -0.5, -0.5, 1.0, 1.0 ) );
markDirty( QSGNode::DirtyGeometry ); markDirty( QSGNode::DirtyGeometry );
} }

View File

@ -21,7 +21,9 @@ class BoxShadowNode : public QSGGeometryNode
void setRect( const QRectF& ); void setRect( const QRectF& );
void setShape( const QskBoxShapeMetrics& ); void setShape( const QskBoxShapeMetrics& );
void setColor( const QColor& ); void setColor( const QColor& );
void setShadow( qreal extent, qreal dx, qreal dy ); void setBlurRadius( qreal );
void setClipRect( const QRectF& );
void updateGeometry(); void updateGeometry();

View File

@ -40,6 +40,10 @@ namespace
{ {
const auto box = static_cast< const ShadowedBox* >( skinnable ); const auto box = static_cast< const ShadowedBox* >( skinnable );
const auto r = box->subControlRect( ShadowedBox::Panel );
if ( r.isEmpty() )
return nullptr;
switch ( nodeRole ) switch ( nodeRole )
{ {
case ShadowRole: case ShadowRole:
@ -48,17 +52,14 @@ namespace
if ( shadowNode == nullptr ) if ( shadowNode == nullptr )
shadowNode = new BoxShadowNode(); shadowNode = new BoxShadowNode();
const auto& s = box->shadow(); const auto& shadowMetrics = box->shadow();
const qreal dx = s.extent + s.xOffset;
const qreal dy = s.extent + s.yOffset;
auto r = box->subControlRect( ShadowedBox::Panel ); shadowNode->setRect( shadowMetrics.shadowRect( r ) );
r.adjust( -dx, -dy, dx, dy );
shadowNode->setRect( r );
shadowNode->setShape( box->shape() ); shadowNode->setShape( box->shape() );
shadowNode->setShadow( s.extent, s.xOffset, s.yOffset ); shadowNode->setBlurRadius( shadowMetrics.blurRadius() );
shadowNode->setColor( box->shadowColor() ); shadowNode->setColor( box->shadowColor() );
shadowNode->setClipRect( r );
shadowNode->updateGeometry(); shadowNode->updateGeometry();
return shadowNode; return shadowNode;
@ -69,10 +70,10 @@ namespace
if ( boxNode == nullptr ) if ( boxNode == nullptr )
boxNode = new QskBoxNode(); boxNode = new QskBoxNode();
const QRectF r = box->subControlRect( ShadowedBox::Panel ); const auto r = box->subControlRect( ShadowedBox::Panel );
boxNode->setBoxData( r, box->shape(), QskBoxBorderMetrics(), boxNode->setBoxData( r, box->shape(), box->borderWidth(),
QskBoxBorderColors(), box->gradient() ); box->borderColor(), box->gradient() );
return boxNode; return boxNode;
} }
@ -96,13 +97,13 @@ ShadowedBox::~ShadowedBox()
{ {
} }
void ShadowedBox::setShadow( const Shadow& shadow ) void ShadowedBox::setShadow( const QskShadowMetrics& shadow )
{ {
m_shadow = shadow; m_shadow = shadow;
update(); update();
} }
const ShadowedBox::Shadow& ShadowedBox::shadow() const const QskShadowMetrics& ShadowedBox::shadow() const
{ {
return m_shadow; return m_shadow;
} }
@ -140,4 +141,26 @@ const QskBoxShapeMetrics& ShadowedBox::shape() const
return m_shape; return m_shape;
} }
void ShadowedBox::setBorderWidth( qreal width )
{
m_borderWidth = qMax( width, 0.0 );
update();
}
qreal ShadowedBox::borderWidth() const
{
return m_borderWidth;
}
void ShadowedBox::setBorderColor( const QColor& color )
{
m_borderColor = color;
update();
}
QColor ShadowedBox::borderColor() const
{
return m_borderColor;
}
#include "moc_ShadowedBox.cpp" #include "moc_ShadowedBox.cpp"

View File

@ -7,6 +7,7 @@
#include <QskControl.h> #include <QskControl.h>
#include <QskBoxShapeMetrics.h> #include <QskBoxShapeMetrics.h>
#include <QskShadowMetrics.h>
class QskGradient; class QskGradient;
@ -17,19 +18,11 @@ class ShadowedBox : public QskControl
public: public:
QSK_SUBCONTROLS( Panel ) QSK_SUBCONTROLS( Panel )
class Shadow
{
public:
qreal extent = 0.0;
qreal xOffset = 0.0;
qreal yOffset = 0.0;
};
ShadowedBox(QQuickItem* parent = nullptr); ShadowedBox(QQuickItem* parent = nullptr);
~ShadowedBox() override; ~ShadowedBox() override;
void setShadow( const Shadow& ); void setShadow( const QskShadowMetrics& );
const Shadow& shadow() const; const QskShadowMetrics& shadow() const;
void setGradient( const QskGradient& ); void setGradient( const QskGradient& );
const QskGradient& gradient() const; const QskGradient& gradient() const;
@ -40,9 +33,19 @@ class ShadowedBox : public QskControl
void setShape( const QskBoxShapeMetrics& ); void setShape( const QskBoxShapeMetrics& );
const QskBoxShapeMetrics& shape() const; const QskBoxShapeMetrics& shape() const;
void setBorderWidth( qreal width );
qreal borderWidth() const;
void setBorderColor( const QColor& );
QColor borderColor() const;
private: private:
Shadow m_shadow; QskShadowMetrics m_shadow;
QColor m_shadowColor = Qt::black; QColor m_shadowColor = Qt::black;
QskGradient m_gradient; QskGradient m_gradient;
QskBoxShapeMetrics m_shape; QskBoxShapeMetrics m_shape;
qreal m_borderWidth = 0.0;
QColor m_borderColor = Qt::black;
}; };

View File

@ -1,72 +1,42 @@
/*
Heavily inspired by code from the kirigami project:
Copyright 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
License: LGPL-2.0-or-later
https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
Copyright: 2017 Inigo Quilez
License: MIT
*/
uniform lowp float opacity; uniform lowp float opacity;
uniform lowp float extent; uniform lowp float blurExtent;
uniform lowp vec4 radius; uniform lowp vec4 radius;
uniform lowp vec4 color; uniform lowp vec4 color;
uniform lowp vec2 offset;
uniform lowp vec2 aspect; uniform lowp vec2 aspect;
varying lowp vec2 coord; varying lowp vec2 coord;
lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point ) lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point )
{ {
if ( point.x > 0.0 ) if ( point.x > 0.0 )
return ( point.y > 0.0) ? radii.x : radii.y; return ( point.y > 0.0) ? radii.x : radii.y;
else else
return ( point.y > 0.0) ? radii.z : radii.w; return ( point.y > 0.0) ? radii.z : radii.w;
}
bool isInside( in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
{
lowp float r = effectiveRadius( radii, point );
lowp vec2 d = abs(point) - size + r;
lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
return l <= 0.0;
}
lowp float shadowAt(
in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
{
lowp float r = effectiveRadius( radii, point );
lowp vec2 d = abs(point) - size + r;
return min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
} }
void main() void main()
{ {
lowp vec4 col = vec4(0.0); lowp vec4 col = vec4(0.0);
if ( extent > 0.0 && opacity > 0.0 ) if ( opacity > 0.0 )
{ {
lowp float t = 1.0 + 2.0 * length( offset ) + extent; const lowp float minRadius = 0.05;
if ( !isInside( coord, aspect / t, radius / t) ) lowp float e2 = 0.5 * blurExtent;
{ lowp float r = 2.0 * effectiveRadius( radius, coord );
const lowp float minRadius = 0.05;
lowp float e2 = 0.5 * extent;
lowp vec4 f = minRadius / max( radius, minRadius ); lowp float f = minRadius / max( r, minRadius );
lowp vec4 r = radius + e2 * f;
lowp float shadow = shadowAt( r += e2 * f;
coord - 2.0 * offset / t, aspect / t, r / t);
lowp float v = smoothstep( -e2, e2, shadow ); lowp vec2 d = r + blurExtent - aspect * ( 1.0 - abs( 2.0 * coord ) );
lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) );
col = mix( color, vec4(0.0), v ) * opacity; lowp float shadow = l - r;
}
} lowp float v = smoothstep( -e2, e2, shadow );
col = mix( color, vec4(0.0), v ) * opacity;
}
gl_FragColor = col; gl_FragColor = col;
} }

View File

@ -8,6 +8,6 @@ varying mediump vec2 coord;
void main() void main()
{ {
coord = ( -1.0 + 2.0 * in_coord ) * aspect; coord = in_coord;
gl_Position = matrix * in_vertex; gl_Position = matrix * in_vertex;
} }

View File

@ -5,6 +5,8 @@
#include "ShadowedBox.h" #include "ShadowedBox.h"
#include <SkinnyShortcut.h>
#include <QGuiApplication> #include <QGuiApplication>
#include <QskObjectCounter.h> #include <QskObjectCounter.h>
#include <QskWindow.h> #include <QskWindow.h>
@ -18,15 +20,26 @@ class Box : public ShadowedBox
Box( QQuickItem* parent = nullptr ) Box( QQuickItem* parent = nullptr )
: ShadowedBox( parent ) : ShadowedBox( parent )
{ {
Shadow shadow; const qreal w = 10;
shadow.extent = 10;
shadow.xOffset = 20.0; QskShadowMetrics shadow;
shadow.yOffset = 20.0; //shadow.setOffset( 20.0, 20.0 );
shadow.setSpreadRadius( w );
shadow.setBlurRadius( w );
setShadow( shadow ); setShadow( shadow );
setShadowColor( Qt::black ); setShadowColor( Qt::black );
setGradient( Qt::darkRed );
setShape( QskBoxShapeMetrics( 5, 10, 15, 20 ) ); QColor c( Qt::darkRed );
#if 0
c.setAlpha( 100 );
#endif
setGradient( c );
setShape( QskBoxShapeMetrics( 40, 10, 15, 5 ) );
setBorderWidth( w );
setBorderColor( Qt::blue );
} }
}; };
@ -38,6 +51,8 @@ int main( int argc, char* argv[] )
QGuiApplication app( argc, argv ); QGuiApplication app( argc, argv );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
auto layout = new QskLinearBox(); auto layout = new QskLinearBox();
layout->setPanel( true ); layout->setPanel( true );
#if 1 #if 1

View File

@ -73,11 +73,14 @@ QVariant QskShadowMetrics::interpolate(
QRectF QskShadowMetrics::shadowRect( const QRectF& sourceRect ) const QRectF QskShadowMetrics::shadowRect( const QRectF& sourceRect ) const
{ {
const auto metrics = toAbsolute( sourceRect.size() );
const auto extent = metrics.m_spreadRadius + metrics.m_blurRadius;
return QRectF( return QRectF(
sourceRect.x() + m_offset.x() - m_spreadRadius, sourceRect.x() + metrics.m_offset.x() - extent,
sourceRect.y() + m_offset.y() - m_spreadRadius, sourceRect.y() + metrics.m_offset.y() - extent,
sourceRect.width() + 2 * m_spreadRadius, sourceRect.width() + 2 * extent,
sourceRect.height() + 2 * m_spreadRadius ); sourceRect.height() + 2 * extent );
} }
uint QskShadowMetrics::hash( uint seed ) const noexcept uint QskShadowMetrics::hash( uint seed ) const noexcept