mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
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:
parent
9c5933a4e5
commit
1fe9f269b1
8 changed files with 448 additions and 9 deletions
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
)";
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue