gl_engine: rewrite the blending shader

Completely rewrite the GL backend shader to support advance blending and
correct the blending behavior.
Now the GL backend has the same blending result as SKIA and CanvasRenderingContext2D in browers did.

The formula is referenced from:
https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators

issue: https://github.com/thorvg/thorvg/issues/2799
This commit is contained in:
Hermet Park 2025-05-12 13:27:14 +09:00
parent 3fbb55440a
commit 3c884232fd
13 changed files with 256 additions and 150 deletions

View file

@ -165,7 +165,7 @@ struct GlRadialGradientBlock
struct GlCompositor : RenderCompositor
{
RenderRegion bbox = {};
BlendMethod blend = BlendMethod::Normal;
GlCompositor(const RenderRegion& box) : bbox(box) {}
};

View file

@ -101,13 +101,17 @@ int32_t GlProgram::getAttributeLocation(const char* name)
int32_t GlProgram::getUniformLocation(const char* name)
{
if (mUniformLocation.count(name)) return mUniformLocation[name];
GL_CHECK(int32_t location = glGetUniformLocation(mProgramObj, name));
mUniformLocation[name] = location;
return location;
}
int32_t GlProgram::getUniformBlockIndex(const char* name)
{
if (mUniformBlock.count(name)) return mUniformBlock[name];
GL_CHECK(int32_t index = glGetUniformBlockIndex(mProgramObj, name));
mUniformBlock[name] = index;
return index;
}

View file

@ -25,6 +25,9 @@
#include "tvgGlShader.h"
#include <unordered_map>
#include <string>
class GlProgram
{
public:
@ -49,6 +52,8 @@ public:
private:
uint32_t mProgramObj;
std::unordered_map<std::string, int32_t> mUniformLocation;
std::unordered_map<std::string, int32_t> mUniformBlock;
static uint32_t mCurrentProgram;
};

View file

@ -25,9 +25,9 @@
#include "tvgGlRenderPass.h"
#include "tvgGlRenderTask.h"
GlRenderPass::GlRenderPass(GlRenderTarget* fbo): mFbo(fbo), mTasks(), mDrawDepth(0) {}
GlRenderPass::GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport): mFbo(fbo), mViewport(viewport), mTasks(), mDrawDepth(0) {}
GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mTasks(), mDrawDepth(0)
GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mViewport(other.mViewport), mTasks(), mDrawDepth(0)
{
mTasks.push(other.mTasks);

View file

@ -32,7 +32,7 @@ class GlProgram;
class GlRenderPass
{
public:
GlRenderPass(GlRenderTarget* fbo);
GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport);
GlRenderPass(GlRenderPass&& other);
~GlRenderPass();
@ -45,7 +45,7 @@ public:
GLuint getTextureId() { return mFbo->getColorTexture(); }
const RenderRegion& getViewport() const { return mFbo->getViewport(); }
const RenderRegion& getViewport() const { return mViewport; }
uint32_t getFboWidth() const { return mFbo->getWidth(); }
@ -63,7 +63,7 @@ public:
auto task = new T(program, targetFbo, mFbo, std::move(mTasks));
const auto& vp = mFbo->getViewport();
const auto& vp = mViewport;
task->setRenderSize(static_cast<uint32_t>(vp.w), static_cast<uint32_t>(vp.h));
@ -77,6 +77,7 @@ public:
GlRenderTarget* getFbo() { return mFbo; }
private:
GlRenderTarget* mFbo;
RenderRegion mViewport;
Array<GlRenderTask*> mTasks = {};
int32_t mDrawDepth = 0;
};

View file

