summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2019-02-22 14:38:19 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-02-27 12:29:18 +0000
commitce4c6534a0d3998c9aa84bec1fda6490c1020345 (patch)
tree3ce119d3e381450ae569c2e6e457bd18972f0a51
parentd13c01ce0e5e3fd921463c5a99aa21c1be244521 (diff)
Drop shadow for distance field text
Implements a shader for drop shadows + distance field text. The idea here is that we expand the area drawn to accommodate the shadow, then we sample the distance field twice, once for the glyph and once for the shadow. But we have to clamp all texture coordinates to the bounds of the current glyph to avoid sampling outside the glyph. Since we have empty margins around each glyph, this works. At 4 floats extra per vertex, it was the cheapest approach I could think of. Fixes: QT3DS-3090 Change-Id: I402099474266b2e0c61194d7855635acd9b27051 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r--src/runtime/q3dsdistancefieldmaterialgenerator.cpp14
-rw-r--r--src/runtime/q3dsdistancefieldmaterialgenerator_p.h3
-rw-r--r--src/runtime/q3dsres.qrc4
-rw-r--r--src/runtime/q3dsscenemanager.cpp84
-rw-r--r--src/runtime/q3dsscenemanager_p.h1
-rw-r--r--src/runtime/q3dstextmesh.cpp36
-rw-r--r--src/runtime/q3dstextmesh_p.h3
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow.frag29
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow.vert75
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow_core.frag34
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow_core.vert55
11 files changed, 310 insertions, 28 deletions
diff --git a/src/runtime/q3dsdistancefieldmaterialgenerator.cpp b/src/runtime/q3dsdistancefieldmaterialgenerator.cpp
index 8dacef1..abb1b4d 100644
--- a/src/runtime/q3dsdistancefieldmaterialgenerator.cpp
+++ b/src/runtime/q3dsdistancefieldmaterialgenerator.cpp
@@ -42,7 +42,8 @@
QT_BEGIN_NAMESPACE
-Qt3DRender::QMaterial *Q3DSDistanceFieldMaterialGenerator::generateMaterial(const QVector<Qt3DRender::QParameter *> &params)
+Qt3DRender::QMaterial *Q3DSDistanceFieldMaterialGenerator::generateMaterial(const QVector<Qt3DRender::QParameter *> &params,
+ bool dropShadow)
{
Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
@@ -53,13 +54,10 @@ Qt3DRender::QMaterial *Q3DSDistanceFieldMaterialGenerator::generateMaterial(cons
Q3DSDefaultMaterialGenerator::addDefaultApiFilter(technique, &isGLES);
Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram;
- if (isGLES) {
- shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QLatin1String("qrc:/q3ds/shaders/distancefieldtext.vert"))));
- shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QLatin1String("qrc:/q3ds/shaders/distancefieldtext.frag"))));
- } else {
- shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QLatin1String("qrc:/q3ds/shaders/distancefieldtext_core.vert"))));
- shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QLatin1String("qrc:/q3ds/shaders/distancefieldtext_core.frag"))));
- }
+
+ QString name = QStringLiteral("distancefieldtext") + (dropShadow ? QStringLiteral("_dropshadow") : QString()) + (!isGLES ? QStringLiteral("_core") : QString());
+ shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/q3ds/shaders/") + name + QStringLiteral(".vert"))));
+ shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/q3ds/shaders/") + name + QStringLiteral(".frag"))));
Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass;
Qt3DRender::QFilterKey *transFilterKey = new Qt3DRender::QFilterKey;
diff --git a/src/runtime/q3dsdistancefieldmaterialgenerator_p.h b/src/runtime/q3dsdistancefieldmaterialgenerator_p.h
index bb85c73..07ef527 100644
--- a/src/runtime/q3dsdistancefieldmaterialgenerator_p.h
+++ b/src/runtime/q3dsdistancefieldmaterialgenerator_p.h
@@ -55,7 +55,8 @@ class QMaterial;
class Q3DSDistanceFieldMaterialGenerator
{
public:
- Qt3DRender::QMaterial *generateMaterial(const QVector<Qt3DRender::QParameter *> &params);
+ Qt3DRender::QMaterial *generateMaterial(const QVector<Qt3DRender::QParameter *> &params,
+ bool dropShadow);
};
QT_END_NAMESPACE
diff --git a/src/runtime/q3dsres.qrc b/src/runtime/q3dsres.qrc
index e70f636..0c20267 100644
--- a/src/runtime/q3dsres.qrc
+++ b/src/runtime/q3dsres.qrc
@@ -132,5 +132,9 @@
<file alias="res/effectlib/gles2/shadowMapping.glsllib">../../res/effectlib/gles2/shadowMapping.glsllib</file>
<file alias="res/effectlib/gles2/SSAOCustomMaterial.glsllib">../../res/effectlib/gles2/SSAOCustomMaterial.glsllib</file>
<file alias="res/effectlib/gles2/tangentSpaceNormalTexture.glsllib">../../res/effectlib/gles2/tangentSpaceNormalTexture.glsllib</file>
+ <file>shaders/distancefieldtext_dropshadow_core.vert</file>
+ <file>shaders/distancefieldtext_dropshadow_core.frag</file>
+ <file>shaders/distancefieldtext_dropshadow.frag</file>
+ <file>shaders/distancefieldtext_dropshadow.vert</file>
</qresource>
</RCC>
diff --git a/src/runtime/q3dsscenemanager.cpp b/src/runtime/q3dsscenemanager.cpp
index d6f909c..8dc7aa2 100644
--- a/src/runtime/q3dsscenemanager.cpp
+++ b/src/runtime/q3dsscenemanager.cpp
@@ -4761,10 +4761,9 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
}
#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
- static bool distanceFieldRendering = distanceFieldsEnabled();
+ static bool shouldDistanceFieldRender = distanceFieldsEnabled();
- bool shouldDistanceFieldRender = distanceFieldRendering && !text3DS->shadow();
- if (data->entity != nullptr && shouldDistanceFieldRender != data->distanceFieldText) {
+ if (data->entity != nullptr && text3DS->shadow() != data->dropShadow) {
for (int i = 0; i < data->glyphsReferencedInSubentity.size(); ++i) {
QPair<Q3DSDistanceFieldGlyphCache *, QVector<quint32> > &glyphsReferenced = data->glyphsReferencedInSubentity[i];
if (glyphsReferenced.first != nullptr && !glyphsReferenced.second.isEmpty())
@@ -4791,6 +4790,7 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
if (shouldDistanceFieldRender) {
data->distanceFieldText = true;
+ data->dropShadow = text3DS->shadow();
QVector2D boundingBox = text3DS->boundingBox();
QRawFont font = m_fontDatabase->findFont(text3DS->font());
@@ -4969,6 +4969,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
QVector<quint32> glyphIndexes;
Q3DSDistanceFieldGlyphCache *cache;
float fontScale;
+ float shadowOffsetX;
+ float shadowOffsetY;
};
QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo> glyphsPerTexture;
@@ -5021,6 +5023,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
cache->processPendingGlyphs();
qreal fontPixelSize = glyphRun.rawFont().pixelSize();
+ float shadowOffsetX = float(fontPixelSize) * text3DS->shadowOffsetX() / 1000.0f;
+ float shadowOffsetY = float(fontPixelSize) * text3DS->shadowOffsetY() / 1000.0f;
qreal maxTexMargin = cache->distanceFieldRadius();
qreal fontScale = cache->fontScale(fontPixelSize);
qreal margin = 2;
@@ -5056,11 +5060,24 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
float cy1 = float(position.y() - metrics.baselineY) + offsetY;
float cy2 = cy1 + float(metrics.height);
+ if (text3DS->shadow()) {
+ cx2 += shadowOffsetX;
+ cy2 += shadowOffsetY;
+ }
+
float tx1 = float(c.x + c.xMargin);
float tx2 = tx1 + float(c.width);
float ty1 = float(c.y + c.yMargin);
float ty2 = ty1 + float(c.height);
+ float ttx2 = tx2;
+ float tty2 = ty2;
+
+ if (text3DS->shadow()) {
+ tx2 += float(c.width) * shadowOffsetX / float(metrics.width);
+ ty2 += float(c.height) * shadowOffsetY / float(metrics.height);
+ }
+
const QSGDistanceFieldGlyphCache::Texture *texture = cache->glyphTexture(glyphIndex);
if (texture->textureId == 0) {
qWarning() << "Empty texture for glyph" << glyphIndex;
@@ -5071,9 +5088,11 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
GlyphInfo &glyphInfo = glyphsPerTexture[textureInfo];
glyphInfo.fontScale = float(fontScale);
+ glyphInfo.shadowOffsetX = shadowOffsetX;
+ glyphInfo.shadowOffsetY = shadowOffsetY;
QVector<float> &vertexes = glyphInfo.vertexes;
- vertexes.reserve(vertexes.size() + 10);
+ vertexes.reserve(vertexes.size() + 20 + (text3DS->shadow() ? 16 : 0));
vertexes.append(cx1);
vertexes.append(0.0);
@@ -5081,23 +5100,51 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
vertexes.append(tx1);
vertexes.append(ty1);
+ if (text3DS->shadow()) {
+ vertexes.append(tx1);
+ vertexes.append(ty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
vertexes.append(cx2);
vertexes.append(0.0);
vertexes.append(cy1);
vertexes.append(tx2);
vertexes.append(ty1);
+ if (text3DS->shadow()) {
+ vertexes.append(tx1);
+ vertexes.append(ty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
vertexes.append(cx2);
vertexes.append(0.0);
vertexes.append(cy2);
vertexes.append(tx2);
vertexes.append(ty2);
+ if (text3DS->shadow()) {
+ vertexes.append(tx1);
+ vertexes.append(ty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
vertexes.append(cx1);
vertexes.append(0.0);
vertexes.append(cy2);
vertexes.append(tx1);
vertexes.append(ty2);
+
+ if (text3DS->shadow()) {
+ vertexes.append(tx1);
+ vertexes.append(ty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
}
}
}
@@ -5140,11 +5187,23 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
Qt3DRender::QParameter *fontScaleParam = new Qt3DRender::QParameter;
fontScaleParam->setName(QLatin1String("fontScale"));
- Qt3DRender::QMaterial *material = m_distanceFieldMaterialGenerator->generateMaterial({ colorParam,
- textureParam,
- fontScaleParam,
- textureWidthParam,
- textureHeightParam });
+ QVector<Qt3DRender::QParameter *> params = { colorParam,
+ textureParam,
+ fontScaleParam,
+ textureWidthParam,
+ textureHeightParam };
+
+ if (text3DS->shadow()) {
+ Qt3DRender::QParameter *shadowColorParam = new Qt3DRender::QParameter;
+ shadowColorParam->setName(QLatin1String("shadowColor"));
+ params.append(shadowColorParam);
+
+ Qt3DRender::QParameter *shadowOffsetParam = new Qt3DRender::QParameter;
+ shadowOffsetParam->setName(QLatin1String("shadowOffset"));
+ params.append(shadowOffsetParam);
+ }
+
+ Qt3DRender::QMaterial *material = m_distanceFieldMaterialGenerator->generateMaterial(params, text3DS->shadow());
subentity->addComponent(material);
subentities.append(subentity);
}
@@ -5152,6 +5211,7 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo>::const_iterator it;
int subentityIndex = 0;
+ int shadowRgb = int(2.55f * (100 - int(text3DS->shadowStrength())));
for (it = glyphsPerTexture.constBegin(); it != glyphsPerTexture.constEnd(); ++it) {
const GlyphInfo &glyphInfo = it.value();
const QVector<float> &vertexes = glyphInfo.vertexes;
@@ -5168,7 +5228,7 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
{
Q3DSTextMesh *mesh = qobject_cast<Q3DSTextMesh *>(component);
if (mesh != nullptr)
- mesh->setVertexes(vertexes);
+ mesh->setVertexes(vertexes, text3DS->shadow());
}
{
@@ -5184,6 +5244,10 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
parameter->setValue(color * data->globalOpacity);
data->colorParam.append(parameter);
+ } else if (parameter->name() == QLatin1String("shadowColor")) {
+ parameter->setValue(QColor(shadowRgb, shadowRgb, shadowRgb));
+ } else if (parameter->name() == QLatin1String("shadowOffset")) {
+ parameter->setValue(QVector2D(glyphInfo.shadowOffsetX, glyphInfo.shadowOffsetY));
} else if (parameter->name() == QLatin1String("_qt_texture")) {
parameter->setValue(QVariant::fromValue(it.key()->texture));
} else if (parameter->name() == QLatin1String("fontScale")) {
diff --git a/src/runtime/q3dsscenemanager_p.h b/src/runtime/q3dsscenemanager_p.h
index d1a4461..5cdb806 100644
--- a/src/runtime/q3dsscenemanager_p.h
+++ b/src/runtime/q3dsscenemanager_p.h
@@ -456,6 +456,7 @@ class Q3DSTextAttached : public Q3DSNodeAttached
{
public:
bool distanceFieldText = false;
+ bool dropShadow = false;
Qt3DExtras::QPlaneMesh *mesh = nullptr;
Qt3DRender::QParameter *opacityParam = nullptr;
QVector<Qt3DRender::QParameter *> colorParam;
diff --git a/src/runtime/q3dstextmesh.cpp b/src/runtime/q3dstextmesh.cpp
index 13fc8dd..4290204 100644
--- a/src/runtime/q3dstextmesh.cpp
+++ b/src/runtime/q3dstextmesh.cpp
@@ -64,30 +64,34 @@ namespace {
class Q3DSTextMeshGeometry : public Qt3DRender::QGeometry
{
public:
- Q3DSTextMeshGeometry(const QVector<float> &vertexes, Qt3DCore::QNode *parent = nullptr);
+ Q3DSTextMeshGeometry(bool shadow,
+ const QVector<float> &vertexes,
+ Qt3DCore::QNode *parent = nullptr);
private:
Qt3DRender::QBuffer *m_vertexBuffer;
Qt3DRender::QBuffer *m_indexBuffer;
Qt3DRender::QAttribute *m_positionAttribute;
Qt3DRender::QAttribute *m_textureCoordinateAttribute;
Qt3DRender::QAttribute *m_indexAttribute;
+ Qt3DRender::QAttribute *m_textureBoundsAttribute;
};
- Q3DSTextMeshGeometry::Q3DSTextMeshGeometry(const QVector<float> &vertexes,
+ Q3DSTextMeshGeometry::Q3DSTextMeshGeometry(bool shadow,
+ const QVector<float> &vertexes,
Qt3DCore::QNode *parent)
: Qt3DRender::QGeometry(parent)
{
- Q_ASSERT(vertexes.size() % 10 == 0);
-
m_vertexBuffer = new Qt3DRender::QBuffer(this);
m_vertexBuffer->setUsage(Qt3DRender::QBuffer::StaticDraw);
m_vertexBuffer->setAccessType(Qt3DRender::QBuffer::Read);
m_vertexBuffer->setData(QByteArray(reinterpret_cast<const char *>(vertexes.constData()),
int(vertexes.size() * sizeof(float))));
- const uint stride = (3 + 2) * sizeof(float);
+ const uint floatsPerVertex = shadow ? 3 + 2 + 4 : 3 + 2;
+ const uint stride = floatsPerVertex * sizeof(float);
- const uint vertexCount = uint(vertexes.size() / 5);
+ Q_ASSERT(uint(vertexes.size()) % floatsPerVertex == 0);
+ const uint vertexCount = uint(vertexes.size()) / floatsPerVertex;
Q_ASSERT(vertexCount % 4 == 0);
const uint quadCount = vertexCount / 4;
@@ -125,6 +129,20 @@ namespace {
m_textureCoordinateAttribute->setByteOffset(3 * sizeof(float));
m_textureCoordinateAttribute->setCount(vertexCount);
+ if (shadow) {
+ m_textureBoundsAttribute = new Qt3DRender::QAttribute(this);
+ m_textureBoundsAttribute->setName(QStringLiteral("textureBounds"));
+ m_textureBoundsAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float);
+ m_textureBoundsAttribute->setVertexSize(4);
+ m_textureBoundsAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
+ m_textureBoundsAttribute->setBuffer(m_vertexBuffer);
+ m_textureBoundsAttribute->setByteStride(stride);
+ m_textureBoundsAttribute->setByteOffset(5 * sizeof(float));
+ m_textureBoundsAttribute->setCount(vertexCount);
+ } else {
+ m_textureBoundsAttribute = nullptr;
+ }
+
m_indexAttribute = new Qt3DRender::QAttribute(this);
m_indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
m_indexAttribute->setVertexBaseType(indexType);
@@ -134,6 +152,8 @@ namespace {
addAttribute(m_positionAttribute);
addAttribute(m_textureCoordinateAttribute);
addAttribute(m_indexAttribute);
+ if (shadow)
+ addAttribute(m_textureBoundsAttribute);
}
}
@@ -143,10 +163,10 @@ Q3DSTextMesh::Q3DSTextMesh(Qt3DCore::QNode *parent)
{
}
-void Q3DSTextMesh::setVertexes(const QVector<float> &vertexes)
+void Q3DSTextMesh::setVertexes(const QVector<float> &vertexes, bool dropShadow)
{
delete m_geometry;
- m_geometry = new Q3DSTextMeshGeometry(vertexes, this);
+ m_geometry = new Q3DSTextMeshGeometry(dropShadow, vertexes, this);
setGeometry(m_geometry);
}
diff --git a/src/runtime/q3dstextmesh_p.h b/src/runtime/q3dstextmesh_p.h
index 69b07a3..b68ecc9 100644
--- a/src/runtime/q3dstextmesh_p.h
+++ b/src/runtime/q3dstextmesh_p.h
@@ -56,7 +56,8 @@ public:
explicit Q3DSTextMesh(Qt3DCore::QNode *parent = nullptr);
~Q3DSTextMesh() override;
- void setVertexes(const QVector<float> &vertexes);
+ void setVertexes(const QVector<float> &vertexes,
+ bool dropShadow);
private:
Qt3DRender::QGeometry *m_geometry;
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow.frag b/src/runtime/shaders/distancefieldtext_dropshadow.frag
new file mode 100644
index 0000000..da51c5c
--- /dev/null
+++ b/src/runtime/shaders/distancefieldtext_dropshadow.frag
@@ -0,0 +1,29 @@
+varying highp vec2 sampleCoord;
+varying highp vec2 alphas;
+varying highp vec2 shadowSampleCoord;
+varying highp vec4 normalizedTextureBounds;
+
+uniform sampler2D _qt_texture;
+uniform highp vec4 color;
+uniform highp vec4 shadowColor;
+
+void main()
+{
+ highp float shadowAlpha = smoothstep(alphas.x,
+ alphas.y,
+ texture2D(_qt_texture,
+ clamp(shadowSampleCoord,
+ normalizedTextureBounds.xy,
+ normalizedTextureBounds.zw)).a);
+ highp vec4 shadowPixel = shadowColor * shadowAlpha;
+
+ highp float textAlpha = smoothstep(alphas.x,
+ alphas.y,
+ texture2D(_qt_texture,
+ clamp(sampleCoord,
+ normalizedTextureBounds.xy,
+ normalizedTextureBounds.zw)).a);
+ highp vec4 textPixel = color * textAlpha;
+
+ gl_FragColor = mix(shadowPixel, textPixel, textPixel.a);
+}
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow.vert b/src/runtime/shaders/distancefieldtext_dropshadow.vert
new file mode 100644
index 0000000..69fd211
--- /dev/null
+++ b/src/runtime/shaders/distancefieldtext_dropshadow.vert
@@ -0,0 +1,75 @@
+uniform highp mat4 mvp;
+uniform highp mat4 modelView;
+uniform highp float fontScale;
+uniform int textureWidth;
+uniform int textureHeight;
+uniform highp vec2 shadowOffset;
+
+attribute highp vec4 vCoord;
+attribute highp vec2 tCoord;
+attribute highp vec4 textureBounds;
+
+varying highp vec2 sampleCoord;
+varying highp vec2 shadowSampleCoord;
+varying highp vec2 alphas;
+varying highp vec4 normalizedTextureBounds;
+
+highp float thresholdFunc(highp float scale)
+{
+ highp float base = 0.5;
+ highp float baseDev = 0.065;
+ highp float devScaleMin = 0.15;
+ highp float devScaleMax = 0.3;
+ return base - ((clamp(scale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
+}
+
+highp float spreadFunc(highp float scale)
+{
+ return 0.06 / scale;
+}
+
+highp vec2 alphaRange(highp float scale)
+{
+ highp float base = thresholdFunc(scale);
+ highp float range = spreadFunc(scale);
+ highp float alphaMin = max(0.0, base - range);
+ highp float alphaMax = min(base + range, 1.0);
+ return highp vec2(alphaMin, alphaMax);
+}
+
+highp float determinantOfSubmatrix(highp mat4 m, int col0, int col1, int row0, int row1)
+{
+ return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0];
+}
+
+highp float determinantOfSubmatrix(highp mat4 m, int col0, int col1, int col2, int row0, int row1, int row2)
+{
+ highp float det = m[col0][row0] * determinantOfSubmatrix(m, col1, col2, row1, row2);
+ det -= m[col1][row0] * determinantOfSubmatrix(m, col0, col2, row1, row2);
+ det += m[col2][row0] * determinantOfSubmatrix(m, col0, col1, row1, row2);
+ return det;
+}
+
+highp float determinant(highp mat4 m)
+{
+ highp float det = m[0][0] * determinantOfSubmatrix(m, 1, 2, 3, 1, 2, 3);
+ det -= m[1][0] * determinantOfSubmatrix(m, 0, 2, 3, 1, 2, 3);
+ det += m[2][0] * determinantOfSubmatrix(m, 0, 1, 3, 1, 2, 3);
+ det -= m[3][0] * determinantOfSubmatrix(m, 0, 1, 2, 1, 2, 3);
+ return det;
+}
+
+void main()
+{
+ highp float scale = fontScale * sqrt(abs(determinant(modelView)));
+ alphas = alphaRange(scale);
+
+ highp vec2 textureSizeMultiplier = highp vec2(1.0 / highp float(textureWidth), 1.0 / highp float(textureHeight));
+
+ sampleCoord = tCoord * textureSizeMultiplier;
+ shadowSampleCoord = (tCoord - shadowOffset) * textureSizeMultiplier;
+ normalizedTextureBounds = highp vec4(textureBounds.xy * textureSizeMultiplier,
+ textureBounds.zw * textureSizeMultiplier);
+
+ gl_Position = mvp * vCoord;
+}
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow_core.frag b/src/runtime/shaders/distancefieldtext_dropshadow_core.frag
new file mode 100644
index 0000000..46a1194
--- /dev/null
+++ b/src/runtime/shaders/distancefieldtext_dropshadow_core.frag
@@ -0,0 +1,34 @@
+#version 150 core
+
+in vec2 sampleCoord;
+in vec2 shadowSampleCoord;
+in vec4 normalizedTextureBounds;
+
+out vec4 fragColor;
+
+uniform sampler2D _qt_texture;
+uniform vec4 color;
+uniform vec4 shadowColor;
+
+in vec2 alphas;
+
+void main()
+{
+ float shadowAlpha = smoothstep(alphas.x,
+ alphas.y,
+ texture(_qt_texture,
+ clamp(shadowSampleCoord,
+ normalizedTextureBounds.xy,
+ normalizedTextureBounds.zw)).r);
+ vec4 shadowPixel = shadowColor * shadowAlpha;
+
+ float textAlpha = smoothstep(alphas.x,
+ alphas.y,
+ texture(_qt_texture,
+ clamp(sampleCoord,
+ normalizedTextureBounds.xy,
+ normalizedTextureBounds.zw)).r);
+ vec4 textPixel = color * textAlpha;
+
+ fragColor = mix(shadowPixel, textPixel, textPixel.a);
+}
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow_core.vert b/src/runtime/shaders/distancefieldtext_dropshadow_core.vert
new file mode 100644
index 0000000..727dac5
--- /dev/null
+++ b/src/runtime/shaders/distancefieldtext_dropshadow_core.vert
@@ -0,0 +1,55 @@
+#version 150 core
+
+in vec4 vCoord;
+in vec2 tCoord;
+in vec4 textureBounds;
+
+out vec2 sampleCoord;
+out vec2 shadowSampleCoord;
+
+out vec2 alphas;
+out vec4 normalizedTextureBounds;
+
+uniform mat4 mvp;
+uniform mat4 modelView;
+uniform int textureWidth;
+uniform int textureHeight;
+uniform float fontScale;
+uniform vec2 shadowOffset;
+
+float thresholdFunc(float scale)
+{
+ float base = 0.5;
+ float baseDev = 0.065;
+ float devScaleMin = 0.15;
+ float devScaleMax = 0.3;
+ return base - ((clamp(scale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
+}
+
+float spreadFunc(float scale)
+{
+ return 0.06 / scale;
+}
+
+vec2 alphaRange(float scale)
+{
+ float base = thresholdFunc(scale);
+ float range = spreadFunc(scale);
+ float alphaMin = max(0.0, base - range);
+ float alphaMax = min(base + range, 1.0);
+ return vec2(alphaMin, alphaMax);
+}
+
+void main()
+{
+ float scale = fontScale * sqrt(abs(determinant(modelView)));
+ alphas = alphaRange(scale);
+
+ vec2 textureSizeMultiplier = vec2(1.0 / textureWidth, 1.0 / textureHeight);
+
+ sampleCoord = tCoord * textureSizeMultiplier;
+ shadowSampleCoord = (tCoord - shadowOffset) * textureSizeMultiplier;
+ normalizedTextureBounds = vec4(textureBounds.xy * textureSizeMultiplier,
+ textureBounds.zw * textureSizeMultiplier);
+ gl_Position = mvp * vCoord;
+}