Does blending work with the GLSL mix function - opengl

These are my blending functions.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
I mix two textures in the fragment shader using mix function.
FragColor =
mix(texture(texture1, TexCoord), texture(texturecubeMap, ransformedR), MixValueVariables.CubeMapValue ) *
vec4(result, material.opacity / 100.0); // multiplying texture with material value
Despite changing the blend Equation or BlendFunc types i don't see any difference in the output.
Does mix function works with the blend types.

Does mix function works with the blend types.
No. Blending and the glsl function mix are completely different things.
The function mix(x, y, a) calculates always x * (1−a) + y * a.
Blending takes the fragment color outputs from the Fragment Shader and combines them with the colors in the color buffer of the frame buffer.
If you want to use Blending, then you've to
draw the geometry with the first texture
activate blending
draw the geometry with the second texture
If you just want to blend the textures in the fragment shader, then you've to use the glsl arithmetic. See GLSL Programming/Vector and Matrix Operations
e.g.
vec4 MyBlend(vec4 source, vec4 dest, float alpha)
{
return source * alpha + dest * (1.0-alpha);
}
void main()
{
vec4 c1 = texture(texture1, TexCoord);
vec4 c2 = texture(texturecubeMap, ransformedR);
float f = MixValueVariables.CubeMapValue
FragColor = MyBlend(c1, c2, f) * vec4(result, material.opacity / 100.0);
}
Adapt the function MyBlend to your needs.

Related

GLSL how to set transparency on a plane?

maybe someone could give me a hint. I wrote a shader that draws a circle on a plane.The circle is colored in two colors mixed together. I would like to make only the circle visible and the plane transparent. I think I need an if statement in the fragment shader, but I can't write it properly to make it work. Below I am pasting my fragment shader. I will be grateful for any hint.
fragmentShader: `
#define PI2 6.28318530718
uniform vec3 u_color1;
uniform vec3 u_color2;
varying vec2 vUv;
varying vec3 vPosition;
varying vec2 p;
varying float result;
float circle(vec2 pt, vec2 center, float radius, float edge_thickness){
vec2 p = pt - center;
float len = length(p);
float result = 1.0-smoothstep(radius-edge_thickness, radius, len);
return result;
}
void main (void)
{
vec3 col = mix(u_color1, u_color2, vUv.y);
vec3 color = col * circle(vPosition.xy, vec2(0.0), 10.0, 0.002);
gl_FragColor = vec4(color, 1.0);
}
`,
On CPU side code you need:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
And also make sure your rendering context has some alpha btis allocated. If not use different blending function (one that does not use alpha).
On GPU side you use alpha channel to set transparency (for the blend function above) so:
gl_FragColor = vec4(color, alpha_transparency);
Its also usual to pass vec4 color in RGBA form directly from CPU side code instead.
On top of all this you also need to render your stuff in correct order see:
OpenGL - How to create Order Independent transparency?

glBlendEquationSeparate and glBlendFuncSeparate - fragment shader implementation

I have OpenGL application which blends colors based on the following settings:
glBlendEquationSeparate( GL_FUNC_ADD, GL_FUNC_ADD );
glBlendFuncSeparate( GL_ONE, GL_ONE, GL_ONE, GL_ONE );
In the same way I would like blend two textures in the fragment shader. Does the above blending settings translate to the simple addition:
vec4 colorA = texture( samplerA, texCoords );
vec4 colorB = texture( samplerB, texCoords );
vec4 colorC = colorA + colorB;
?
The resulting formula of
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
is
dst.rgb = 1 * src.rgb + 1 * dst.rgb
dst.a = 1 * src.a + 1 * dst.a
Blending is very different from computing colors in the fragment shader.
In this case, the current fragment color (and the alpha channel) is added to the fragment color (and the alpha channel) in the framebuffer. So yes, you can think of it as colorA + colorB.

Render a texture (decal) with an alpha value?

I tried to draw two textures of decals and background, but only the alpha part of the decals becomes white.
I simply tried the following.
Draw 2 textures (background & decals)
Add glBlendFunc to apply decals alpha value
#version 330 core
in vec2 UV;
out vec3 color;
uniform sampler2D background;
in vec3 decalCoord;
uniform sampler2D decal;
void main(){
vec3 BGTex = texture( background, UV ).rgb;
vec3 DecalTex = texture(decal, decalCoord.xy).rgba;
color =
vec4(BGTex,1.0) + // Background texture is DDS DXT1 (I wonder if DXT1 is the cause?)
vec4(DecalTex,0.0); // Decal texture is DDS DXT3 for alpha
}
// Set below...
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
I was able to draw normally but there are the following problems.
  -The alpha part of the decals can not be made transparent.