@ -29,6 +29,7 @@ GlRenderTarget::~GlRenderTarget()
if (mFbo == 0) return;
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CHECK(glDeleteFramebuffers(1, &mFbo));
GL_CHECK(glDeleteFramebuffers(1, &mResolveFbo));
if (mColorTex != 0) {
GL_CHECK(glDeleteTextures(1, &mColorTex));
@ -36,6 +37,9 @@ GlRenderTarget::~GlRenderTarget()
if (mDepthStencilBuffer != 0) {
GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer));
}
if (mColorBuffer!= 0) {
GL_CHECK(glDeleteRenderbuffers(1, &mColorBuffer));
}
}
void GlRenderTarget::init(GLint resolveId)
@ -119,15 +123,15 @@ GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLui
for (uint32_t i = 0; i < mPool.count; i++) {
auto rt = mPool[i];
if (rt->getWidth() == width && rt->getHeight() == height) {
rt->setViewport(vp);
if (rt->getWidth() == width && rt->getHeight() == height && !rt->isInUse()) {
rt->setInUse(true);
return rt;
}
}
auto rt = new GlRenderTarget(width, height);
rt->init(resolveId);
rt->setViewport(vp);
rt->setInUse(true);
mPool.push(rt);
return rt;
}

View file

@ -45,16 +45,19 @@ public:
const RenderRegion& getViewport() const { return mViewport; }
bool invalid() const { return mFbo == GL_INVALID_VALUE; }
bool isInUse() const { return mInUse; }
void setInUse(bool inUse) { mInUse = inUse; }
private:
uint32_t mWidth = 0;
uint32_t mHeight = 0;
uint32_t mWidth;
uint32_t mHeight;
RenderRegion mViewport{};
GLuint mFbo = GL_INVALID_VALUE;
GLuint mColorBuffer = 0;
GLuint mDepthStencilBuffer = 0;
GLuint mResolveFbo = 0;
GLuint mColorTex = 0;
bool mInUse = false;
};
class GlRenderTargetPool {

View file

@ -332,12 +332,11 @@ void GlSimpleBlendTask::run()
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(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask)
: GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mComposeTask(composeTask) {}
GlComplexBlendTask::~GlComplexBlendTask()
{
delete mStencilTask;
delete mComposeTask;
}
@ -349,8 +348,8 @@ void GlComplexBlendTask::run()
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));
GL_CHECK(glViewport(0, 0, mViewport.w, mViewport.h));
GL_CHECK(glScissor(0, 0, mViewport.w, mViewport.h));
const auto& vp = getViewport();
@ -358,21 +357,6 @@ void GlComplexBlendTask::run()
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();
@ -383,7 +367,6 @@ void GlComplexBlendTask::run()
void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth)
{
mStencilTask->normalizeDrawDepth(maxDepth);
GlRenderTask::normalizeDrawDepth(maxDepth);
}

View file

@ -206,17 +206,21 @@ private:
class GlComplexBlendTask: public GlRenderTask
{
public:
GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask);
GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask);
~GlComplexBlendTask() override;
void run() override;
void normalizeDrawDepth(int32_t maxDepth) override;
void setViewport(const RenderRegion& viewport) { mViewport = viewport; }
void setCopyViewport(const RenderRegion& viewport) { mCopyViewport = viewport; }
private:
GlRenderTarget* mDstFbo;
GlRenderTarget* mDstCopyFbo;
GlRenderTask* mStencilTask;
GlComposeTask* mComposeTask;
RenderRegion mViewport = {};
RenderRegion mCopyViewport = {};
};
class GlGaussianBlurTask: public GlRenderTask

View file

