1
\$\begingroup\$

I have two textures, Source image A and Overlay B, B is drawed on top of A, I want to replace the black color in B, then make the region of A visible, what should I do?

This is my shader language:

 varying highp vec2 textureCoordinate;
 varying highp vec2 textureCoordinate2;
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 void main()
 {
 lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
 lowp vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);

 if (overlay.r == 0.0 && overlay.g == 0.0 && overlay.b == 0.0)
 {
     gl_FragColor = base;
 }
 else
 {
     gl_FragColor = overlay;
 }
}

but the result is not right.

This is my overlay

enter image description here

when I mix the source and overlay by using the shader, I got the result like the following, What should I do to make it works right?

enter image description here

\$\endgroup\$
  • \$\begingroup\$ How will you manage the case when the objects in B has black in them, such as the eyes of the sun? \$\endgroup\$ – Andreas Apr 12 '16 at 21:06
2
\$\begingroup\$

There is noise introduced in your texture due to compression which causes some of the pixels to not be perfectly black.

You need to add an error threshold to your comparison, fastest way is to use a distance comparison by considering the RGB color as a 3D vector and use a dot-product to calculate the squared length.

This reduces the multiple comparisons and shortcut boolean operators || which are generally very costly on GPUs.

varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;

const float squared_threshold = (0.1 * 0.1); //threshold value, squared

void main()
{
 lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
 vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);

 float squared_distance = dot(overlay.rgb, overlay.rgb);

 if (squared_distance < squared_threshold)
 {
     gl_FragColor = base;
 }
 else
 {
     gl_FragColor = overlay;
 }
}

if you need to compare to any other colors you can do:

vec3 color_diff = overlay.rgb - erase_color;
float squared_distance = dot(color_diff, color_diff);

if (squared_distance < squared_threshold)

Where erase_color is the color you want to erase, but since we're erasing black ( vec3(0, 0, 0) ) we can eliminate this subtraction.

Using a squared threshold allows us to skip calculating the square root to compare the distance.

\$\endgroup\$
0
\$\begingroup\$

Fastest solution is to change your comparison to account for JPG compression artifacts (that slightly alter pixels colors):

if (overlay.r <= 0.02 && overlay.g <= 0.02 && overlay.b <= 0.02)

Right solution would be to add Alpha to your image and use it for transparency.

\$\endgroup\$
  • \$\begingroup\$ Inside the fragment shaded, if the color is the one you want, then discard the fragment (skmpky use discard()) \$\endgroup\$ – Bálint Apr 8 '16 at 21:57
  • \$\begingroup\$ @Bálint He mixes 2 images, not overlays one over background. \$\endgroup\$ – Kromster says support Monica Apr 9 '16 at 10:12
  • \$\begingroup\$ @Krom Stern, no I want to overlay the image on the other one, and I also want exclude the black color region. \$\endgroup\$ – user2803729 Apr 11 '16 at 15:31
  • \$\begingroup\$ Then discard is the way to go :-) \$\endgroup\$ – Kromster says support Monica Apr 11 '16 at 17:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.