From 1fe9f269b1abbf474ee5b2c81806c92c1a8d30e3 Mon Sep 17 00:00:00 2001 From: RuiwenTang Date: Sat, 14 Sep 2024 17:51:54 +0800 Subject: [PATCH] gl_engine: implement advance blending Implement some advance blending equation in GLEngine by using two RenderPass. * The first pass render the content into an off-screen framebuffer, also blit the screen content to an off-screen Texture. * The second pass renders the final blended color to the screen. Also use a stencil test to prevent non-drawn areas from participating in the color blending calculation. issue: https://github.com/thorvg/thorvg/issues/2435 --- src/renderer/gl_engine/tvgGlRenderPass.h | 4 + src/renderer/gl_engine/tvgGlRenderTask.cpp | 77 +++++++++ src/renderer/gl_engine/tvgGlRenderTask.h | 28 ++++ src/renderer/gl_engine/tvgGlRenderer.cpp | 184 ++++++++++++++++++++- src/renderer/gl_engine/tvgGlRenderer.h | 16 ++ src/renderer/gl_engine/tvgGlShader.cpp | 2 +- src/renderer/gl_engine/tvgGlShaderSrc.cpp | 137 ++++++++++++++- src/renderer/gl_engine/tvgGlShaderSrc.h | 9 + 8 files changed, 448 insertions(+), 9 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlRenderPass.h b/src/renderer/gl_engine/tvgGlRenderPass.h index a0fefc17..d5df3854 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.h +++ b/src/renderer/gl_engine/tvgGlRenderPass.h @@ -74,6 +74,10 @@ public: } int nextDrawDepth() { return ++mDrawDepth; } + + void setDrawDepth(int32_t depth) { mDrawDepth = depth; } + + GlRenderTarget* getFbo() { return mFbo; } private: GlRenderTarget* mFbo; Array mTasks = {}; diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 816ae840..a67f3dc8 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -308,3 +308,80 @@ void GlClipTask::normalizeDrawDepth(int32_t maxDepth) mClipTask->normalizeDrawDepth(maxDepth); mMaskTask->normalizeDrawDepth(maxDepth); } + +GlSimpleBlendTask::GlSimpleBlendTask(BlendMethod method, GlProgram* program) + : GlRenderTask(program), mBlendMethod(method) {} + +void GlSimpleBlendTask::run() +{ + if (mBlendMethod == BlendMethod::Add) glBlendFunc(GL_ONE, GL_ONE); + else if (mBlendMethod == BlendMethod::Darken) { + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_MIN); + } else if (mBlendMethod == BlendMethod::Lighten) { + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_MAX); + } + else glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + GlRenderTask::run(); + + if (mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) glBlendEquation(GL_FUNC_ADD); + + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +} + +GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask) + : GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask) {} + +GlComplexBlendTask::~GlComplexBlendTask() +{ + delete mStencilTask; + delete mComposeTask; +} + +void GlComplexBlendTask::run() +{ + mComposeTask->run(); + + // copy the current fbo to the dstCopyFbo + GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); + GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId())); + + GL_CHECK(glViewport(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h)); + GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h)); + + const auto& vp = getViewport(); + + GL_CHECK(glBlitFramebuffer(vp.x, vp.y, vp.x + vp.w, vp.y + vp.h, 0, 0, vp.w, vp.h, GL_COLOR_BUFFER_BIT, GL_LINEAR)); + + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId())); + + GL_CHECK(glEnable(GL_STENCIL_TEST)); + GL_CHECK(glColorMask(0, 0, 0, 0)); + GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x0, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); + + GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x0, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); + + + mStencilTask->run(); + + GL_CHECK(glColorMask(1, 1, 1, 1)); + GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); + GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); + + GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO)); + + GlRenderTask::run(); + + GL_CHECK(glDisable(GL_STENCIL_TEST)); + GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); +} + +void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth) +{ + mStencilTask->normalizeDrawDepth(maxDepth); + GlRenderTask::normalizeDrawDepth(maxDepth); +} diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index 46b87560..774b6d7d 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -91,6 +91,7 @@ public: GlProgram* getProgram() { return mProgram; } const RenderRegion& getViewport() const { return mViewport; } + float getDrawDepth() const { return mDrawDepth; } private: GlProgram* mProgram; RenderRegion mViewport = {}; @@ -194,4 +195,31 @@ private: GlRenderTask* mMaskTask; }; +class GlSimpleBlendTask : public GlRenderTask +{ +public: + GlSimpleBlendTask(BlendMethod method, GlProgram* program); + ~GlSimpleBlendTask() override = default; + + void run() override; +private: + BlendMethod mBlendMethod; +}; + +class GlComplexBlendTask: public GlRenderTask +{ +public: + GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask); + ~GlComplexBlendTask() override; + + void run() override; + + void normalizeDrawDepth(int32_t maxDepth) override; +private: + GlRenderTarget* mDstFbo; + GlRenderTarget* mDstCopyFbo; + GlRenderTask* mStencilTask; + GlComposeTask* mComposeTask; +}; + #endif /* _TVG_GL_RENDER_TASK_H_ */ diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 19227e72..f8e4b48f 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -66,6 +66,10 @@ GlRenderer::~GlRenderer() if (mComposePool[i]) delete mComposePool[i]; } + for (uint32_t i = 0; i < mBlendPool.count; i++) { + if (mBlendPool[i]) delete mBlendPool[i]; + } + --rendererCnt; if (rendererCnt == 0 && initEngineCnt == 0) _termEngine(); @@ -99,22 +103,43 @@ void GlRenderer::initShaders() mPrograms.push_back(make_unique(GlShader::gen(STENCIL_VERT_SHADER, STENCIL_FRAG_SHADER))); // blit Renderer mPrograms.push_back(make_unique(GlShader::gen(BLIT_VERT_SHADER, BLIT_FRAG_SHADER))); + + // complex blending Renderer + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, MULTIPLY_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, SCREEN_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, OVERLAY_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, COLOR_DODGE_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, COLOR_BURN_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, HARD_LIGHT_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, SOFT_LIGHT_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, DIFFERENCE_BLEND_FRAG))); + mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, EXCLUSION_BLEND_FRAG))); } void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, RenderUpdateFlag flag, int32_t depth) { - const auto& vp = currentPass()->getViewport(); + auto vp = currentPass()->getViewport(); auto bbox = sdata.geometry->getViewport(); bbox.intersect(vp); + bool complexBlend = beginComplexBlending(bbox, sdata.geometry->getBounds()); + + if (complexBlend) { + vp = currentPass()->getViewport(); + bbox.intersect(vp); + } + auto x = bbox.x - vp.x; auto y = bbox.y - vp.y; auto w = bbox.w; auto h = bbox.h; - auto task = new GlRenderTask(mPrograms[RT_Color].get()); + GlRenderTask* task = nullptr; + if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Color].get()); + else task = new GlRenderTask(mPrograms[RT_Color].get()); + task->setDrawDepth(depth); if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) { @@ -197,12 +222,19 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, } else { currentPass()->addRenderTask(task); } + + if (complexBlend) { + auto task = new GlRenderTask(mPrograms[RT_Stencil].get()); + sdata.geometry->draw(task, mGpuBuffer.get(), flag); + + endBlendingCompose(task, sdata.geometry->getTransformMatrix()); + } } void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag, int32_t depth) { - const auto& vp = currentPass()->getViewport(); + auto vp = currentPass()->getViewport(); auto bbox = sdata.geometry->getViewport(); bbox.intersect(vp); @@ -229,6 +261,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla return; } + bool complexBlend = beginComplexBlending(bbox, sdata.geometry->getBounds()); + + if (complexBlend) vp = currentPass()->getViewport(); + auto x = bbox.x - vp.x; auto y = bbox.y - vp.y; @@ -397,6 +433,13 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla } else { currentPass()->addRenderTask(task); } + + if (complexBlend) { + auto task = new GlRenderTask(mPrograms[RT_Stencil].get()); + sdata.geometry->draw(task, mGpuBuffer.get(), flag); + + endBlendingCompose(task, sdata.geometry->getTransformMatrix()); + } } @@ -512,6 +555,117 @@ GlRenderPass* GlRenderer::currentPass() return &mRenderPassStack.back(); } +bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds) +{ + if (vp.w == 0 || vp.h == 0) return false; + + bounds.intersect(vp); + + if (bounds.w == 0 || bounds.h == 0) return false; + + if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false; + + if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); + + auto blendFbo = mBlendPool[0]->getRenderTarget(bounds); + + mRenderPassStack.emplace_back(GlRenderPass{blendFbo}); + + return true; +} + +void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix) +{ + auto blendPass = std::move(mRenderPassStack.back()); + mRenderPassStack.pop_back(); + + blendPass.setDrawDepth(currentPass()->nextDrawDepth()); + + auto composeTask = blendPass.endRenderPass(nullptr, currentPass()->getFboId()); + + const auto& vp = blendPass.getViewport(); + if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); + auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp); + + { + const auto& passVp = currentPass()->getViewport(); + + auto x = vp.x; + auto y = vp.y; + auto w = vp.w; + auto h = vp.h; + + stencilTask->setViewport(RenderRegion{ + x, + passVp.h - y - h, + w, + h, + }); + } + + stencilTask->setDrawDepth(currentPass()->nextDrawDepth()); + + { + // set view matrix + uint32_t loc = stencilTask->getProgram()->getUniformBlockIndex("Matrix"); + + float matrix44[16]; + currentPass()->getMatrix(matrix44, matrix); + uint32_t viewOffset = mGpuBuffer->push(matrix44, 16 * sizeof(float), true); + stencilTask->addBindResource(GlBindingResource{ + 0, + loc, + mGpuBuffer->getBufferId(), + viewOffset, + 16 * sizeof(float), + }); + } + + auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask); + + prepareCmpTask(task, vp, blendPass.getFboWidth(), blendPass.getFboHeight()); + + task->setDrawDepth(currentPass()->nextDrawDepth()); + + // src and dst texture + { + uint32_t loc = task->getProgram()->getUniformLocation("uSrcTexture"); + task->addBindResource(GlBindingResource{1, blendPass.getFbo()->getColorTexture(), loc}); + } + { + uint32_t loc = task->getProgram()->getUniformLocation("uDstTexture"); + task->addBindResource(GlBindingResource{2, dstCopyFbo->getColorTexture(), loc}); + } + + currentPass()->addRenderTask(task); +} + +GlProgram* GlRenderer::getBlendProgram() +{ + switch (mBlendMethod) { + case BlendMethod::Multiply: + return mPrograms[RT_MultiplyBlend].get(); + case BlendMethod::Screen: + return mPrograms[RT_ScreenBlend].get(); + case BlendMethod::Overlay: + return mPrograms[RT_OverlayBlend].get(); + case BlendMethod::ColorDodge: + return mPrograms[RT_ColorDodgeBlend].get(); + case BlendMethod::ColorBurn: + return mPrograms[RT_ColorBurnBlend].get(); + case BlendMethod::HardLight: + return mPrograms[RT_HardLightBlend].get(); + case BlendMethod::SoftLight: + return mPrograms[RT_SoftLightBlend].get(); + case BlendMethod::Difference: + return mPrograms[RT_DifferenceBlend].get(); + case BlendMethod::Exclusion: + return mPrograms[RT_ExclusionBlend].get(); + default: + return nullptr; + } +} + void GlRenderer::prepareBlitTask(GlBlitTask* task) { @@ -764,8 +918,10 @@ bool GlRenderer::target(int32_t id, uint32_t w, uint32_t h) mComposeStack.clear(); for (uint32_t i = 0; i < mComposePool.count; i++) delete mComposePool[i]; + for (uint32_t i = 0; i < mBlendPool.count; i++) delete mBlendPool[i]; mComposePool.clear(); + mBlendPool.clear(); return true; } @@ -912,10 +1068,13 @@ const Surface* GlRenderer::mainSurface() } -bool GlRenderer::blend(TVG_UNUSED BlendMethod method) +bool GlRenderer::blend(BlendMethod method) { - //TODO: - return false; + if (method == mBlendMethod) return true; + + mBlendMethod = method; + + return true; } @@ -928,7 +1087,7 @@ bool GlRenderer::renderImage(void* data) if (currentPass()->isEmpty()) return true; if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return true; - const auto& vp = currentPass()->getViewport(); + auto vp = currentPass()->getViewport(); auto bbox = sdata->geometry->getViewport(); @@ -952,6 +1111,10 @@ bool GlRenderer::renderImage(void* data) return true; } + bool complexBlend = beginComplexBlending(bbox, sdata->geometry->getBounds()); + + if (complexBlend) vp = currentPass()->getViewport(); + // matrix buffer { const auto& matrix = sdata->geometry->getTransformMatrix(); @@ -998,6 +1161,13 @@ bool GlRenderer::renderImage(void* data) currentPass()->addRenderTask(task); + if (complexBlend) { + auto task = new GlRenderTask(mPrograms[RT_Stencil].get()); + sdata->geometry->draw(task, mGpuBuffer.get(), RenderUpdateFlag::Image); + + endBlendingCompose(task, sdata->geometry->getTransformMatrix()); + } + return true; } diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index b6208bb0..6e94a663 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -49,6 +49,15 @@ public: RT_MaskDifference, RT_Stencil, RT_Blit, + RT_MultiplyBlend, + RT_ScreenBlend, + RT_OverlayBlend, + RT_ColorDodgeBlend, + RT_ColorBurnBlend, + RT_HardLightBlend, + RT_SoftLightBlend, + RT_DifferenceBlend, + RT_ExclusionBlend, RT_None, }; @@ -91,6 +100,10 @@ private: GlRenderPass* currentPass(); + bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds); + void endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix); + GlProgram* getBlendProgram(); + void prepareBlitTask(GlBlitTask* task); void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight); void endRenderPass(Compositor* cmp); @@ -105,6 +118,7 @@ private: vector> mPrograms; unique_ptr mRootTarget = {}; Array mComposePool = {}; + Array mBlendPool = {}; vector mRenderPassStack = {}; vector> mComposeStack = {}; @@ -115,6 +129,8 @@ private: } mDisposed; bool mClearBuffer = true; + + BlendMethod mBlendMethod = BlendMethod::Normal; }; #endif /* _TVG_GL_RENDERER_H_ */ diff --git a/src/renderer/gl_engine/tvgGlShader.cpp b/src/renderer/gl_engine/tvgGlShader.cpp index 4aef1f73..99f63f0a 100644 --- a/src/renderer/gl_engine/tvgGlShader.cpp +++ b/src/renderer/gl_engine/tvgGlShader.cpp @@ -79,7 +79,7 @@ uint32_t GlShader::compileShader(uint32_t type, char* shaderSrc) #else shaderPack[0] ="#version 330 core\n"; #endif - shaderPack[1] = "precision highp float;\n precision mediump int;\n"; + shaderPack[1] = "precision highp float;\n precision highp int;\n"; shaderPack[2] = shaderSrc; // Load the shader source diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.cpp b/src/renderer/gl_engine/tvgGlShaderSrc.cpp index 2343de3b..9eb1e5a4 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.cpp +++ b/src/renderer/gl_engine/tvgGlShaderSrc.cpp @@ -198,7 +198,7 @@ void main() \n vec2 ba = ed - st; \n \n - float d = abs(dot(pos - st, ba) / dot(ba, ba)); \n + float d = dot(pos - st, ba) / dot(ba, ba); \n \n float t = gradientWrap(d); \n \n @@ -509,3 +509,138 @@ const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER( FragColor = texture(uSrcTexture, vUV); \n } ); + +#define COMPLEX_BLEND_HEADER R"( \ + uniform sampler2D uSrcTexture; \ + uniform sampler2D uDstTexture; \ + \ + in vec2 vUV; \ + out vec4 FragColor; \ +)" + +const char* MULTIPLY_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + FragColor = srcColor * dstColor; + } +)"; + +#define SCREEN_BLEND_FUNC R"( \ + vec4 screenBlend(vec4 srcColor, vec4 dstColor) { \ + return dstColor + srcColor - (dstColor * srcColor); \ + } \ +)" + +#define HARD_LIGHT_BLEND_FUNC R"( \ + vec4 hardLightBlend(vec4 srcColor, vec4 dstColor) { \ + return vec4(srcColor.r < 0.5 ? 2.0 * srcColor.r * dstColor.r : 1.0 - 2.0 * (1.0 - srcColor.r) * (1.0 - dstColor.r), \ + srcColor.g < 0.5 ? 2.0 * srcColor.g * dstColor.g : 1.0 - 2.0 * (1.0 - srcColor.g) * (1.0 - dstColor.g), \ + srcColor.b < 0.5 ? 2.0 * srcColor.b * dstColor.b : 1.0 - 2.0 * (1.0 - srcColor.b) * (1.0 - dstColor.b), \ + 1.0); \ + } \ +)" + +#define SOFT_LIGHT_BLEND_FUNC R"( \ + float softLightD(float v) { \ + if (v <= 0.25) return ((16.0 * v - 12.0) * v + 4.0) * v; \ + else return sqrt(v); \ + } \ +)" + +const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER SCREEN_BLEND_FUNC R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + FragColor = screenBlend(srcColor, dstColor); + } +)"; + +const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + FragColor = hardLightBlend(dstColor, srcColor); + } +)"; + +const char* COLOR_DODGE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + float opacity = srcColor.a; + + srcColor *= 255.0; + dstColor *= 255.0; + vec4 color = vec4( + 255.0 - srcColor.r > 0.0 ? dstColor.r / (255.0 - srcColor.r) : dstColor.r, + 255.0 - srcColor.g > 0.0 ? dstColor.g / (255.0 - srcColor.g) : dstColor.g, + 255.0 - srcColor.b > 0.0 ? dstColor.b / (255.0 - srcColor.b) : dstColor.b, + 255.0 - srcColor.a > 0.0 ? dstColor.a / (255.0 - srcColor.a) : dstColor.a + ); + + FragColor = vec4(color.rgb, 255.0) * opacity / 255.0; + } +)"; + +const char* COLOR_BURN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + float opacity = srcColor.a; + + if (srcColor.a > 0.0) srcColor.rgb /= srcColor.a; + if (dstColor.a > 0.0) dstColor.rgb /= dstColor.a; + vec4 id = vec4(1.0) - dstColor; + vec4 color = vec4( + srcColor.r > 0.0 ? (255.0 - (255.0 - dstColor.r * 255.0) / (srcColor.r * 255.0)) / 255.0 : (1.0 - dstColor.r), + srcColor.g > 0.0 ? (255.0 - (255.0 - dstColor.g * 255.0) / (srcColor.g * 255.0)) / 255.0 : (1.0 - dstColor.g), + srcColor.b > 0.0 ? (255.0 - (255.0 - dstColor.b * 255.0) / (srcColor.b * 255.0)) / 255.0 : (1.0 - dstColor.b), + srcColor.a > 0.0 ? (255.0 - (255.0 - dstColor.a * 255.0) / (srcColor.a * 255.0)) / 255.0 : (1.0 - dstColor.a) + ); + + FragColor = color * srcColor.a; + } +)"; + +const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + FragColor = hardLightBlend(srcColor, dstColor); + } +)"; + +const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER SOFT_LIGHT_BLEND_FUNC R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + FragColor = vec4( + srcColor.r <= 0.5 ? dstColor.r - (1.0 - 2.0 * srcColor.r) * dstColor.r * (1.0 - dstColor.r) : dstColor.r + (2.0 * srcColor.r - 1.0) * (softLightD(dstColor.r) - dstColor.r), + srcColor.g <= 0.5 ? dstColor.g - (1.0 - 2.0 * srcColor.g) * dstColor.g * (1.0 - dstColor.g) : dstColor.g + (2.0 * srcColor.g - 1.0) * (softLightD(dstColor.g) - dstColor.g), + srcColor.b <= 0.5 ? dstColor.b - (1.0 - 2.0 * srcColor.b) * dstColor.b * (1.0 - dstColor.b) : dstColor.b + (2.0 * srcColor.b - 1.0) * (softLightD(dstColor.b) - dstColor.b), + 1.0 + ); + } +)"; + +const char* DIFFERENCE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + FragColor = abs(dstColor - srcColor); + } +)"; + +const char* EXCLUSION_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + FragColor = dstColor + srcColor - (2.0 * dstColor * srcColor); + } +)"; diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.h b/src/renderer/gl_engine/tvgGlShaderSrc.h index 94859027..353f9822 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.h +++ b/src/renderer/gl_engine/tvgGlShaderSrc.h @@ -43,5 +43,14 @@ extern const char* STENCIL_VERT_SHADER; extern const char* STENCIL_FRAG_SHADER; extern const char* BLIT_VERT_SHADER; extern const char* BLIT_FRAG_SHADER; +extern const char* MULTIPLY_BLEND_FRAG; +extern const char* SCREEN_BLEND_FRAG; +extern const char* OVERLAY_BLEND_FRAG; +extern const char* COLOR_DODGE_BLEND_FRAG; +extern const char* COLOR_BURN_BLEND_FRAG; +extern const char* HARD_LIGHT_BLEND_FRAG; +extern const char* SOFT_LIGHT_BLEND_FRAG; +extern const char* DIFFERENCE_BLEND_FRAG; +extern const char* EXCLUSION_BLEND_FRAG; #endif /* _TVG_GL_SHADERSRC_H_ */