Compositing colors and textures in webgl - webgl

How to do color compositing in webgl? I am not sure what to search for exactly maybe I am missing a word. I want to be able to draw a color or texture as overlay. For example, here I tried to add black overlay with 0.2 opacity but instead of using color directly I am substracting opacity. If I want to use different color as overlay, I will have to use different work around for it. How can I overlay/composite different colors or textures without using any work around? Shadertoy link
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord/iResolution.xy;
vec4 col = texture(iChannel0, uv);
vec4 overlay = vec4(0., 0., 0., 0.2);
col -= overlay.a;
fragColor = col;
}

If you want to darken the texture color towards black, you could use a simple multiplication:
vec4 col = texture(iChannel0, uv);
col *= 0.8; // 20% darker
Or if you'd like to use a color, you could also multiply those
vec4 col = texture(iChannel0, uv);
vec4 tint = vec4(1.0, 0.5, 0.0, 1.0); // Orange
col *= tint;
There are a lot of ways to blend colors together. Another alternative to create smooth gradients between colors is by using the mix() function. There is no one answer.

Related

SpriteKit: SKShader with custom shader not doing `mix` properly

I am using a custom shader to create a sprite in SpriteKit.
The main part of the shader does this...
vec4 col = mix(baseColor, overlayColor, overlayColor.a);
The colours I have are something like...
baseColor = UIColor(red:0.51, green:0.71, blue:0.88, alpha:1.0)
and...
overlay = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5)
According to everywhere on the internet (links to follow) the blend function I'm using above is the same as a Normal blend mode in Photoshop, Pixelmator, Sketch, etc... this should result in the colour...
col = UIColor(red:0.75, green:0.85, blue:0.95, alpha:1.00)
Which is a bright blue colour. However, what I'm getting is...
col = UIColor(red:0.51, green:0.61, blue:0.69, alpha:1.00)
Which is a murky grey colour.
You can see what it should look like here... https://thebookofshaders.com/glossary/?search=mix
If you enter the code...
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
vec4 colorA = vec4(0.5, 0.7, 0.9, 1.0);
vec4 colorB = vec4(1.0, 1.0, 1.0, 0.5);
void main() {
vec4 color = vec4(0.0);
// Mix uses pct (a value from 0-1) to
// mix the two colors
color = mix(colorA, colorB, colorB.a);
color.a = 1.0;
gl_FragColor = color;
}
The ideal output colour looks like this...
But it looks like this...
I’m gonna investigate this more tomorrow. I wish I knew why the output was completely different than everywhere else says it should be.
OK, thanks to #Reaper's comment I started out on a full investigation of what was going wrong with this.
When mixing the hard coded colours in our shader the colour was actually correct.
So it lead me to look at the texture images we were using.
The one that had the white with 50% alpha was definitely white. (not grey).
But... (quirk in the system?) open GL was picking it up as 50% grey with 50% alpha.
And so the mix wasn't actually mixing the correct colours in the first place.
We fixed this by using a 100% alpha image for the colours and a 100% image for the alpha map (black -> white).
By doing this it fixed the problem we were having.

WebGL: Beveled smooth anti-aliased circles using GL_POINT? [duplicate]

This question already has an answer here:
Alpha rendering difference between OpenGL and WebGL
(1 answer)
Closed 5 years ago.
Sorry for such a long title.
This is a followup on my old question, which led me to drawing a beveled circle looking like this:
If you look closely at this image, you can see the pixelation around the edge.
I wanted to smooth this circle with anti-aliasing, and I ended up with this:
If you look closely at this image, you can see a white border around the edge.
I'd like to remove this white border if possible, but my shader is a mess at this point:
#extension GL_OES_standard_derivatives : enable
void main(void) {
lowp vec2 cxy = 2.0 * gl_PointCoord - 1.0;
lowp float radius = dot(cxy, cxy);
const lowp vec3 ambient = vec3(0.5, 0.2, 0.1);
const lowp vec3 lightDiffuse = vec3(1, 0.5, 0.2);
lowp vec3 normal = vec3(cxy, sqrt(1.0 - radius));
lowp vec3 lightDir = normalize(vec3(0, -1, -0.5));
lowp float color = max(dot(normal, lightDir), 0.0);
lowp float delta = fwidth(radius);
lowp float alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, radius);
gl_FragColor = vec4(ambient + lightDiffuse * color, alpha);
}
If anyone could figure out how to remove the white border, that'd be fantastic!
In WebGL the default is to use pre-multiplied alpha. (see WebGL and Alpha)
When initializing the canvas, I has to be specified no alpha:
gl = document.getElementById("canvas").getContext("webgl", { alpha: false });
see further the answers to the questions:
WebGL: How to correctly blend alpha channel png
Alpha rendering difference between OpenGL and WebGL

How can I set webgl opacity without losing png transparency?

