Translucent sprite in opengl es 2.0 using shader - iphone

I'm trying to create an shader that does the same thing as glcolor4f and then the alpha part of it. In opengl es 1.1 if you set the alpha to say 0.5 the sprite would be half translucent.
Now i can't seem to get the effect using an shader, this is how my shader looks like now:
gl_FragColor = texture2d(texture, coord) * blend;
And using this blend mode:
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
But that doesn't work, it does change the color of an sprite but not the translucency. What am i missing?
Thanks for your time,
Richard.

It seems that you are scaling the color you get from the texture by the blend factor, which is not how alpha is performed (this would just make it darker).
I believe you need something along the lines of the following
gl_FragColor = vec4(texture2d(texture, coord).rgb, blend);
See if that works

Related

How to remove anti-aliasing artifacts inside the object's material HLSL shader (Unity)

I am trying to create a shader that would blend a background's texture with the 3d object's texture. The background texture is a render texture from another camera. I pass it to the shader of the foreground object.
The idea is to achieve an effect of various blendings on the 3d object "in the front".
The fragment shder looks like this:
fixed4 frag(v2f i) : SV_Target
{
float4 color_from_background = tex2D(_BackTex, float2(i.pos.x / _ScreenParams.x, i.pos.y / _ScreenParams.y)); //A
fixed4 surfcol = tex2D(_MainTex, i.uv);
fixed4 c = BlendMode_Multiply(color_from_background,fixed4(surfcol.rgb,surfcol.a * _Transparency));
return c;
}
It kind of works...
Unfortunately due to imprecisions in the division in the line marked with "//A" - I get visible artifacts along the object's edges. In this line I sample the background texture for the blending's input, How to solve that? First thing that comes to my mind is anti-aliasing. But I dont know how to implement it here. I tried to change sampling levels and filtering of the textures but it does not solve the problem.
I attach image that shows the problem. At the top part you can see the artifacts when using the shader. At the bottom the actual object with non-opaque material.
The solution to this is disabling the anti-aliasing in the project settings globally OR in the target render texture settings and camera settings. I only had it off in the target render texture which was not enough

Toon shader shadow

I'm currently trying to change my asset style from realistic to low poly / cartoonic.
For example, I have a toon surface shader
half4 LightingRamp(SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot(s.Normal, lightDir);
half diff = NdotL * 0.5 + 0.5;
half3 ramp = tex2D(_LightingTex, float2(diff, 0)).rgb;
half4 c;
c.rgb = _LightColor0.rgb * atten * ramp *_Color;
c.a = s.Alpha;
return c;
}
where _LightingTex is a 2D texture ramp. This works fine for lighting effects on the objects themselfs.
As I have multiple objects with this shader in my scene, some of them are casting a shadow onto my wall.
As you can see, the shadow here is not a ramp but a continuous gradient, as it is (probably) done in some sort of ambient from unity. My question is now: is there an option to create this colorramp effect on the global shadows as well? Something like this:
Can I do it material shader based, or is it a post processing effect?
Thanks
With surface shaders: No, you can't do it in the shader. Actually, I think the best way to get a unified cartoon effect is to use a color grading LUT as a post effect. The great thing about LUTs is that you can create one easily in photoshop by first applying some cool effects to a regular image until it looks the way you want (such as "Posterize"), and then copy the effect stack to apply to a LUT texture, like this one. When you use this LUT in Unity, everything will look as they would with your Photoshop filters applied. One small caveat I've noticed though is that some standard LUT textures need to be flipped vertically to work with the Post Processing Stack. Here is a nice tutorial on how to create posterized LUTs.
If you want to get the toon-like shadows directly in the shader, it is not any harder than making a regular forward rendered vertex/fragment shader, though this by itself requires a bit of knowledge on how these work - i can recommend looking at the standard shader source code, this, or this (somewhat outdated) tutorial. You can find the details surrounding how to add shadow support from my post here. The only thing you need to change is to add a similar color ramp to the shadow mask:
half shadow = SHADOW_ATTENUATION(IN)
shadow = tex2D(_ShadowRamp, float2(shadow, 0));
For this, you can set the shadow ramp as a global shader variable from script, so you won't have to assign it for each material.

How can I get the lighting information from a skybox?

