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

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.

Related

Does dual-source blending require larger color buffer?

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,

Weird behaviour using mipmapping from glsl

I am trying to use mipmapping with vulkan. I do understand that I should use vkCmdBlit between each layer for each image, but before doing that, I just wanted to know how to change the layer in GLSL.
Here is what I did.
First I load and draw a texture (using layer 0) and there was no problem. The "rendered image" is the texture I load, so it is good.
Second, I use this shader (so I wanted to use the second layer (number 1)) but the "rendered image" does not change :
#version 450
layout(set = 0, binding = 0) uniform sampler2D tex;
in vec2 texCoords;
layout(location = 0) out vec4 outColor;
void main() {
outColor = textureLod(tex, texCoords, 1);
}
According to me, the rendered image should be changed, but not at all, it is always the same image, even if I increase the "1" (the number of the layer).
Third instead changing anything in the glsl code, I change the layer number into the ImageSubresourceRange to create the imageView, and the "rendered image" changed, so it seems normal to me and when I will use vkCmdBlit, I must see the original image in lower resolution.
The real problem is, when I try to use a mipmapping (through mipmapping) in GLSL, it does not affect at all the rendered image, but in C++ it does (and that seems fair).
here is (all) my source code
https://github.com/qnope/Vulkan-Example/tree/master/Mipmap
Judging by your default sampler creation info (https://github.com/qnope/Vulkan-Example/blob/master/Mipmap/VkTools/System/sampler.cpp#L28) you always set the maxLod member of your samplers to zero, so your lod is always clamped between 0.0 and 0.0 (minLod/maxLod). This would fit the behaviour you described.
So try setting the maxLod member of your sampler creation info to the actual number of mip maps in your texture and changing the lod level in the shader shoudl work fine.

WebGL alpha blending

I'm trying to layer one texture over another, but I'm having alpha blending issues around the edges. I've tried many blending combinations with no luck. Where am I going wrong?
Current state of framebuffer (opaque):
Transparent texture rendered in off-screen framebuffer:
Result when I try to blend the two. Notice the edges on the circle:
Here's the blendFunc:
_gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
Here's the shader. Just basic rendering of a texture:
uniform sampler2D texture;
varying vec2 vUv;
void main() {
vec4 tColor = texture2D(texture, vUv);
gl_FragColor = tColor;
}
Most likely your textures are using premultiplied alpha and so your blend function should be
_gl.blendFunc(_gl.ONE, _gl.ONE_MINUS_SRC_ALPHA);
If your textures are not premultiplied you probably want to premultiply them either in your shader
gl_FragColor.rgb *= gl_FragColor.a
or when you load them (before you call gl.texImage2D) you can tell the browser to premultiply them
_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
This document probably explains the issues better
and you might find this relevant as well
WebGL: How to correctly blend alpha channel png

Get current fragment color

Hey I currently have a system in OpenGL that uses glBlendFunc for bleding diffrent shaders but I would like to do something like this
fragColor = currentColor * lightAmount;
I tried to use gl_Color but its depricated and my engine will not let me use it.
According to this document there is no built-in access for the fragment color in the fragment shader.
What you could do is render your previous passes in another textures, send those textures to the GPU (as uniforms) and do the blending in your last pass.

How to invert texture colours in OpenGL

I'm texturing a 2D object in OpenGL, and while I have the texture loading fine, I'm just not quite sure how to invert the colours. How do I access the colour information of the bitmap and invert it? Would this be done best in a shader or in the Main program?
I'm leaving this kind of open as I'm not looking for a "fix my code" type of answer, but rather "this is how you access the colour information of the bitmap".
Its s simple as
gl_FragColor = vec4(1.0 - textureColor.r,1.0 -textureColor.g,1.0 -textureColor.b,1)
It is best to do this kind of thing in the shader, if you want to do it once and then reuse it for future draws just use render to texture. This is the fastest way to do color inversion.
EDIT: You use
vec4 textureColor = texture2D(uSampler,vTexCoords)
before your do gl_FragColor
using .r ,.g,.b and .a access the red, green, blue and alpha values respectively.
I'm writing a vision impaired game and for this I wanted to invert the colors, so white is black etc... which sounds like what you're doing. I eventually hit on this that works and does not involve shaders or modifying the textures. However it does kill blending. So I render to a texture buffer and then do
glLogicOp (GL_COPY_INVERTED);
glEnable (GL_COLOR_LOGIC_OP);
<bind to texture buffer and blit that>
glLogicOp (GL_COPY);
glDisable (GL_COLOR_LOGIC_OP);
Actually, you have to invert the texture coordinates before sampling the texture to achieve that.
So, in fragment shader:
vec2 vert_uv_New=vec2(1-vert_uv.x,1-vert_uv.y);
Now sample it:
gl_FragColor=texture2D(sampler,vert_uv_New);
This will do the job.
precision mediump float;
uniform sampler2D u_tex;
varying vec2 v_texCoord;
void main()
{
gl_FragColor = vec4(1,1,1,1) - texture2D(u_tex, v_texCoord);
}