Mixing two sprites preserving alpha channel
Mixing two sprites according to a percentage can be interesting, in order to fade from the first to the second one.
Let’s suppose for now that the two sprites have the same shape.
An initial idea: draw the first sprite with alpha 1, then draw the second sprite above the first one with the desired alpha.
This is not good for sprites with varying alpha channels: because all the pixels are drawn two times, semi transparent areas will be more opaque than they need to be.
So here is a shader that mixes two sprites correctly. It requires the second texture and the opacity parameter (between 0 and 1: 0 for the first texture, 1 for the second one).
Vertex shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
attribute vec3 in_Position; // (x,y,z) attribute vec4 in_Colour; // (r,g,b,a) attribute vec2 in_TextureCoord; // (u,v) varying vec2 v_vTexcoord_A; varying vec2 v_vTexcoord_B; varying vec4 v_vColour; uniform vec4 u_vA; uniform vec4 u_vB; void main() { vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0); gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos; v_vColour = in_Colour; v_vTexcoord_A = in_TextureCoord; v_vTexcoord_B = u_vB.xy + ( in_TextureCoord - u_vA.xy ) * u_vB.zw / u_vA.zw; } |
Fragment shader:
1 2 3 4 5 6 7 8 9 10 |
varying vec2 v_vTexcoord_A; varying vec2 v_vTexcoord_B; varying vec4 v_vColour; uniform sampler2D texture2; uniform float Opacity; void main() { gl_FragColor = texture2D( gm_BaseTexture, v_vTexcoord_A ) * ( 1.0 - Opacity ) + texture2D( texture2, v_vTexcoord_B ) * Opacity; } |
Here is an example code that shows how to use it (Draw event):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
shader_set(shader0); _opa = shader_get_uniform(shader0, "Opacity"); _tex2 = shader_get_sampler_index(shader0,"texture2"); shader_set_uniform_f(_opa ,opacity); _spr = sprite_get_texture(sprite1,0); texture_set_stage(_tex2,_spr); var _uvs_a = sprite_get_uvs( sprite0, 0 ); var _uvs_b = sprite_get_uvs( sprite1, 0 ); shader_set_uniform_f( shader_get_uniform( shader0, "u_vA" ), _uvs_a[0], _uvs_a[1], _uvs_a[2]-_uvs_a[0], _uvs_a[3]-_uvs_a[1] ); shader_set_uniform_f( shader_get_uniform( shader0, "u_vB" ), _uvs_b[0], _uvs_b[1], _uvs_b[2]-_uvs_b[0], _uvs_b[3]-_uvs_b[1] ); draw_sprite_ext(sprite0,0,x,y,1,1,0,c_white,1); shader_reset(); |
Warning: if you have sprites with different shapes, you also need to premultiply alpha for both sprites, then draw using the following blend mode:
1 2 3 |
draw_set_blend_mode_ext(bm_one,bm_inv_src_alpha); draw_sprite_ext(sprite0,0,x,y,1,1,0,c_white,1); draw_set_blend_mode(bm_normal); |