I am freshman to the Unity3d shader. I am writing a custome unlit shader now. I know I can use _WorldSpaceLightPos0.xyz and _LightColor0.rgb to get the information of a directional light source.
However, if I have a skybox instead of a light source, how can I get the light information? The radiance(Li) is coming from my skybox now. How can I get the value of them and compute Li*my_custome_brdf?
Thanks
The skybox does not have a light position since it is omnidirectional, so you can't calculate proper directional lighting from it (only ambient). However, if you just want the skybox color in any direction, there's unity_SpecCube0, unity_SpecCube1, etc. for reflection probes listed by weight. You can use something like this:
inline half3 SurfaceReflection(half3 viewDir, half3 worldNormal, half roughness) {
half3 worldRefl = reflect(-viewDir, worldNormal);
half r = roughness * 1.7 - 0.7 * roughness; // Some magic for calculating MIP level
float4 reflData = UNITY_SAMPLE_TEXCUBE_LOD(
unity_SpecCube0, worldRefl, r * 6
);
return DecodeHDR (reflData, unity_SpecCube0_HDR);
}
If your reflection probe is different from your skybox, just define the skybox as a texCUBE uniform in your shader instead and use that. If you don't want to have to set it for each material using this shader, just declare it in the CGPROGRAM block as a uniform, but not as a ShaderLab property - it will then be considered a shader global. You can then set it through script using Shader.SetGlobalTexture. Check out the documentation for this here:
https://docs.unity3d.com/ScriptReference/Shader.SetGlobalTexture.html

iPhone OpenGL ES 2.0 blending with Cocos2D gives unexpected results

I have very simple CCScene with ONLY 1 CCLayer containing:
CCSprite for background with standard blending mode
CCRenderTexture to draw paint brushes, with its sprite attached to root CCLayer above background sprite:
_bgSprite = [CCSprite spriteWithFile:backgroundPath];
_renderTexture = [CCRenderTexture renderTextureWithWidth:self.contentSize.width height:self.contentSize.height];
[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}];
[self addChild:_bgSprite z:-100];
[self addChild:_renderTexture];
Brush rendering code:
[_renderTexture begin];
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); // 1.
// calculate vertices code,etc...
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)count);
[_renderTexture end];
When user brushes with first colored brush, it blends with background as expected.
But when when continues brushing with another color on top of the previous brush, it goes wrong (soft alpha edges loses opacity when 2 brushes overlap each other):
I tried many blending options but somehow I cannot find correct one.
Is there something special about CCRenderTexture that it does not blend with itself (with previously drawn content) as expected?
My fragment shader used for brushing is just standard texture shader with minor change to preserve input color alpha in texture:
void main()
{
gl_FragColor = texture2D(u_texture, v_texCoord);
gl_FragColor.a = v_fragmentColor.a;
}
UPDATE - ALMOST PERFECT SOLUTION : by jozxyqk
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
in rendering code (in place of // 1. and
[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];
THIS WORKS GREAT AND GIVES ME WHAT I WANT...
...BUT ONLY WHEN _rederTexture is in full opacity.
When opacity of _rendertexture.sprite is lowered, brushes get lightened up instead of fading out as one could expect:
Why alphas of the brushes are blending with background correctly when parent texture is in full opacity but go bananas when opacity is lowered? How can I make brushes to blend with background correctly?
EDIT
Blending brush -> layer -> background
OK, what's happening is glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) is working for blending the brush strokes into the brush texture, but the resulting alpha values in the texture are wrong. Each added fragment needs to 1. add it's alpha to the final alpha value - it has to remove exactly that much light for the interaction and 2. scale the previous alpha by the remainder - previous surfaces reduce the light by the previous value, but since a new surface is added there is less light for them to reduce. I'm not sure if that made sense but it leads to this...
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Now the colour channel of the brush texture contains the total colour to be blended with the background (pre-multiplied with alpha) and the alpha channel gives the weight (or the amount the colour obscures the background). Since the colour is pre-multiplied with alpha, the default RenderTexture blending GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA scales with alpha again and hence darkens the overall colour. You now need to blend the brush texture with the background using the following function, which I gather must be set in Cocos2D:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Hopefully this is possible. I haven't given a lot of thought on how to manage the possibility of setting up the brush texture to blend with GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA but it may require a floating point texture and/or an extra pass to divide/normalize the alpha, which sounds painful.
Alternatively, splat the background into your render texture before drawing and keep the lot there without any blending of layers.
This worked for me:
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
fbo.bind();
glClear(GL_COLOR_BUFFER_BIT);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(brush1);
drawTexture(brush2);
fbo.unbind();
drawTexture(grassTex); //tex alpha is 1.0, so blending doesn't affect background
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(fbo.getColour(0)); //blend in the brush layer
Brush layer opacity
Using GL_ONE, GL_ONE_MINUS_SRC_ALPHA causes issues with the library's implementation of opacity in layer blending since it assumes the colour is multiplied by alpha. By reducing the opacity value, the alpha of the brush layer is scaled down during blending. GL_ONE_MINUS_SRC_ALPHA then causes the amount of background colour to increase, however GL_ONE sums 100% of the brush layer and oversaturates the image.
The simplest solution imo is to find a way to scale down the colour by the global layer opacity yourself and continue to use GL_ONE, GL_ONE_MINUS_SRC_ALPHA.
Actually using GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA might be an answer if the library supported it, but apparently it doesn't.
You could use fixed pipeline rendering to scale the colour: glColor4f(opacity, opacity, opacity, opacity), but this will require a second render target and doing the blend manually, similarly to the code above, where you draw a full screen quad once for the background and again for the brush layer.
If you're doing the blend manually it would be more robust to use a fragment shader instead of the glColor method. This would allow far greater control if you ever wanted to play with more complex blending functions, especially where divisions and temporaries outside the 0 to 1 range are concerned:
gl_FragColour = texture(brushTexture, coord) * layerOpacity;
END EDIT
The standard alpha blending function is glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);, not quite the GL "initial"/default function.
Summing alpha values as you do in glBlendFuncSeparate will oversaturate alpha and the underneath colour is completely replaced. Saturation blending may give decent results: glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE). It might also be worth experimenting with glBlendEquationSeparate and MAX blending, if it's supported. The advantage of playing with MAX would be reducing the overlapping artefacts (hard triangular bits) from your line drawing code - eg replace colour, but only until total alpha value X is reached. EDIT: Both cases will require blending and clearing after each stroke.
I can only assume blending the render texture onto the background is in fact working. (not for the current layer values)
On a side note and largely unrelated there's also "Under Blending", where you keep a transmittance value instead of alpha/opacity (from here):
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);

