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; }
|
int nextDrawDepth() { return ++mDrawDepth; }
|
||||||
|
|
||||||
|
void setDrawDepth(int32_t depth) { mDrawDepth = depth; }
|
||||||
|
|
||||||
|
GlRenderTarget* getFbo() { return mFbo; }
|
||||||
private:
|
private:
|
||||||
GlRenderTarget* mFbo;
|
GlRenderTarget* mFbo;
|
||||||
Array<GlRenderTask*> mTasks = {};
|
Array<GlRenderTask*> mTasks = {};
|
||||||
|
|
|
@ -308,3 +308,80 @@ void GlClipTask::normalizeDrawDepth(int32_t maxDepth)
|
||||||
mClipTask->normalizeDrawDepth(maxDepth);
|
mClipTask->normalizeDrawDepth(maxDepth);
|
||||||
mMaskTask->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; }
|
GlProgram* getProgram() { return mProgram; }
|
||||||
const RenderRegion& getViewport() const { return mViewport; }
|
const RenderRegion& getViewport() const { return mViewport; }
|
||||||
|
float getDrawDepth() const { return mDrawDepth; }
|
||||||
private:
|
private:
|
||||||
GlProgram* mProgram;
|
GlProgram* mProgram;
|
||||||
RenderRegion mViewport = {};
|
RenderRegion mViewport = {};
|
||||||
|
@ -194,4 +195,31 @@ private:
|
||||||
GlRenderTask* mMaskTask;
|
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_ */
|
#endif /* _TVG_GL_RENDER_TASK_H_ */
|
||||||
|
|
|
@ -66,6 +66,10 @@ GlRenderer::~GlRenderer()
|
||||||
if (mComposePool[i]) delete mComposePool[i];
|
if (mComposePool[i]) delete mComposePool[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < mBlendPool.count; i++) {
|
||||||
|
if (mBlendPool[i]) delete mBlendPool[i];
|
||||||
|
}
|
||||||
|
|
||||||
--rendererCnt;
|
--rendererCnt;
|
||||||
|
|
||||||
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
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)));
|
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(STENCIL_VERT_SHADER, STENCIL_FRAG_SHADER)));
|
||||||
// blit Renderer
|
// blit Renderer
|
||||||
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(BLIT_VERT_SHADER, BLIT_FRAG_SHADER)));
|
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)
|
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();
|
auto bbox = sdata.geometry->getViewport();
|
||||||
|
|
||||||
bbox.intersect(vp);
|
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 x = bbox.x - vp.x;
|
||||||
auto y = bbox.y - vp.y;
|
auto y = bbox.y - vp.y;
|
||||||
auto w = bbox.w;
|
auto w = bbox.w;
|
||||||
auto h = bbox.h;
|
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);
|
task->setDrawDepth(depth);
|
||||||
|
|
||||||
if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) {
|
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 {
|
} else {
|
||||||
currentPass()->addRenderTask(task);
|
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)
|
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();
|
auto bbox = sdata.geometry->getViewport();
|
||||||
|
|
||||||
bbox.intersect(vp);
|
bbox.intersect(vp);
|
||||||
|
@ -229,6 +261,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool complexBlend = beginComplexBlending(bbox, sdata.geometry->getBounds());
|
||||||
|
|
||||||
|
if (complexBlend) vp = currentPass()->getViewport();
|
||||||
|
|
||||||
auto x = bbox.x - vp.x;
|
auto x = bbox.x - vp.x;
|
||||||
auto y = bbox.y - vp.y;
|
auto y = bbox.y - vp.y;
|
||||||
|
|
||||||
|
@ -397,6 +433,13 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
||||||
} else {
|
} else {
|
||||||
currentPass()->addRenderTask(task);
|
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();
|
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)
|
void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
||||||
{
|
{
|
||||||
|
@ -764,8 +918,10 @@ bool GlRenderer::target(int32_t id, uint32_t w, uint32_t h)
|
||||||
mComposeStack.clear();
|
mComposeStack.clear();
|
||||||
|
|
||||||
for (uint32_t i = 0; i < mComposePool.count; i++) delete mComposePool[i];
|
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();
|
mComposePool.clear();
|
||||||
|
mBlendPool.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -912,10 +1068,13 @@ const Surface* GlRenderer::mainSurface()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
|
bool GlRenderer::blend(BlendMethod method)
|
||||||
{
|
{
|
||||||
//TODO:
|
if (method == mBlendMethod) return true;
|
||||||
return false;
|
|
||||||
|
mBlendMethod = method;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -928,7 +1087,7 @@ bool GlRenderer::renderImage(void* data)
|
||||||
if (currentPass()->isEmpty()) return true;
|
if (currentPass()->isEmpty()) return true;
|
||||||
|
|
||||||
if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return true;
|
if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return true;
|
||||||
const auto& vp = currentPass()->getViewport();
|
auto vp = currentPass()->getViewport();
|
||||||
|
|
||||||
auto bbox = sdata->geometry->getViewport();
|
auto bbox = sdata->geometry->getViewport();
|
||||||
|
|
||||||
|
@ -952,6 +1111,10 @@ bool GlRenderer::renderImage(void* data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool complexBlend = beginComplexBlending(bbox, sdata->geometry->getBounds());
|
||||||
|
|
||||||
|
if (complexBlend) vp = currentPass()->getViewport();
|
||||||
|
|
||||||
// matrix buffer
|
// matrix buffer
|
||||||
{
|
{
|
||||||
const auto& matrix = sdata->geometry->getTransformMatrix();
|
const auto& matrix = sdata->geometry->getTransformMatrix();
|
||||||
|
@ -998,6 +1161,13 @@ bool GlRenderer::renderImage(void* data)
|
||||||
|
|
||||||
currentPass()->addRenderTask(task);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,15 @@ public:
|
||||||
RT_MaskDifference,
|
RT_MaskDifference,
|
||||||
RT_Stencil,
|
RT_Stencil,
|
||||||
RT_Blit,
|
RT_Blit,
|
||||||
|
RT_MultiplyBlend,
|
||||||
|
RT_ScreenBlend,
|
||||||
|
RT_OverlayBlend,
|
||||||
|
RT_ColorDodgeBlend,
|
||||||
|
RT_ColorBurnBlend,
|
||||||
|
RT_HardLightBlend,
|
||||||
|
RT_SoftLightBlend,
|
||||||
|
RT_DifferenceBlend,
|
||||||
|
RT_ExclusionBlend,
|
||||||
|
|
||||||
RT_None,
|
RT_None,
|
||||||
};
|
};
|
||||||
|
@ -91,6 +100,10 @@ private:
|
||||||
|
|
||||||
GlRenderPass* currentPass();
|
GlRenderPass* currentPass();
|
||||||
|
|
||||||
|
bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds);
|
||||||
|
void endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix);
|
||||||
|
GlProgram* getBlendProgram();
|
||||||
|
|
||||||
void prepareBlitTask(GlBlitTask* task);
|
void prepareBlitTask(GlBlitTask* task);
|
||||||
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight);
|
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight);
|
||||||
void endRenderPass(Compositor* cmp);
|
void endRenderPass(Compositor* cmp);
|
||||||
|
@ -105,6 +118,7 @@ private:
|
||||||
vector<std::unique_ptr<GlProgram>> mPrograms;
|
vector<std::unique_ptr<GlProgram>> mPrograms;
|
||||||
unique_ptr<GlRenderTarget> mRootTarget = {};
|
unique_ptr<GlRenderTarget> mRootTarget = {};
|
||||||
Array<GlRenderTargetPool*> mComposePool = {};
|
Array<GlRenderTargetPool*> mComposePool = {};
|
||||||
|
Array<GlRenderTargetPool*> mBlendPool = {};
|
||||||
vector<GlRenderPass> mRenderPassStack = {};
|
vector<GlRenderPass> mRenderPassStack = {};
|
||||||
vector<unique_ptr<GlCompositor>> mComposeStack = {};
|
vector<unique_ptr<GlCompositor>> mComposeStack = {};
|
||||||
|
|
||||||
|
@ -115,6 +129,8 @@ private:
|
||||||
} mDisposed;
|
} mDisposed;
|
||||||
|
|
||||||
bool mClearBuffer = true;
|
bool mClearBuffer = true;
|
||||||
|
|
||||||
|
BlendMethod mBlendMethod = BlendMethod::Normal;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _TVG_GL_RENDERER_H_ */
|
#endif /* _TVG_GL_RENDERER_H_ */
|
||||||
|
|
|
@ -79,7 +79,7 @@ uint32_t GlShader::compileShader(uint32_t type, char* shaderSrc)
|
||||||
#else
|
#else
|
||||||
shaderPack[0] ="#version 330 core\n";
|
shaderPack[0] ="#version 330 core\n";
|
||||||
#endif
|
#endif
|
||||||
shaderPack[1] = "precision highp float;\n precision mediump int;\n";
|
shaderPack[1] = "precision highp float;\n precision highp int;\n";
|
||||||
shaderPack[2] = shaderSrc;
|
shaderPack[2] = shaderSrc;
|
||||||
|
|
||||||
// Load the shader source
|
// Load the shader source
|
||||||
|
|
|
@ -198,7 +198,7 @@ void main()
|
||||||
\n
|
\n
|
||||||
vec2 ba = ed - st; \n
|
vec2 ba = ed - st; \n
|
||||||
\n
|
\n
|
||||||
float d = abs(dot(pos - st, ba) / dot(ba, ba)); \n
|
float d = dot(pos - st, ba) / dot(ba, ba); \n
|
||||||
\n
|
\n
|
||||||
float t = gradientWrap(d); \n
|
float t = gradientWrap(d); \n
|
||||||
\n
|
\n
|
||||||
|
@ -509,3 +509,138 @@ const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER(
|
||||||
FragColor = texture(uSrcTexture, vUV); \n
|
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* STENCIL_FRAG_SHADER;
|
||||||
extern const char* BLIT_VERT_SHADER;
|
extern const char* BLIT_VERT_SHADER;
|
||||||
extern const char* BLIT_FRAG_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_ */
|
#endif /* _TVG_GL_SHADERSRC_H_ */
|
||||||
|
|
Loading…
Add table
Reference in a new issue