I'm attempting to write a shader to put a border around text. It compiles and runs ok, but the border is grainy and aliased. Any suggestions for making it better please?
This is the first shader I have written so don't have much knowledge about the API. Could any example code be commented please? I don't want to just copy and paste an answer; I'd like to understand it.
Additionally, I have a problem when I make the text smaller which is best explained by this image. Any suggestions on fixing this too please?
The edge detection could be better too. So far I take advantage of the fact that the antialiased texture I use for input has edges with 0.0 < alpha < 1.0
So far I have this:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform vec4 u_borderColor;
uniform float u_width;
uniform float u_height;
void main()
{
mediump vec4 total = vec4(0.0);
float alpha = texture2D(u_texture, v_texCoord).a;
//If somewhere between complete transparent and completely opaque
if (alpha > 0.0 && alpha < 1.0)
{
total.rgb = u_borderColor.rgb;
total.a *= u_borderColor.a;
gl_FragColor = u_borderColor;
}
else
{
gl_FragColor = texture2D(u_texture, v_texCoord);
}
}
Why aren't you setting the value of gl_FragColor to total? Alpha is no more maintained correctly....
Next, total.a = 1 - alpha; (from outer to inner)
or a variant like:
total.a = abs(0.5 - alpha); (blend both sides)
would probably make more sense, when blending.
In the code i see that your setting gl_FragColor = u_borderColor which is (0,0,0,0) from total if alpha is >0 <1. Firstly change your total to some other contrast color and your code work on for a image. but text has different bends which makes it grainy so need to change it the rendering which cannot be said without code.
Do suggest if i am wrong
Thank you
Related
I am currently using this fragment shader in WebGL to apply highlights/shadows adjustments to photo textures.
The shader itself was pulled directly from the excellent GPUImage library for iOS.
uniform sampler2D inputImageTexture;
varying highp vec2 textureCoordinate;
uniform lowp float shadows;
uniform lowp float highlights;
const mediump vec3 luminanceWeighting = vec3(0.3, 0.3, 0.3);
void main()
{
lowp vec4 source = texture2D(inputImageTexture, textureCoordinate);
mediump float luminance = dot(source.rgb, luminanceWeighting);
mediump float shadow = clamp((pow(luminance, 1.0/(shadows+1.0)) + (-0.76)*pow(luminance, 2.0/(shadows+1.0))) - luminance, 0.0, 1.0);
mediump float highlight = clamp((1.0 - (pow(1.0-luminance, 1.0/(2.0-highlights)) + (-0.8)*pow(1.0-luminance, 2.0/(2.0-highlights)))) - luminance, -1.0, 0.0);
lowp vec3 result = vec3(0.0, 0.0, 0.0) + ((luminance + shadow + highlight) - 0.0) * ((source.rgb - vec3(0.0, 0.0, 0.0))/(luminance - 0.0));
gl_FragColor = vec4(result.rgb, source.a);
}
This shader as it stands, will only reduce highlights on a scale of 0.0 - 1.0. However I would like it to also brighten the highlights on a scale of 1.0-2.0.
With the aim of having a complete filter that reduces the images highlights when the highlights uniform is less than 1.0 and increases the intensity of the highlights when it is above 1.0. The same goes for the darkness shadows uniform
Highlights:
0.0(duller) ---- 1.0 (default - original pixel values) ----- 2.0 (brighter)
I have tried simply changing the clamp on the highlights variable to 0.0,2.0, and although this does indeed increase the brightness of the highlights when the uniform is above 1.0 it also seriously messes up the colors.
My understanding of image processing and constructing fragment shaders is extremely weak at best as you my be able to tell.
I'm just hoping someone can point me in the right direction.
EDIT:
Here are some example screenshots:-
The current filter with highlights set to 1.00 (basically the source image)
The current filter with highlights set to 0.00 as you can see the highlights get flattened/removed.
And finally here is what happens when I change the clamp in the fragment shader to allow values above 1.00 and set the highlights value to 2.00
I simply wish to be able to boost the highlights, making them brighter/more defined. i.e. the opposite of setting the value to 0.00
I don't really understand the shadow and highlight equations, but I can see that they are set up to never enhance shadows and highlights, but rather to wash them out. So we need a secondary step for enhancement.
For the highlights, I think to handle brighter colors, you need to blend towards white instead of adding something, so you don't get hue-shifts. I used a basic contrast equation to pick out the highlights, and then cubed it to clip out the midtones and shadows. The whiteTarget is just pulling out the top half of the 0.0-2.0 range to use as a multiplier to determine the strength of the brightening effect.
For the shadows, we are changing our range from 0.0-1.0 (where 0 is unchanged and 1 is washed out) to 0.0-2.0 (where 1 is unchanged and 2 is washed out). Therefore, the +1.0's in the shadow equation should be removed. Then for the 0.0-1.0 range, I just copied what I did for the highlights, except blending toward black. Maybe that can be optimized to avoid a mix function (not sure).
So here is my unoptimized version of the shader, set up so both shadows and highlights are on 0.0-2.0 scales, with 1.0 being the nominal. You might want to play around with those lines where I cube the luminance, and also with the value I used for contrast (currently 1.5), but it seems pretty good to me the way it is now--I adjusted it to try to avoid any ugly overlap between shadows and highlight ranges when the input parameters are at the two extremes.
uniform sampler2D inputImageTexture;
varying highp vec2 textureCoordinate;
uniform lowp float shadows;
uniform lowp float highlights;
const mediump vec3 luminanceWeighting = vec3(0.3, 0.3, 0.3);
void main()
{
lowp vec4 source = texture2D(inputImageTexture, textureCoordinate);
mediump float luminance = dot(source.rgb, luminanceWeighting);
//(shadows+1.0) changed to just shadows:
mediump float shadow = clamp((pow(luminance, 1.0/shadows) + (-0.76)*pow(luminance, 2.0/shadows)) - luminance, 0.0, 1.0);
mediump float highlight = clamp((1.0 - (pow(1.0-luminance, 1.0/(2.0-highlights)) + (-0.8)*pow(1.0-luminance, 2.0/(2.0-highlights)))) - luminance, -1.0, 0.0);
lowp vec3 result = vec3(0.0, 0.0, 0.0) + ((luminance + shadow + highlight) - 0.0) * ((source.rgb - vec3(0.0, 0.0, 0.0))/(luminance - 0.0));
// blend toward white if highlights is more than 1
mediump float contrastedLuminance = ((luminance - 0.5) * 1.5) + 0.5;
mediump float whiteInterp = contrastedLuminance*contrastedLuminance*contrastedLuminance;
mediump float whiteTarget = clamp(highlights, 1.0, 2.0) - 1.0;
result = mix(result, vec3(1.0), whiteInterp*whiteTarget);
// blend toward black if shadows is less than 1
mediump float invContrastedLuminance = 1.0 - contrastedLuminance;
mediump float blackInterp = invContrastedLuminance*invContrastedLuminance*invContrastedLuminance;
mediump float blackTarget = 1.0 - clamp(shadows, 0.0, 1.0);
result = mix(result, vec3(0.0), blackInterp*blackTarget);
gl_FragColor = vec4(result, source.a);
}
By the way, any idea why the original result line keeps adding 0's to everything? Seems like it could be simplified to
vec3 result = (luminance + shadow + highlight) * source.rgb / luminance;
But maybe it's a trick to cast to lowp within the calculation instead of after the calculation. Just a guess.
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.
I have a requirement to implement an iOS UIImage filter / effect which is a copy of Photoshop's Distort Wave effect. The wave has to have multiple generators and repeat in a tight pattern within a CGRect.
Photos of steps are attached.
I'm having problems creating the glsl code to reproduce the sine wave pattern. I'm also trying to smooth the edge of the effect so that the transition to the area outside the rect is not so abrupt.
I found some WebGL code that produces a water ripple. The waves produced before the center point look close to what I need, but I can't seem to get the math right to remove the water ripple (at center point) and just keep the repeating sine pattern before it:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float time;
uniform highp vec2 center;
uniform highp float angle;
void main() {
highp vec2 cPos = -1.0 + 2.0 * gl_FragCoord.xy / center.xy;
highp float cLength = length(cPos);
highp vec2 uv = gl_FragCoord.xy/center.xy+(cPos/cLength)*cos(cLength*12.0-time*4.0)*0.03;
highp vec3 col = texture2D(inputImageTexture,uv).xyz;
gl_FragColor = vec4(col,1.0);
}
I have to process two Rect areas, one at top and one at the bottom. So being able to process two Rect areas in one pass would be ideal. Plus the edge smoothing.
Thanks in advance for any help.
I've handled this in the past by generating an offset table on the CPU and uploading it as an input texture. So on the CPU, I'd do something like:
for (i = 0; i < tableSize; i++)
{
table [ i ].x = amplitude * sin (i * frequency * 2.0 * M_PI / tableSize + phase);
table [ i ].y = 0.0;
}
You may need to add in more sine waves if you have multiple "generators". Also, note that the above code offsets the x coordinate of each pixel. You could do Y instead, or both, depending on what you need.
Then in the glsl, I'd use that table as an offset for sampling. So it would be something like this:
uniform sampler2DRect table;
uniform sampler2DRect inputImage;
//... rest of your code ...
// Get the offset from the table
vec2 coord = glTexCoord [ 0 ].xy;
vec2 newCoord = coord + texture2DRect (table, coord);
// Sample the input image at the offset coordinate
gl_FragColor = texture2DRect (inputImage, newCoord);
I'm trying to combine two texture using shaders in opengl es 2.0
as you can see on the screen shot, I am trying to create a needle reflection on backward object using dynamic environment mapping.
but, reflection of the needle looks semi transparent and it's blend with my environment map.
here is the my fragment shader;
varying highp vec4 R;
uniform samplerCube cube_map1;
uniform samplerCube cube_map2;
void main()
{
mediump vec3 output_color1;
mediump vec3 output_color2;
output_color1 = textureCube(cube_map1 , R.xyz).rgb;
output_color2 = textureCube(cube_map2 , R.xyz).rgb;
gl_FragColor = mix(vec4(output_color1,1.0),vec4(output_color2,1.0),0.5);
}
but, "mix" method cause a blending two textures.
I'm also checked Texture Combiners examples but it didn't help either.
is there any way to combine two textures without blend each other.
thanks.
Judging from the comments, my guess is you want to draw the needle on top of the landscape picture. I'd simply render it as an overlay but since you want to do it in a shader maybe this would work:
void main()
{
mediump vec3 output_color1;
mediump vec3 output_color2;
output_color1 = textureCube(cube_map1 , R.xyz).rgb;
output_color2 = textureCube(cube_map2 , R.xyz).rgb;
if ( length( output_color1 ) > 0.0 )
gl_FragColor = vec4(output_color1,1.0);
else
gl_FragColor = vec4(output_color2,1.0);
}
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).