Does dual-source blending require larger color buffer? - opengl

In OpenGL, we can turn on dual-source blending through following code in fragment shader:
layout(location = 0, index = 0) out vec4 color1;
layout(location = 0, index = 1) out vec4 color2;
And through token XX_SRC1_XX get color2 in blending functions. I have questions that:
If I want to do off-screen rendering, do I need to double the texture's storage's size as there are two colors output.
Is it that once I turn on dual-source blending then I can only output two colors to one buffer? And it means that I cannot bind more than one color buffers through attaching them to GL_COLOR_ATTACHMENTi tokens.
Is the qualifier 'index' here only used for dual-source blending purpose?

Dual-source blending means blending involving two color outputs from the FS. That has nothing to do with the nature of the images being written to (only the number). Location 0 still refers to the 0th index in the glDrawBuffers array, and the attachment works just as it did previously.
You are outputting two colors. But only one value (the result of the blend operation) gets written.
And it means that I cannot bind more than one color buffers through attaching them to GL_COLOR_ATTACHMENTi tokens.
Well, that depends on the number of attached images allowed when doing dual-source blending, as specified by your implementation. Granted, all known implementations set this to 1. Even Vulkan implementations all use 1 (or 0 if they don't support dual-source blending); the only ones that don't seem to be either early, broken implementations or software renderers,

Related

Dual Source Blending with Multiple Render Target link error

I am trying to implement text subpixel using dual-source blending.
https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_blend_func_extended.txt
layout(location = 0, index = 0) out vec4 fragColor;
layout(location = 0, index = 1) out vec4 srcColor1;
void main()
{
vec4 scolor0;
vec4 scolor1;
// some calulcations
fragColor = scolor0;
srcColor1 = scolor1;
}
It works fine.
But now I want to write to another render target
like
layout(location = 0, index = 0) out vec4 fragColor;
layout(location = 0, index = 1) out vec4 srcColor1;
layout(location = 1) out uvec4 myMRT;
and I tried changing location = 1 or location = 2 but in either case,I get this linking error
error: assembly compile error for fragment shader at offset
error: too many color outputs when using dual source output
Which is the best way to use MRT in case of Dual source blending?
The OpenGL 4.6 core profile specification states in section 17.3.6.3 "Dual Source Blending and Multiple Draw Buffers" (emphasis mine):
Blend functions that require the second color input
(Rs1;Gs1;Bs1;As1) (SRC1_- COLOR, SRC1_ALPHA, ONE_MINUS_SRC1_COLOR, or ONE_MINUS_SRC1_ALPHA)
may consume hardware resources that could otherwise be used for rendering to multiple draw buffers. Therefore, the number of draw
buffers that can be attached to a framebuffer may be lower when using
dual-source blending.
The maximum number of draw buffers that may be attached to a single framebuffer when using dual-source blending functions is
implementation-dependent and may be queried by calling GetIntegerv
with pname MAX_DUAL_SOURCE_DRAW_BUFFERS. When using dual-source
blending, MAX_DUAL_SOURCE_DRAW_BUFFERS should be used in place of
MAX_DRAW_BUFFERS to determine the maximum number of draw buffers
that may be attached to a single framebuffer. The value of
MAX_DUAL_SOURCE_DRAW_BUFFERS must be at least 1.
(The older ARB_blend_func_exteded extension you referred uses different wording, but also only guarantees a minumum of 1 for MAX_DUAL_SOURCE_DRAW_BUFFERS)
So even wit the most current GL spec, GL implementations are not required to support dual-source blending with multiple render targets at all. And looking at the current report for that capability on gpuinfo.org shows that there are no real word implementations supporting a value bigger than 1. So nope, you can't do this. At least that's the state of affairs in December 2020.

Framebuffer's RGB colors are different when alpha value is modified

I have fragment shader that writes to framebuffer's texture. I have 2 shaders that use the framebuffer's output texture. First uses just alpha and Second just RGB. When framebuffer's shader return fragmentColor.a = 0 the RGB component completely dissapear when used in Second shader even when I set fragmentColor.a to 1 manually. Is it possible to prevent this "RGB dissapear" or am I getting some bug? Yes I can generate different textures for each shader but it costs huge amount of perfomance because even with only one "draw" it's very costly. But if I could output to two textures at same time that would also solve whole problem. Anyone has any advice/solution?
You probably have blending enabled and set glBlendFunc to something like glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), which will also affect the destination alpha values. To overcome this there's separable blending, set it up with glBlendFuncSeparate[i] and choose the source and destination equations so that the RGB values are passed through as needed for your application. Use the "i" variant to set different blending functions for each fragment shader output target (if you have a single fragment shader with multiple outputs), or call the non-"i" version multiple times, before each render pass to set it up for your individual fragment shaders.

