playing with shadows
This commit is contained in:
parent
1e9080ee82
commit
2438b0f7ee
@ -5,6 +5,7 @@ SUBDIRS += \
|
||||
dialogbuttons \
|
||||
invoker \
|
||||
inputpanel \
|
||||
shadows \
|
||||
images
|
||||
|
||||
qtHaveModule(webengine) {
|
||||
|
@ -27,13 +27,12 @@ namespace
|
||||
QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override;
|
||||
|
||||
private:
|
||||
int m_matrixLocation = -1;
|
||||
int m_opacityLocation = -1;
|
||||
int m_aspectLocation = -1;
|
||||
int m_extentLocation = -1;
|
||||
int m_radiusLocation = -1;
|
||||
int m_colorLocation = -1;
|
||||
int m_offsetLocation = -1;
|
||||
int m_matrixId = -1;
|
||||
int m_opacityId = -1;
|
||||
int m_aspectId = -1;
|
||||
int m_blurExtentId = -1;
|
||||
int m_radiusId = -1;
|
||||
int m_colorId = -1;
|
||||
};
|
||||
|
||||
class Material final : public QSGMaterial
|
||||
@ -48,10 +47,9 @@ namespace
|
||||
int compare( const QSGMaterial* other ) const override;
|
||||
|
||||
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};
|
||||
QColor color = Qt::black;
|
||||
QVector2D offset;
|
||||
};
|
||||
|
||||
Shader::Shader()
|
||||
@ -74,13 +72,12 @@ namespace
|
||||
|
||||
auto p = program();
|
||||
|
||||
m_matrixLocation = p->uniformLocation( "matrix" );
|
||||
m_aspectLocation = p->uniformLocation( "aspect" );
|
||||
m_opacityLocation = p->uniformLocation( "opacity" );
|
||||
m_extentLocation = p->uniformLocation( "extent" );
|
||||
m_offsetLocation = p->uniformLocation( "offset" );
|
||||
m_radiusLocation = p->uniformLocation( "radius" );
|
||||
m_colorLocation = p->uniformLocation( "color" );
|
||||
m_matrixId = p->uniformLocation( "matrix" );
|
||||
m_aspectId = p->uniformLocation( "aspect" );
|
||||
m_opacityId = p->uniformLocation( "opacity" );
|
||||
m_blurExtentId = p->uniformLocation( "blurExtent" );
|
||||
m_radiusId = p->uniformLocation( "radius" );
|
||||
m_colorId = p->uniformLocation( "color" );
|
||||
}
|
||||
|
||||
void Shader::updateState( const QSGMaterialShader::RenderState& state,
|
||||
@ -89,20 +86,20 @@ namespace
|
||||
auto p = program();
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
p->setUniformValue(m_matrixLocation, state.combinedMatrix() );
|
||||
p->setUniformValue( m_matrixId, state.combinedMatrix() );
|
||||
|
||||
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 );
|
||||
|
||||
p->setUniformValue( m_aspectLocation, material->aspect );
|
||||
p->setUniformValue( m_extentLocation, material->extent );
|
||||
p->setUniformValue( m_radiusLocation, material->radius );
|
||||
p->setUniformValue( m_colorLocation, material->color );
|
||||
p->setUniformValue( m_offsetLocation, material->offset );
|
||||
p->setUniformValue( m_aspectId, material->aspect );
|
||||
p->setUniformValue( m_blurExtentId, material->blurExtent);
|
||||
p->setUniformValue( m_radiusId, material->radius );
|
||||
p->setUniformValue( m_colorId, material->color );
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,9 +124,8 @@ namespace
|
||||
auto material = static_cast< const Material* >( other );
|
||||
|
||||
if ( material->color == color
|
||||
&& material->offset == offset
|
||||
&& material->aspect == aspect
|
||||
&& qFuzzyCompare(material->extent, extent)
|
||||
&& qFuzzyCompare(material->blurExtent, blurExtent)
|
||||
&& qFuzzyCompare(material->radius, radius) )
|
||||
{
|
||||
return 0;
|
||||
@ -175,16 +171,16 @@ void BoxShadowNode::setRect( const QRectF& rect )
|
||||
|
||||
d->rect = rect;
|
||||
|
||||
QVector2D newAspect( 1.0, 1.0 );
|
||||
QVector2D aspect( 1.0, 1.0 );
|
||||
|
||||
if ( rect.width() >= rect.height() )
|
||||
newAspect.setX( rect.width() / rect.height() );
|
||||
aspect.setX( rect.width() / rect.height() );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
@ -193,7 +189,7 @@ void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape )
|
||||
{
|
||||
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 r2 = shape.radius( Qt::TopRightCorner ).width();
|
||||
@ -224,55 +220,38 @@ void BoxShadowNode::setColor( const QColor& color )
|
||||
if ( 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 );
|
||||
|
||||
if ( extent <= 0.0 )
|
||||
extent = 0.0;
|
||||
if ( blurRadius <= 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.extent, uniformExtent ) )
|
||||
if ( !qFuzzyCompare( d->material.blurExtent, uniformExtent ) )
|
||||
{
|
||||
d->material.extent = uniformExtent;
|
||||
markDirty(QSGNode::DirtyMaterial);
|
||||
}
|
||||
|
||||
const auto uniformOffset = QVector2D( dx, dy ) / minDimension;
|
||||
|
||||
if ( d->material.offset != uniformOffset)
|
||||
{
|
||||
d->material.offset = uniformOffset;
|
||||
d->material.blurExtent = uniformExtent;
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadowNode::setClipRect( const QRectF& rect )
|
||||
{
|
||||
Q_UNUSED( rect )
|
||||
}
|
||||
|
||||
void BoxShadowNode::updateGeometry()
|
||||
{
|
||||
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(
|
||||
&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 );
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ class BoxShadowNode : public QSGGeometryNode
|
||||
void setRect( const QRectF& );
|
||||
void setShape( const QskBoxShapeMetrics& );
|
||||
void setColor( const QColor& );
|
||||
void setShadow( qreal extent, qreal dx, qreal dy );
|
||||
void setBlurRadius( qreal );
|
||||
|
||||
void setClipRect( const QRectF& );
|
||||
|
||||
void updateGeometry();
|
||||
|
||||
|
@ -40,6 +40,10 @@ namespace
|
||||
{
|
||||
const auto box = static_cast< const ShadowedBox* >( skinnable );
|
||||
|
||||
const auto r = box->subControlRect( ShadowedBox::Panel );
|
||||
if ( r.isEmpty() )
|
||||
return nullptr;
|
||||
|
||||
switch ( nodeRole )
|
||||
{
|
||||
case ShadowRole:
|
||||
@ -48,17 +52,14 @@ namespace
|
||||
if ( shadowNode == nullptr )
|
||||
shadowNode = new BoxShadowNode();
|
||||
|
||||
const auto& s = box->shadow();
|
||||
const qreal dx = s.extent + s.xOffset;
|
||||
const qreal dy = s.extent + s.yOffset;
|
||||
const auto& shadowMetrics = box->shadow();
|
||||
|
||||
auto r = box->subControlRect( ShadowedBox::Panel );
|
||||
r.adjust( -dx, -dy, dx, dy );
|
||||
|
||||
shadowNode->setRect( r );
|
||||
shadowNode->setRect( shadowMetrics.shadowRect( r ) );
|
||||
shadowNode->setShape( box->shape() );
|
||||
shadowNode->setShadow( s.extent, s.xOffset, s.yOffset );
|
||||
shadowNode->setBlurRadius( shadowMetrics.blurRadius() );
|
||||
shadowNode->setColor( box->shadowColor() );
|
||||
shadowNode->setClipRect( r );
|
||||
|
||||
shadowNode->updateGeometry();
|
||||
|
||||
return shadowNode;
|
||||
@ -69,10 +70,10 @@ namespace
|
||||
if ( boxNode == nullptr )
|
||||
boxNode = new QskBoxNode();
|
||||
|
||||
const QRectF r = box->subControlRect( ShadowedBox::Panel );
|
||||
const auto r = box->subControlRect( ShadowedBox::Panel );
|
||||
|
||||
boxNode->setBoxData( r, box->shape(), QskBoxBorderMetrics(),
|
||||
QskBoxBorderColors(), box->gradient() );
|
||||
boxNode->setBoxData( r, box->shape(), box->borderWidth(),
|
||||
box->borderColor(), box->gradient() );
|
||||
|
||||
return boxNode;
|
||||
}
|
||||
@ -96,13 +97,13 @@ ShadowedBox::~ShadowedBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ShadowedBox::setShadow( const Shadow& shadow )
|
||||
void ShadowedBox::setShadow( const QskShadowMetrics& shadow )
|
||||
{
|
||||
m_shadow = shadow;
|
||||
update();
|
||||
}
|
||||
|
||||
const ShadowedBox::Shadow& ShadowedBox::shadow() const
|
||||
const QskShadowMetrics& ShadowedBox::shadow() const
|
||||
{
|
||||
return m_shadow;
|
||||
}
|
||||
@ -140,4 +141,26 @@ const QskBoxShapeMetrics& ShadowedBox::shape() const
|
||||
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"
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <QskControl.h>
|
||||
#include <QskBoxShapeMetrics.h>
|
||||
#include <QskShadowMetrics.h>
|
||||
|
||||
class QskGradient;
|
||||
|
||||
@ -17,19 +18,11 @@ class ShadowedBox : public QskControl
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel )
|
||||
|
||||
class Shadow
|
||||
{
|
||||
public:
|
||||
qreal extent = 0.0;
|
||||
qreal xOffset = 0.0;
|
||||
qreal yOffset = 0.0;
|
||||
};
|
||||
|
||||
ShadowedBox(QQuickItem* parent = nullptr);
|
||||
~ShadowedBox() override;
|
||||
|
||||
void setShadow( const Shadow& );
|
||||
const Shadow& shadow() const;
|
||||
void setShadow( const QskShadowMetrics& );
|
||||
const QskShadowMetrics& shadow() const;
|
||||
|
||||
void setGradient( const QskGradient& );
|
||||
const QskGradient& gradient() const;
|
||||
@ -40,9 +33,19 @@ class ShadowedBox : public QskControl
|
||||
void setShape( const QskBoxShapeMetrics& );
|
||||
const QskBoxShapeMetrics& shape() const;
|
||||
|
||||
void setBorderWidth( qreal width );
|
||||
qreal borderWidth() const;
|
||||
|
||||
void setBorderColor( const QColor& );
|
||||
QColor borderColor() const;
|
||||
|
||||
private:
|
||||
Shadow m_shadow;
|
||||
QskShadowMetrics m_shadow;
|
||||
QColor m_shadowColor = Qt::black;
|
||||
|
||||
QskGradient m_gradient;
|
||||
QskBoxShapeMetrics m_shape;
|
||||
|
||||
qreal m_borderWidth = 0.0;
|
||||
QColor m_borderColor = Qt::black;
|
||||
};
|
||||
|
@ -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 extent;
|
||||
uniform lowp float blurExtent;
|
||||
uniform lowp vec4 radius;
|
||||
uniform lowp vec4 color;
|
||||
uniform lowp vec2 offset;
|
||||
uniform lowp vec2 aspect;
|
||||
|
||||
varying lowp vec2 coord;
|
||||
|
||||
lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point )
|
||||
{
|
||||
if ( point.x > 0.0 )
|
||||
return ( point.y > 0.0) ? radii.x : radii.y;
|
||||
else
|
||||
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;
|
||||
if ( point.x > 0.0 )
|
||||
return ( point.y > 0.0) ? radii.x : radii.y;
|
||||
else
|
||||
return ( point.y > 0.0) ? radii.z : radii.w;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 col = vec4(0.0);
|
||||
lowp vec4 col = vec4(0.0);
|
||||
|
||||
if ( extent > 0.0 && opacity > 0.0 )
|
||||
{
|
||||
lowp float t = 1.0 + 2.0 * length( offset ) + extent;
|
||||
if ( opacity > 0.0 )
|
||||
{
|
||||
const lowp float minRadius = 0.05;
|
||||
|
||||
if ( !isInside( coord, aspect / t, radius / t) )
|
||||
{
|
||||
const lowp float minRadius = 0.05;
|
||||
lowp float e2 = 0.5 * extent;
|
||||
lowp float e2 = 0.5 * blurExtent;
|
||||
lowp float r = 2.0 * effectiveRadius( radius, coord );
|
||||
|
||||
lowp vec4 f = minRadius / max( radius, minRadius );
|
||||
lowp vec4 r = radius + e2 * f;
|
||||
lowp float f = minRadius / max( r, minRadius );
|
||||
|
||||
lowp float shadow = shadowAt(
|
||||
coord - 2.0 * offset / t, aspect / t, r / t);
|
||||
r += e2 * f;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ varying mediump vec2 coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
coord = ( -1.0 + 2.0 * in_coord ) * aspect;
|
||||
coord = in_coord;
|
||||
gl_Position = matrix * in_vertex;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "ShadowedBox.h"
|
||||
|
||||
#include <SkinnyShortcut.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QskObjectCounter.h>
|
||||
#include <QskWindow.h>
|
||||
@ -18,15 +20,26 @@ class Box : public ShadowedBox
|
||||
Box( QQuickItem* parent = nullptr )
|
||||
: ShadowedBox( parent )
|
||||
{
|
||||
Shadow shadow;
|
||||
shadow.extent = 10;
|
||||
shadow.xOffset = 20.0;
|
||||
shadow.yOffset = 20.0;
|
||||
const qreal w = 10;
|
||||
|
||||
QskShadowMetrics shadow;
|
||||
//shadow.setOffset( 20.0, 20.0 );
|
||||
shadow.setSpreadRadius( w );
|
||||
shadow.setBlurRadius( w );
|
||||
|
||||
setShadow( shadow );
|
||||
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 );
|
||||
|
||||
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
||||
|
||||
auto layout = new QskLinearBox();
|
||||
layout->setPanel( true );
|
||||
#if 1
|
||||
|
@ -73,11 +73,14 @@ QVariant QskShadowMetrics::interpolate(
|
||||
|
||||
QRectF QskShadowMetrics::shadowRect( const QRectF& sourceRect ) const
|
||||
{
|
||||
const auto metrics = toAbsolute( sourceRect.size() );
|
||||
const auto extent = metrics.m_spreadRadius + metrics.m_blurRadius;
|
||||
|
||||
return QRectF(
|
||||
sourceRect.x() + m_offset.x() - m_spreadRadius,
|
||||
sourceRect.y() + m_offset.y() - m_spreadRadius,
|
||||
sourceRect.width() + 2 * m_spreadRadius,
|
||||
sourceRect.height() + 2 * m_spreadRadius );
|
||||
sourceRect.x() + metrics.m_offset.x() - extent,
|
||||
sourceRect.y() + metrics.m_offset.y() - extent,
|
||||
sourceRect.width() + 2 * extent,
|
||||
sourceRect.height() + 2 * extent );
|
||||
}
|
||||
|
||||
uint QskShadowMetrics::hash( uint seed ) const noexcept
|
||||
|
Loading…
x
Reference in New Issue
Block a user