@ -156,6 +156,8 @@ void GlRenderer::initShaders()
mPrograms.push(new GlProgram(MASK_VERT_SHADER, SOFT_LIGHT_BLEND_FRAG));
mPrograms.push(new GlProgram(MASK_VERT_SHADER, DIFFERENCE_BLEND_FRAG));
mPrograms.push(new GlProgram(MASK_VERT_SHADER, EXCLUSION_BLEND_FRAG));
mPrograms.push(new GlProgram(MASK_VERT_SHADER, LIGHTEN_BLEND_FRAG));
mPrograms.push(new GlProgram(MASK_VERT_SHADER, DARKEN_BLEND_FRAG));
// effects
mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL));
@ -174,7 +176,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
bbox.intersect(vp);
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds());
if (complexBlend) {
vp = currentPass()->getViewport();
@ -254,11 +256,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
if (stencilTask) currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode));
else currentPass()->addRenderTask(task);
if (complexBlend) {
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
sdata.geometry.draw(task, &mGpuBuffer, flag);
endBlendingCompose(task, sdata.geometry.matrix);
}
if (complexBlend) endBlendingCompose(mBlendMethod);
}
@ -290,7 +288,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
return;
}
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds());
if (complexBlend) vp = currentPass()->getViewport();
@ -441,11 +439,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
currentPass()->addRenderTask(task);
}
if (complexBlend) {
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
sdata.geometry.draw(task, &mGpuBuffer, flag);
endBlendingCompose(task, sdata.geometry.matrix);
}
if (complexBlend) endBlendingCompose(mBlendMethod);
}
@ -539,7 +533,7 @@ GlRenderPass* GlRenderer::currentPass()
return mRenderPassStack.last();
}
bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds)
bool GlRenderer::beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds)
{
if (vp.w == 0 || vp.h == 0) return false;
@ -547,18 +541,18 @@ bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bound
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 (blend == BlendMethod::Normal || blend == BlendMethod::Add) return false;
if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
auto blendFbo = mBlendPool[0]->getRenderTarget(bounds);
mRenderPassStack.push(new GlRenderPass(blendFbo));
mRenderPassStack.push(new GlRenderPass(blendFbo, bounds));
return true;
}
void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix)
void GlRenderer::endBlendingCompose(BlendMethod blend)
{
auto blendPass = mRenderPassStack.last();
mRenderPassStack.pop();
@ -571,34 +565,10 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp);
{
const auto& passVp = currentPass()->getViewport();
auto task = new GlComplexBlendTask(getBlendProgram(blend), currentPass()->getFbo(), dstCopyFbo, composeTask);
auto x = vp.x;
auto y = vp.y;
auto w = vp.w;
auto h = vp.h;
stencilTask->setViewport({x, passVp.h - y - h, w, h});
}
stencilTask->setDrawDepth(currentPass()->nextDrawDepth());
{
// set view matrix
float matrix44[16];
currentPass()->getMatrix(matrix44, matrix);
uint32_t viewOffset = mGpuBuffer.push(matrix44, 16 * sizeof(float), true);
stencilTask->addBindResource(GlBindingResource{
0,
stencilTask->getProgram()->getUniformBlockIndex("Matrix"),
mGpuBuffer.getBufferId(),
viewOffset,
16 * sizeof(float),
});
}
auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask);
task->setViewport(currentPass()->getViewport());
task->setCopyViewport(blendPass->getViewport());
prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight());
@ -613,9 +583,9 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
delete(blendPass);
}
GlProgram* GlRenderer::getBlendProgram()
GlProgram* GlRenderer::getBlendProgram(BlendMethod blend)
{
switch (mBlendMethod) {
switch (blend) {
case BlendMethod::Multiply: return mPrograms[RT_MultiplyBlend];
case BlendMethod::Screen: return mPrograms[RT_ScreenBlend];
case BlendMethod::Overlay: return mPrograms[RT_OverlayBlend];
@ -625,6 +595,8 @@ GlProgram* GlRenderer::getBlendProgram()
case BlendMethod::SoftLight: return mPrograms[RT_SoftLightBlend];
case BlendMethod::Difference: return mPrograms[RT_DifferenceBlend];
case BlendMethod::Exclusion: return mPrograms[RT_ExclusionBlend];
case BlendMethod::Lighten: return mPrograms[RT_LightenBlend];
case BlendMethod::Darken: return mPrograms[RT_DarkenBlend];
default: return nullptr;
}
}
@ -711,6 +683,41 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
task->setViewport({x, static_cast<int32_t>((passVp.h - y - h)), w, h});
}
void GlRenderer::prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp)
{
auto x = 0;
auto y = 0;
auto w = vp.w;
auto h = vp.h;
Array<float> vertices(4 * 2);
// left top
vertices.push(x);
vertices.push(y);
// left bottom
vertices.push(x);
vertices.push(y + h);
// right top
vertices.push(x + w);
vertices.push(y);
// right bottom
vertices.push(x + w);
vertices.push(y + h);
Array<uint32_t> indices(6);
indices.push(0);
indices.push(1);
indices.push(2);
indices.push(2);
indices.push(1);
indices.push(3);
uint32_t vertexOffset = mGpuBuffer.push(vertices.data, vertices.count * sizeof(float));
uint32_t indexOffset = mGpuBuffer.pushIndex(indices.data, indices.count * sizeof(uint32_t));
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), vertexOffset});
task->setDrawRange(indexOffset, indices.count);
}
void GlRenderer::endRenderPass(RenderCompositor* cmp)
{
@ -723,6 +730,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
auto maskPass = mRenderPassStack.last();
mRenderPassStack.pop();
bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox);
GlProgram* program = nullptr;
switch(cmp->method) {
case MaskMethod::Alpha: program = mPrograms[RT_MaskAlpha]; break;
@ -758,6 +767,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
currentPass()->addRenderTask(compose_task);
}
if (complexBlend) endBlendingCompose(glCmp->blend);
maskPass->getFbo()->setInUse(false);
selfPass->getFbo()->setInUse(false);
delete(selfPass);
delete(maskPass);
} else {
@ -766,6 +780,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
mRenderPassStack.pop();
if (!renderPass->isEmpty()) {
bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox);
auto task = renderPass->endRenderPass<GlDrawBlitTask>(mPrograms[RT_Image], currentPass()->getFboId());
task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight());
@ -793,10 +809,22 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
4 * sizeof(uint32_t),
});
task->addBindResource(GlBindingResource{
1,
task->getProgram()->getUniformBlockIndex("ColorInfo"),
mGpuBuffer.getBufferId(),
mGpuBuffer.push(info, 4 * sizeof(uint32_t), true),
4 * sizeof(uint32_t),
});
// texture id
task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")});
task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
currentPass()->addRenderTask(std::move(task));
if (complexBlend) endBlendingCompose(glCmp->blend);
renderPass->getFbo()->setInUse(false);
}
delete(renderPass);
}
@ -902,7 +930,7 @@ bool GlRenderer::preRender()
currentContext();
if (mPrograms.empty()) initShaders();
mRenderPassStack.push(new GlRenderPass(&mRootTarget));
mRenderPassStack.push(new GlRenderPass(&mRootTarget, RenderRegion{0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)}));
return true;
}
@ -940,13 +968,14 @@ bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
}
auto glCmp = static_cast<GlCompositor*>(cmp);
glCmp->blend = mBlendMethod;
if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) {
auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox);
mRenderPassStack.push(new GlRenderPass(renderTarget));
mRenderPassStack.push(new GlRenderPass(renderTarget, glCmp->bbox));
} else {
// empty render pass
mRenderPassStack.push(new GlRenderPass(nullptr));
mRenderPassStack.push(new GlRenderPass(nullptr, glCmp->bbox));
}
return true;
@ -1265,6 +1294,10 @@ bool GlRenderer::renderImage(void* data)
auto bbox = sdata->geometry.viewport;
bool complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata->geometry.getBounds());
if (complexBlend) vp = currentPass()->getViewport();
bbox.intersect(vp);
if (bbox.w <= 0 || bbox.h <= 0) return true;
@ -1277,7 +1310,10 @@ bool GlRenderer::renderImage(void* data)
if (!sdata->clips.empty()) drawClip(sdata->clips);
auto task = new GlRenderTask(mPrograms[RT_Image]);
GlRenderTask* task = nullptr;
if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Image]);
else task = new GlRenderTask(mPrograms[RT_Image]);
task->setDrawDepth(drawDepth);
if (!sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image)) {
@ -1285,10 +1321,6 @@ bool GlRenderer::renderImage(void* data)
return true;
}
bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds());
if (complexBlend) vp = currentPass()->getViewport();
// matrix buffer
float matrix44[16];
currentPass()->getMatrix(matrix44, sdata->geometry.matrix);
@ -1319,11 +1351,7 @@ bool GlRenderer::renderImage(void* data)
currentPass()->addRenderTask(task);
if (complexBlend) {
auto task = new GlRenderTask(mPrograms[RT_Stencil]);
sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image);
endBlendingCompose(task, sdata->geometry.matrix);
}
if (complexBlend) endBlendingCompose(mBlendMethod);
return true;
}
@ -1566,4 +1594,4 @@ GlRenderer* GlRenderer::gen(TVG_UNUSED uint32_t threads)
}
return new GlRenderer;
}
}