How can I simulate "Glow Dodge" blending by using OpenGL Shader?

I wanted to simulate 'glow dodge' effect on clip studio using opengl and its shader.
So I found out that following equation is how 'glow dodge' works.
final.rgb = dest.rgb / (1 - source.rgb)
Then I came up with 2 ways to actually do it, but neither one doesn't seem to work.
First one was to calculate 1 / (1 - source.rgb) in the shader, and do multiply blending by using glBlendfunc(GL_ZERO, GL_SRC_COLOR), or glBlendfunc(GL_DST_COLOR, GL_ZERO).
but as khronos page says, all scale factors have range 0 to 1. which means I can't multiply the numbers over 1. so I can't use this method cuz most of the case goes more than 1.
Second one was to bring background texels by using glReadPixel(), then calculate everything in shader, then do additive blending by using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
regardless the outcome I could get, glReadPixel() itself takes way too much time even with a 30 x 30 texel area. so I can't use this method.
I wonder if there's any other way to get an outcome as glowdodge blending mode should have.
With the extension EXT_shader_framebuffer_fetch a value from the framebuffer can be read:
This extension provides a mechanism whereby a fragment shader may read existing framebuffer data as input. This can be used to implement compositing operations that would have been inconvenient or impossible with fixed-function blending. It can also be used to apply a function to the framebuffer color, by writing a shader which uses the existing framebuffer color as its only input.
The extension is available for dektop OpenGL and OpenGL ES. An OpenGL extension has to be enabled (see Core Language (GLSL)- Extensions. The fragment color can be retrieved by the built in variable gl_LastFragData. e.g.:
#version 400
#extension GL_EXT_shader_framebuffer_fetch : require
out vec4 fragColor;
# [...]
void main
{
# [...]
fragColor = vec4(gl_LastFragData[0].rgb / (1.0 - source.rgb), 1.0);
}

Conditional Blending with OpenGL ES

I want to implement a fragment/blending operation using OpenGL ES 3.1 that fulfills the following requirements:
If the pixel produced by the fragment shader fulfills a certain condition (that can be determined as early as in the vertex shader) then its color value should be added to the one in the framebuffer.
If the pixel doesn't fulfill the condition, then the color should completely replace the one in the framebuffer.
Can this be done via the usual blending functions, alpha tricks etc.?
I think you could just use standard premultiplied alpha blending:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
If you want to replace, then you output an alpha value of 1 from your fragment shader. If you want to do additive then you output an alpha value of 0 from your fragment shader.
That assumes you're only really interested in the RGB values that end up in your framebuffer.
If you know this at vertex shading time I presume that whole triangles are either one blend or another. This is an ideal thing for stencil testing provided that you don't have too much geometry.
Clear stencil to zero.
Draw shape with color writes disabled, and stencil being set to one for regions which match one of the two blend rules.
Set blend rule one.
Draw shape with stencil testing enabled and passing when stencil == 0.
Set blend rule two.
Draw shape with stencil testing enabled and passing when stencil == 1.
How much this costs depends on your geometry, as you need to pass it in to rendering multiple times, but the stencil testing part of this is normally close to "free".

In OpenGL, is there a way to blend based on a separate channel's value in the shader?

In OpenGL (not ES), is there a universal way to blend based a texture while drawing based on another texture or variable's value? On OpenGLES, I know that I can do custom blending on some platforms via extensions like GL_EXT_shader_framebuffer_fetch. The reason I ask, is that I have a special texture where the forth channel is not alpha, and I need to be able to blend it on a separate alpha which is available on a different map.
You want dual-source blending, which is available in core as of OpenGL 3.3. This allows you to provide a fragment shader with two outputs and use both of them in the blend function.
You would declare outputs in the fragment shader like this:
layout(location = 0, index = 0) out vec4 outColor;
layout(location = 0, index = 1) out vec4 outAlpha;
You could then set the blending function like this, for premultiplied alpha:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC1_COLOR);
Or non-premultiplied alpha:
glBlendFunc(GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR);
Note that SRC1 here is the second output of the fragment shader. If I remember correctly, this blending will only work for one location.