2

I am experiencing an issue on Android with OpenGL ES 3.1. I wrote an application that shows a liquid falling down from the top of the screen. This liquid is made of many particles that are a bit transparent, but the color using alpha blending are displayed differently on another phone.

The color of every drop is defined as follow:

private int particleColor = Color.argb(50, 0, 172, 231);

Each particle color is stored in a buffer:

private ByteBuffer colorBuffer = ByteBuffer.allocateDirect(MAX_PARTICLES * PARTICLE_COLOR_COUNT).order(ByteOrder.nativeOrder());

And this buffer is passed to the OpenGL entity to be drawn:

/**
* Binds the data to OpenGL.
* @param program as the program used for the binding.
* @param positionBuffer as the position buffer.
* @param colorBuffer as the color buffer.
*/
public void bindData(LiquidShaderProgram program, ByteBuffer positionBuffer, ByteBuffer colorBuffer){

glVertexAttribPointer(program.getPositionAttributeLocation(), POSITION_COMPONENT_COUNT, GL_FLOAT, false, POSITION_COMPONENT_COUNT * OpenGLUtils.BYTE_PER_FLOAT, positionBuffer);

glEnableVertexAttribArray(program.getPositionAttributeLocation());
glVertexAttribPointer(program.getColorAttributeLocation(), COLOR_COMPONENT_COUNT, GL_UNSIGNED_BYTE, true, COLOR_COMPONENT_COUNT, colorBuffer);

glEnableVertexAttribArray(program.getColorAttributeLocation());
}

The rendering is down by calling this function:

/**
* Render the liquid.
* @param particleCount as the particle count.
*/
public void draw(int particleCount) {
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDrawArrays(GL_POINTS, 0, particleCount);
  glDisable(GL_BLEND);
}

The fragment shader just draw the color that it receives:

precision mediump float;

varying vec4 v_Color;

void main() {
  if (length(gl_PointCoord - vec2(0.5)) > 0.5) {
    discard;
  } else {
    gl_FragColor = v_Color;
  }
}

It works very well on one phone (Nexus 5X): Correct rendering

But on another phone (Galaxy S10), with the exact same code, the color is not the same: Incorrect rendering

Does anyone has any idea about a way to solve this issue? I would like to display the correct color on the second phone as well.

Shibakaneki
  • 213
  • 3
  • 12
  • Can you confirm that `COLOR_COMPONENT_COUNT` is 4 and maybe post the code that fills `colorBuffer`? Also, is `getColorAttributeLocation` returning something plausible on the problem device? – Columbo Jun 05 '19 at 09:15
  • Thank you for your answer, COLOR_COMPONENT_COUNT is indeed 4, getColorAttributeLocation returns something plausible. I just found the solution and I will post it now, it had to do with premultiplication of the alpha! – Shibakaneki Jun 05 '19 at 13:12

1 Answers1

0

I finally understood the issue and found the solution, after reading A LOT of documentation and browsing the web for many hours.

It looks like, on Android, the alpha premultiplication is managed by the framework while OpenGL and the native code have to manage it manually ( I still don't understand why it worked correctly on some phone and not on other phones ). But after changing the blending function and performing alpha premultiplication in the fragment shader, the problem was fixed.

The correct blending function to use is the following one:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

Using this function ensure that the colors are the same on all phones, but that's not enough because the colors are not correct. To make it perfect, alpha premultiplication must be performed in the fragment shader:

gl_FragColor = vec4(v_Color.x * v_Color.w, v_Color.y * v_Color.w, v_Color.z * v_Color.w, v_Color.w);

After doing that, I was able to display the correct colors on all phones.

That's my understanding of the problem and the fix I found. If someone has a better explanation, I would be happy to hear it, otherwise I hope it will help someone.

Shibakaneki
  • 213
  • 3
  • 12