2

我在使用 OpenGL ES 3.1 的 Android 上遇到问题。我编写了一个应用程序,显示液体从屏幕顶部落下。这种液体由许多有点透明的粒子组成,但使用 alpha 混合的颜色在另一部手机上显示不同。

每一滴的颜色定义如下:

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

每个粒子颜色都存储在缓冲区中:

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

并将这个缓冲区传递给要绘制的 OpenGL 实体:

/**
* 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());
}

通过调用此函数来关闭渲染:

/**
* 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);
}

片段着色器只绘制它接收到的颜色:

precision mediump float;

varying vec4 v_Color;

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

它在一部手机(Nexus 5X)上运行良好: 正确渲染

但是在另一部手机(Galaxy S10)上,使用完全相同的代码,颜色就不一样了: 不正确的渲染

有没有人知道解决这个问题的方法?我也想在第二部手机上显示正确的颜色。

4

1 回答 1

0

在阅读了大量文档并浏览网页数小时后,我终于理解了这个问题并找到了解决方案。

看起来,在 Android 上,alpha 预乘是由框架管理的,而 OpenGL 和本机代码必须手动管理它(我仍然不明白为什么它在某些手机上正常工作而不在其他手机上正常工作)。但是在片段着色器中更改混合函数并执行 alpha 预乘后,问题得到了解决。

要使用的正确混合功能如下:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

使用此功能可确保所有手机上的颜色都相同,但这还不够,因为颜色不正确。为了使其完美,必须在片段着色器中执行 alpha 预乘:

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

之后,我能够在所有手机上显示正确的颜色。

这就是我对问题的理解和我找到的解决方法。如果有人有更好的解释,我会很高兴听到它,否则我希望它会对某人有所帮助。

于 2019-06-05T13:17:48.910 回答