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
This commit is contained in:
RuiwenTang 2024-09-14 17:51:54 +08:00 committed by Hermet Park
parent 9c5933a4e5
commit 1fe9f269b1
8 changed files with 448 additions and 9 deletions

View file

@ -74,6 +74,10 @@ public:
}
int nextDrawDepth() { return ++mDrawDepth; }
void setDrawDepth(int32_t depth) { mDrawDepth = depth; }
GlRenderTarget* getFbo() { return mFbo; }
private:
GlRenderTarget* mFbo;
Array<GlRenderTask*> mTasks = {};

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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<GlProgram>(GlShader::gen(STENCIL_VERT_SHADER, STENCIL_FRAG_SHADER)));
// blit Renderer
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(BLIT_VERT_SHADER, BLIT_FRAG_SHADER)));
// complex blending Renderer
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, MULTIPLY_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, SCREEN_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, OVERLAY_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, COLOR_DODGE_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, COLOR_BURN_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, HARD_LIGHT_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, SOFT_LIGHT_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, DIFFERENCE_BLEND_FRAG)));
mPrograms.push_back(make_unique<GlProgram>(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<GlComposeTask>(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;
}

View file

@ -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<std::unique_ptr<GlProgram>> mPrograms;
unique_ptr<GlRenderTarget> mRootTarget = {};
Array<GlRenderTargetPool*> mComposePool = {};
Array<GlRenderTargetPool*> mBlendPool = {};
vector<GlRenderPass> mRenderPassStack = {};
vector<unique_ptr<GlCompositor>> mComposeStack = {};
@ -115,6 +129,8 @@ private:
} mDisposed;
bool mClearBuffer = true;
BlendMethod mBlendMethod = BlendMethod::Normal;
};
#endif /* _TVG_GL_RENDERER_H_ */

View file

@ -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

View file

@ -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);
}
)";

View file

@ -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_ */