From 6c3fee84ecaac76be8540563d671c6b00df148f1 Mon Sep 17 00:00:00 2001 From: Sergii Liebodkin Date: Thu, 10 Apr 2025 14:20:01 +0000 Subject: [PATCH] gl_engine: introduce dropshadow effect for opengl renderer issue: https://github.com/thorvg/thorvg/issues/3054 --- src/renderer/gl_engine/tvgGlCommon.h | 5 ++ src/renderer/gl_engine/tvgGlRenderTask.cpp | 54 +++++++++++++++ src/renderer/gl_engine/tvgGlRenderTask.h | 18 +++++ src/renderer/gl_engine/tvgGlRenderer.cpp | 76 +++++++++++++++++++++- src/renderer/gl_engine/tvgGlRenderer.h | 4 +- src/renderer/gl_engine/tvgGlShaderSrc.cpp | 26 ++++++++ src/renderer/gl_engine/tvgGlShaderSrc.h | 1 + 7 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index dab396cd..c00b8cec 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -208,6 +208,11 @@ struct GlGaussianBlur { float extend{}; }; +struct GlDropShadow: GlGaussianBlur { + float color[4]; + float offset[2]; +}; + struct GlEffectParams { // fill: [0..3]: color // tint: [0..2]: black, [4..6]: white, [8]: intensity diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 4d6c1250..1ec8bf81 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -435,6 +435,60 @@ void GlGaussianBlurTask::run() GL_CHECK(glEnable(GL_BLEND)); } + +void GlEffectDropShadowTask::run() +{ + const auto vp = getViewport(); + const auto width = mDstFbo->getWidth(); + const auto height = mDstFbo->getHeight(); + + // get targets handles + GLuint dstCopyTexId0 = mDstCopyFbo0->getColorTexture(); + GLuint dstCopyTexId1 = mDstCopyFbo1->getColorTexture(); + // get programs properties + GlProgram* programHorz = horzTask->getProgram(); + GlProgram* programVert = vertTask->getProgram(); + GLint horzSrcTextureLoc = programHorz->getUniformLocation("uSrcTexture"); + GLint vertSrcTextureLoc = programVert->getUniformLocation("uSrcTexture"); + + GLint srcTextureLoc = getProgram()->getUniformLocation("uSrcTexture"); + GLint blrTextureLoc = getProgram()->getUniformLocation("uBlrTexture"); + addBindResource({ 0, dstCopyTexId0, srcTextureLoc }); + addBindResource({ 1, dstCopyTexId1, blrTextureLoc }); + + GL_CHECK(glViewport(0, 0, width, height)); + GL_CHECK(glScissor(0, 0, width, height)); + + // we need to make a full copy of dst to intermediate buffers to be sure that they don’t contain prev data. + GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); + GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo0->getResolveFboId())); + GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); + GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo1->getResolveFboId())); + GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + + GL_CHECK(glDisable(GL_BLEND)); + // horizontal blur + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstCopyFbo0->getResolveFboId())); + horzTask->setViewport(vp); + horzTask->addBindResource({ 0, dstCopyTexId1, horzSrcTextureLoc }); + horzTask->run(); + // vertical blur + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstCopyFbo1->getResolveFboId())); + vertTask->setViewport(vp); + vertTask->addBindResource({ 0, dstCopyTexId0, vertSrcTextureLoc }); + vertTask->run(); + // copy original image to intermediate buffer + GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); + GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo0->getResolveFboId())); + GL_CHECK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + // run drop shadow effect + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId())); + GlRenderTask::run(); + GL_CHECK(glEnable(GL_BLEND)); +} + + void GlEffectColorTransformTask::run() { const auto width = mDstFbo->getWidth(); diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index 9edfc314..d6fa93f2 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -237,6 +237,24 @@ private: GlRenderTarget* mDstCopyFbo1; }; +class GlEffectDropShadowTask: public GlRenderTask +{ +public: + GlEffectDropShadowTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo0, GlRenderTarget* dstCopyFbo1): + GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo0(dstCopyFbo0), mDstCopyFbo1(dstCopyFbo1) {}; + ~GlEffectDropShadowTask(){ delete horzTask; delete vertTask; }; + + void run() override; + + GlRenderTask* horzTask; + GlRenderTask* vertTask; + RenderEffectDropShadow* effect; +private: + GlRenderTarget* mDstFbo; + GlRenderTarget* mDstCopyFbo0; + GlRenderTarget* mDstCopyFbo1; +}; + class GlEffectColorTransformTask: public GlRenderTask { public: diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 0eb3a64e..c15ae64e 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -144,6 +144,7 @@ void GlRenderer::initShaders() // effects mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL)); mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL)); + mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_DROPSHADOW)); mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_FILL)); mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_TINT)); mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_TRITONE)); @@ -964,6 +965,32 @@ void GlRenderer::effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, cons } +void GlRenderer::effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform) +{ + GlDropShadow* dropShadow = (GlDropShadow*)effect->rd; + if (!dropShadow) dropShadow = tvg::malloc(sizeof(GlDropShadow)); + const float sigma = effect->sigma; + const float scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); + const float radian = tvg::deg2rad(90.0f - effect->angle); + const Point offset = { + -effect->distance * cosf(radian) * scale, + -effect->distance * sinf(radian) * scale + }; + dropShadow->sigma = sigma; + dropShadow->scale = scale; + dropShadow->level = int(GL_GAUSSIAN_MAX_LEVEL * ((effect->quality - 1) * 0.01f)) + 1; + dropShadow->color[0] = effect->color[0] / 255.0f; + dropShadow->color[1] = effect->color[1] / 255.0f; + dropShadow->color[2] = effect->color[2] / 255.0f; + dropShadow->color[3] = effect->color[3] / 255.0f; + dropShadow->offset[0] = offset.x; + dropShadow->offset[1] = offset.y; + dropShadow->extend = 2 * std::max(sigma * scale + std::abs(offset.x), sigma * scale + std::abs(offset.y)); + effect->rd = dropShadow; + effect->valid = true; +} + + void GlRenderer::effectFillUpdate(RenderEffectFill* effect, const Matrix& transform) { auto params = (GlEffectParams*)effect->rd; @@ -1031,6 +1058,17 @@ bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect) }; +bool GlRenderer::effectDropShadowRegion(RenderEffectDropShadow* effect) +{ + auto gaussianBlur = (GlDropShadow*)effect->rd; + effect->extend.x = -gaussianBlur->extend; + effect->extend.w = +gaussianBlur->extend * 2; + effect->extend.y = -gaussianBlur->extend; + effect->extend.h = +gaussianBlur->extend * 2; + return true; +}; + + void GlRenderer::prepare(RenderEffect* effect, const Matrix& transform) { // we must be sure, that we have intermidiate FBOs @@ -1039,6 +1077,7 @@ void GlRenderer::prepare(RenderEffect* effect, const Matrix& transform) switch (effect->type) { case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast(effect), transform); break; + case SceneEffect::DropShadow : effectDropShadowUpdate(static_cast(effect), transform); break; case SceneEffect::Fill: effectFillUpdate(static_cast(effect), transform); break; case SceneEffect::Tint: effectTintUpdate(static_cast(effect), transform); break; case SceneEffect::Tritone: effectTritoneUpdate(static_cast(effect), transform); break; @@ -1052,6 +1091,7 @@ bool GlRenderer::region(RenderEffect* effect) { switch (effect->type) { case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast(effect)); + case SceneEffect::DropShadow : return effectDropShadowRegion(static_cast(effect)); case SceneEffect::Fill: case SceneEffect::Tint: case SceneEffect::Tritone: return true; @@ -1074,6 +1114,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef auto voffset = mGpuBuffer.push((void*)vdata, sizeof(vdata)); auto ioffset = mGpuBuffer.pushIndex((void*)idata, sizeof(idata)); + // effect gaussian blur if (effect->type == SceneEffect::GaussianBlur) { // get gaussian programs GlProgram* programHorz = mPrograms[RT_GaussianHorz]; @@ -1102,7 +1143,40 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef gaussianTask->vertTask->setDrawRange(ioffset, 6); // add task to render pipeline pass->addRenderTask(gaussianTask); - } // effect fill + } // effect drop shadow + else if (effect->type == SceneEffect::DropShadow) { + // get programs + GlProgram* program = mPrograms[RT_DropShadow]; + GlProgram* programHorz = mPrograms[RT_GaussianHorz]; + GlProgram* programVert = mPrograms[RT_GaussianVert]; + // get current and intermidiate framebuffers + auto dstFbo = pass->getFbo(); + auto dstCopyFbo0 = mBlendPool[0]->getRenderTarget(vp); + auto dstCopyFbo1 = mBlendPool[1]->getRenderTarget(vp); + // add uniform data + GlDropShadow* params = (GlDropShadow*)(effect->rd); + auto paramsOffset = mGpuBuffer.push(params, sizeof(GlDropShadow), true); + + // create gaussian blur tasks + auto task = new GlEffectDropShadowTask(program, dstFbo, dstCopyFbo0, dstCopyFbo1); + task->effect = (RenderEffectDropShadow*)effect; + task->setViewport({0, 0, vp.w, vp.h}); + task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("DropShadow"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlDropShadow)}); + task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->setDrawRange(ioffset, 6); + // horizontal blur task and geometry + task->horzTask = new GlRenderTask(programHorz); + task->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); + task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->horzTask->setDrawRange(ioffset, 6); + // vertical blur task and geometry + task->vertTask = new GlRenderTask(programVert); + task->vertTask->addBindResource(GlBindingResource{0, programVert->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); + task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->vertTask->setDrawRange(ioffset, 6); + // add task to render pipeline + pass->addRenderTask(task); + } // effect fill, tint, tritone else if ((effect->type == SceneEffect::Fill) || (effect->type == SceneEffect::Tint) || (effect->type == SceneEffect::Tritone)) { GlProgram* program{}; if (effect->type == SceneEffect::Fill) program = mPrograms[RT_EffectFill]; diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index c736740c..7ec1650d 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -62,7 +62,7 @@ public: RT_GaussianVert, RT_GaussianHorz, - //RT_DropShadow, + RT_DropShadow, RT_EffectFill, RT_EffectTint, RT_EffectTritone, @@ -124,11 +124,13 @@ private: void endRenderPass(RenderCompositor* cmp); void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform); + void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform); void effectFillUpdate(RenderEffectFill* effect, const Matrix& transform); void effectTintUpdate(RenderEffectTint* effect, const Matrix& transform); void effectTritoneUpdate(RenderEffectTritone* effect, const Matrix& transform); bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect); + bool effectDropShadowRegion(RenderEffectDropShadow* effect); void flush(); void clearDisposes(); diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.cpp b/src/renderer/gl_engine/tvgGlShaderSrc.cpp index ef78e58b..8243a058 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.cpp +++ b/src/renderer/gl_engine/tvgGlShaderSrc.cpp @@ -821,6 +821,32 @@ void main() } )"; +const char* EFFECT_DROPSHADOW = R"( +uniform sampler2D uSrcTexture; +uniform sampler2D uBlrTexture; +layout(std140) uniform DropShadow { + int level; + float sigma; + float scale; + float extend; + vec4 color; + vec2 offset; +} uDropShadow; + +in vec2 vUV; +out vec4 FragColor; + +void main() +{ + vec2 texelSize = 1.0 / vec2(textureSize(uSrcTexture, 0)); + vec2 offset = uDropShadow.offset * texelSize; + vec4 orig = texture(uSrcTexture, vUV); + vec4 blur = texture(uBlrTexture, vUV + offset); + vec4 shad = uDropShadow.color * blur.a; + FragColor = orig + shad * (1.0 - orig.a); +} +)"; + const char* EFFECT_FILL = R"( uniform sampler2D uSrcTexture; layout(std140) uniform Params { diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.h b/src/renderer/gl_engine/tvgGlShaderSrc.h index 1bf5ce67..94716751 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.h +++ b/src/renderer/gl_engine/tvgGlShaderSrc.h @@ -57,6 +57,7 @@ extern const char* EXCLUSION_BLEND_FRAG; extern const char* EFFECT_VERTEX; extern const char* GAUSSIAN_VERTICAL; extern const char* GAUSSIAN_HORIZONTAL; +extern const char* EFFECT_DROPSHADOW; extern const char* EFFECT_FILL; extern const char* EFFECT_TINT; extern const char* EFFECT_TRITONE;