I use webgl to draw 2d pictures, with two triangles, faster than with canvas 2d context. I keep things very simple, because I only use it to draw pictures, no 3d.
Now I am trying to add opacity to my fragment shader, because webgl context don't have .globalalpha property.
Here is my code :
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
varying vec2 v_texCoord;
uniform float alpha;
void main()
{
gl_FragColor = vec4(texture2D(u_image, vec2(v_texCoord.s, v_texCoord.t)).rgb, alpha);
}
</script>
...
webglalpha = gl.getUniformLocation(gl.program, "alpha");
...
gl.uniform1f(webglalpha, d_opacity);
d_opacity is the opacity value my drawing function gets as an argument.
The opacity changes, but now my transparent pngs get a black background, though the png transparency used to work before, when I was still using
gl_FragColor = texture2D(u_image, vec2(v_texCoord.s, v_texCoord.t));
How can I set opacity without losing the pngs transparency ?
You need to multiply the overriding alpha by the original alpha in the texture:
void main()
{
vec4 color = texture2D(u_image, vec2(v_texCoord.s, v_texCoord.t));
gl_FragColor = vec4(color.rgb, alpha * color.a);
}
For example, when texel A has a PNG opacity of 0.5 and texel B a PNG opacity of 0.3, you want these to become 0.25 and 0.15 respectively when you set your global alpha to 0.5 and not both 0.5.

OpenGL ES Green Screen... But I want to use black

This works great for green screen, my background is green and using this code it makes green = alpha.
lowp vec4 textureColor = texture2D(u_samplers2D[0], vTextu);
lowp float rbAverage = textureColor.r * 0.5 + textureColor.b * 0.5;
lowp float gDelta = textureColor.g - rbAverage;
textureColor.a = 1.0 - smoothstep(0.0, 0.25, gDelta);
textureColor.a = textureColor.a * textureColor.a * textureColor.a;
gl_FragColor = textureColor;
How do I change the code so that it use a black background instead of green? I'm thinking I could get values for the dark red, green, blues then use that as the alpha? Any pointers would be kind.
You can calculate how close value to black, simplest way is to take maximum of the r g and b values, then set some threshold when you consider color opaque, and map your alpha values to [0..1] in pseudo code:
float brightness = max(textureColor.r, textureColor.g, textureColor.b);
float threshold = 0.1;
float alpha = brightness > threshold ? 1 : (threshold - brightness) / threshold;
gl_FragColor = vec4(textureColor.rgb, alpha);
lowp vec4 textureColor = texture2D(u_samplers2D, vTextu);
lowp float gtemp = smoothstep(0.0, 0.5, textureColor.r);
gl_FragColor = vec4(1.0, 1.0, 1.0, gtemp);
This works for my situation. I was using a greyscale image with black background and just needed the white elements. The problem I had was that I was applying the alpha to grey pixels, but now Im using white and applying the alpha to that. I can adjust the smoothstep values to change the effect. And also add another alpha to fade in or out the image.

Opengles fragment shader achieve the effect

I want to achieve a smooth merge effect of the image on center cut. The centre cut i achieved from the below code.
varying highp vec2 textureCoordinate;
uniform sampler2D videoFrame;
void main(){
vec4 CurrentColor = vec4(0.0);
if(textureCoordinate.y < 0.5){
CurrentColor = texture2D(videoFrame,vec2(textureCoordinate.x,(textureCoordinate.y-0.125)));
} else{
CurrentColor = texture2D(videoFrame,vec2(textureCoordinate.x,(textureCoordinate.y+0.125)));
}
gl_fragColor = CurrentColor;
}
The above code gives the effect to below image.
Actual:
Centre cut:
Desired Output:
What i want is the sharp cut should not be there, there should be smooth gradient merge of both halves.
Do you want an actual blur there, or just linear blend? Because blurring involves a blurring kernel, whereas a blend would be simple interpolation between those two, depending on the y-coordinate.
This is the code for a linear blend.
varying highp vec2 textureCoordinate;
uniform sampler2D videoFrame;
void main(){
float steepness = 20; /* controls the width of the blending zone, larger values => shaper gradient */
vec4 a = texture2D(videoFrame,vec2(textureCoordinate.x,(textureCoordinate.y-0.125)));
vec4 b = texture2D(videoFrame,vec2(textureCoordinate.x,(textureCoordinate.y+0.125)));
/* EDIT: Added a clamp to the smoothstep parameter -- should not be neccessary though */
vec4 final = smoothstep(a, b, clamp((y-0.5)*steepness, 0., 1.)); /* there's also mix instead of smoothstep, try both */
gl_FragColor = final;
}
Doing an actual blur is a bit more complicated, as you've to apply that blurring kernel. Basically it involves two nested loops, iterating over the neighbouring texels and summing them up according to some distribution (most flexible by supplying that distribution through an additional texture which also allowed to add some bokeh).

Resources