View file

@ -59,6 +59,8 @@ public:
RT_SoftLightBlend,
RT_DifferenceBlend,
RT_ExclusionBlend,
RT_LightenBlend,
RT_DarkenBlend,
RT_GaussianVert,
RT_GaussianHorz,
@ -113,12 +115,13 @@ private:
GlRenderPass* currentPass();
bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds);
void endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix);
GlProgram* getBlendProgram();
bool beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds);
void endBlendingCompose(BlendMethod blend);
GlProgram* getBlendProgram(BlendMethod blend);
void prepareBlitTask(GlBlitTask* task);
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight);
void prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp);
void endRenderPass(RenderCompositor* cmp);
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform);

View file

@ -598,131 +598,199 @@ const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER(
\
in vec2 vUV; \
out vec4 FragColor; \
// 1/1024.
const float kEhCloseEnoughHalf = 0.0009765625;
)"
const char* MULTIPLY_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = srcColor * dstColor;
vec4 src = texture(uSrcTexture, vUV);
vec4 dst = texture(uDstTexture, vUV);
FragColor = vec4((1.0 - src.a)*dst.rgb + (1.0 - dst.a)*src.rgb + src.rgb*dst.rgb, src.a + (1.0 - src.a)*dst.a);
}
)";
#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"(
const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = screenBlend(srcColor, dstColor);
FragColor = srcColor + (1.0 - srcColor) * dstColor;
}
)";
const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"(
#define BLEND_OVERLAY_FUNC R"(
float blend_overlay_component(vec2 s, vec2 d)
{
return (2.0 * d.x <= d.y) ? 2.0 * s.x * d.x
: s.y * d.y - 2.0 * (d.y - d.x) * (s.y - s.x);
}
vec4 blend_overlay(vec4 src, vec4 dst)
{
vec4 result = vec4(
blend_overlay_component(src.ra, dst.ra),
blend_overlay_component(src.ga, dst.ga),
blend_overlay_component(src.ba, dst.ba),
src.a + (1.0 - src.a) * dst.a
);
result.rgb += dst.rgb*(1 - src.a) + src.rgb*(1 - dst.a);
return result;
}
)"
const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = hardLightBlend(dstColor, srcColor);
FragColor = blend_overlay(srcColor, dstColor);
}
)";
const char* COLOR_DODGE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
float color_dodge_component(vec2 s, vec2 d)
{
float dxScale = d.x == 0.0 ? 0.0 : 1.0;
float delta = dxScale * min(d.y, abs(s.y - s.x) >= kEhCloseEnoughHalf ? (d.x * s.y / (s.y - s.x)) : d.y);
return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y);
}
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = vec4(
srcColor.r < 1.0 ? dstColor.r / (1.0 - srcColor.r) : (dstColor.r > 0.0 ? 1.0 : 0.0),
srcColor.g < 1.0 ? dstColor.g / (1.0 - srcColor.g) : (dstColor.g > 0.0 ? 1.0 : 0.0),
srcColor.b < 1.0 ? dstColor.b / (1.0 - srcColor.b) : (dstColor.b > 0.0 ? 1.0 : 0.0),
1.0
color_dodge_component(srcColor.ra, dstColor.ra),
color_dodge_component(srcColor.ga, dstColor.ga),
color_dodge_component(srcColor.ba, dstColor.ba),
srcColor.a + (1.0 - srcColor.a) * dstColor.a
);
}
)";
const char* COLOR_BURN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
float color_burn_component(vec2 s, vec2 d)
{
float dyTerm = d.y == d.x ? d.y : 0.0;
float delta = abs(s.x) >= kEhCloseEnoughHalf ? d.y - min(d.y, ((d.y - d.x) * s.y / s.x)) : dyTerm;
return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y);
}
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = vec4(
srcColor.r > 0.0 ? (1.0 - (1.0 - dstColor.r) / srcColor.r) : (dstColor.r < 1.0 ? 0.0 : 1.0),
srcColor.g > 0.0 ? (1.0 - (1.0 - dstColor.g) / srcColor.g) : (dstColor.g < 1.0 ? 0.0 : 1.0),
srcColor.b > 0.0 ? (1.0 - (1.0 - dstColor.b) / srcColor.b) : (dstColor.b < 1.0 ? 0.0 : 1.0),
1.0
color_burn_component(srcColor.ra, dstColor.ra),
color_burn_component(srcColor.ga, dstColor.ga),
color_burn_component(srcColor.ba, dstColor.ba),
srcColor.a + (1.0 - srcColor.a) * dstColor.a
);
}
)";
const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"(
const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = hardLightBlend(srcColor, dstColor);
FragColor = blend_overlay(dstColor, srcColor);
}
)";
const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER SOFT_LIGHT_BLEND_FUNC R"(
const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
float soft_light_component(vec2 s, vec2 d)
{
if (2.0 * s.x <= s.y) {
return (d.x * d.x * (s.y - 2.0 * s.x) / d.y) + (1.0 - d.y) * s.x + d.x * (-s.y + 2.0 * s.x + 1.0);
} else if(4.0 * d.x <= d.y) {
float DSqd = d.x * d.x;
float DCub = DSqd * d.x;
float DaSqd = d.y * d.y;
float DaCub = DaSqd * d.y;
return (DaSqd * (s.x - d.x * (3.0 * s.y - 6.0 * s.x - 1.0)) + 12.0 * d.y * DSqd * (s.y - 2.0 * s.x) - 16.0 * DCub * (s.y - 2.0 * s.x) - DaCub * s.x) / DaSqd;
} else {
return d.x * (s.y - 2.0 * s.x + 1.0) + s.x - sqrt(d.y * d.x) * (s.y - 2.0 * s.x) - d.y * s.x;
}
}
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
);
FragColor = dstColor.a == 0.0 ? srcColor : vec4(
soft_light_component(srcColor.ra, dstColor.ra),
soft_light_component(srcColor.ga, dstColor.ga),
soft_light_component(srcColor.ba, dstColor.ba),
srcColor.a + (1.0 - srcColor.a) * dstColor.a
);
}
)";
const char* DIFFERENCE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
vec4 src = texture(uSrcTexture, vUV);
vec4 dst = texture(uDstTexture, vUV);
FragColor = abs(dstColor - srcColor);
FragColor = vec4(src.rgb + dst.rgb - 2.0 * min(src.rgb * dst.a, dst.rgb * src.a), src.a + (1.0 - src.a) * dst.a);
}
)";
const char* EXCLUSION_BLEND_FRAG = COMPLEX_BLEND_HEADER R"(
void main()
{
vec4 src = texture(uSrcTexture, vUV);
vec4 dst = texture(uDstTexture, vUV);
FragColor = vec4(dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb, src.a + (1.0 - src.a) * dst.a);
}
)";
#define LD_BLEND_FUNC R"(
vec4 blendSrcOver(vec4 src, vec4 dst) { return src + (1 - src.a)*dst; }
vec4 blendLightDarken(float mode, vec4 src, vec4 dst)
{
vec4 a = blendSrcOver(src, dst);
vec3 b = (1.0 - dst.a) * src.rgb + dst.rgb;
a.rgb = mode * min(a.rgb * mode, b.rgb * mode);
return a;
}
)"
const char* LIGHTEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = dstColor + srcColor - (2.0 * dstColor * srcColor);
FragColor = blendLightDarken(-1.0, srcColor, dstColor);
}
)";
const char* DARKEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"(
void main()
{
vec4 srcColor = texture(uSrcTexture, vUV);
vec4 dstColor = texture(uDstTexture, vUV);
FragColor = blendLightDarken(1.0, srcColor, dstColor);
}
)";

View file

@ -58,12 +58,15 @@ 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;
extern const char* EFFECT_VERTEX;
extern const char* LIGHTEN_BLEND_FRAG;
extern const char* DARKEN_BLEND_FRAG;
extern const char* GAUSSIAN_VERTICAL;
extern const char* GAUSSIAN_HORIZONTAL;
extern const char* EFFECT_VERTEX;
extern const char* EFFECT_DROPSHADOW;
extern const char* EFFECT_FILL;
extern const char* EFFECT_TINT;
extern const char* EFFECT_TRITONE;
#endif /* _TVG_GL_SHADERSRC_H_ */