Is this a problem that can be solved within the fragmentshader?
In that case, how should I rewrite the code?
The texture is DDS, one of which is DXT1 type (Background. Because this texture doesn't need alpha) and the other is DXT3 type (Decal). Is this also the cause?(Both need to be the DXT3 type?)
Also, should I look for another way to put on the decals?
DecalTex should be a vec4, not a vec3. Otherwise the alpha
value will not be stored.
You will also have to change the line at the end to: color =
vec4(BGTex, 1.0) + DecalTex * DecalTex.a As currently it sets the alpha component
to 0.
The DecalTex has an alpha channel. The alpha channel is "weight", which indicates the intensity of the DecalTex. If the alpha channel is 1, then the color of DecalTex has to be used, if the alpha channel is 0, then the color of BGTex has to be used.
Use mix to mix the color of BGTex and DecalTex dependent on the alpha channel of DecalTex. Of course the type of the DecalTex has to be vec4:
vec3 BGTex = texture( background, UV ).rgb;
vec4 DecalTex = texture(decal, decalCoord.xy).rgba;
color = vec4(mix(BGTex.rgb, DecalTex.rgb, DecalTex.a), 1.0);
Note, mix linear interpolates between the 2 values:
mix(x, y, a) = x * (1−a) + y * a
This is similar the operation which is performed by the blending function and equation:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
But Blending is applied to the fragment shader output and the current value in the framebuffer.
You don't need any blending at all, because the textures are "blended" in the fragment shader and the result is put in the framebuffer. Since the alpha channel of the fragment shader output is >= 1.0, the output is completely "opaque".

Rendering to and sampling same texture in one draw call

Is it possible in OpenGL to sample from, and then write to the same texture in one draw call using GLSL?
I think it actually IS possible. I did it like this for GLSL alpha blending:
vec4 prevColor = texture(dfFrameTexture, pointCoord);
vec4 result = vec4(color.a) * color + vec4(1.0 - color.a) * prevColor;
gl_FragColor = result;
dfFrameTexture is the texture that is being written to with gl_FragColor.
I tested this and it worked. Drawing a white quad onto dfFrameTexture after it's been cleared to black results in a grey quad when I set the color to (0.5, 0.5, 0.5, 0.5)

How do I use a GLSL shader to apply a radial blur to an entire scene?

I have a radial blur shader in GLSL, which takes a texture, applies a radial blur to it and renders the result to the screen. This works very well, so far.
The problem is, that this applies the radial blur to the first texture in the scene. But what I actually want to do, is to apply this blur to the whole scene.
What is the best way to achieve this functionality? Can I do this with only shaders, or do I have to render the scene to a texture first (in OpenGL) and then pass this texture to the shader for further processing?
// Vertex shader
varying vec2 uv;
void main(void)
{
gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
gl_Position = sign( gl_Position );
uv = (vec2( gl_Position.x, - gl_Position.y ) + vec2(1.0) ) / vec2(2.0);
}
// Fragment shader
uniform sampler2D tex;
varying vec2 uv;
const float sampleDist = 1.0;
const float sampleStrength = 2.2;
void main(void)
{
float samples[10];
samples[0] = -0.08;
samples[1] = -0.05;
samples[2] = -0.03;
samples[3] = -0.02;
samples[4] = -0.01;
samples[5] = 0.01;
samples[6] = 0.02;
samples[7] = 0.03;
samples[8] = 0.05;
samples[9] = 0.08;
vec2 dir = 0.5 - uv;
float dist = sqrt(dir.x*dir.x + dir.y*dir.y);
dir = dir/dist;
vec4 color = texture2D(tex,uv);
vec4 sum = color;
for (int i = 0; i < 10; i++)
sum += texture2D( tex, uv + dir * samples[i] * sampleDist );
sum *= 1.0/11.0;
float t = dist * sampleStrength;
t = clamp( t ,0.0,1.0);
gl_FragColor = mix( color, sum, t );
}
This basically is called "post-processing" because you're applying an effect (here: radial blur) to the whole scene after it's rendered.
So yes, you're right: the good way for post-processing is to:
create a screen-sized NPOT texture (GL_TEXTURE_RECTANGLE),
create a FBO, attach the texture to it
set this FBO to active, render the scene
disable the FBO, draw a full-screen quad with the FBO's texture.
As for the "why", the reason is simple: the scene is rendered in parallel (the fragment shader is executed independently for many pixels). In order to do radial blur for pixel (x,y), you first need to know the pre-blur pixel values of the surrounding pixels. And those are not available in the first pass, because they are only being rendered in the meantime.
Therefore, you must apply the radial blur only after the whole scene is rendered and fragment shader for fragment (x,y) is able to read any pixel from the scene. This is the reason why you need 2 rendering stages for that.