OpenGL texture blending problems

I'm creating a 2d application for the iPad using OpenGL ES and having some issue drawing transparent images.
I'm using png-24 images with full transparency. I'm also changing the color of some of some textures, which are white with some areas transparent or semi-transparent. That all works fine.
When I try to set the alpha value of one of these textures, however, it's not working quite right. The colors are much too saturated, and if the alpha value = 0, i'm left with a white rather than transparent image over a light grey background. When such a transparent image is over a dark image, the dark image becomes a color similar to color of the transparent image.
I've tried a many parameter combinations of the glTexEnvi and glBlendFunc with no success.
I'm not very knowledgable about OpenGL, so if anyone has any suggestions, that would be great. Let me know if there are any details that would help.
Thanks.
Here is the initialization of OpenGL
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glDisable(GL_DEPTH_TEST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_BLEND);
Sounds like you told OpenGL your texture had premultiplied alpha, but it actually doesn't.
What parameters are you using for glBlendFunc?
More explanation of pre-multiplied alpha
Thanks for everyone's input. I think I've found a solution.
The problem was that the textures had premultiplied alpha but the polygons the textures are being applied to did not. With glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA); and glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); in place all I had to do was manually multiply the R, G, and B color values of the polygons by the alpha value so that everything is essentially using premultiplied alpha and renders consistently.
I'm not sure if this is the most efficient solution, but it seems to work perfectly so far and it was just a matter of adding a few lines of code to my image rendering class to update the color values whenever the opacity is changed.
wooo
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ALPHA);
This is not valid, are you checking GL errors? Check the glTexEnv man page to see the valid values:
http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml
GL_ALPHA is not a texture function, the valid texture functions are GL_ADD, GL_MODULATE, GL_DECAL, GL_BLEND, GL_REPLACE, and GL_COMBINE.
You might want to replace that GL_ALPHA with GL_MODULATE so you can control the texture alpha with the vertex color (glColor). This mode multiplies the texture with the vertex color.
Chances are if you're using a 24-bit PNG, you wont have the alpha channel. Try converting to a 32-bit PNG and editing the alpha channel. Alternatively you can convert from 24bit to 32bit yourself and generate the alpha channel from the image data (eg: by chroma-keying).
Then use glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); or glBlendFunc(GL_SRC_ALPHA, GL_ONE); if you want additive blending.