I’m trying to blend a texture with alpha channel onto the framebuffer using the blend function
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
The texture is loaded using texture environment GL_MODULATE. I’m using OpenGL ES 1.1 on Android, on the DROID MOTOROLA and Nexus One phone.
This results in the dark colors where the texture has a semi-transparent alpha (ie. 0< alpha < 1). The resulting color of each pixel roughly corresponds to the alpha channel of the texture having been applied twice to the color of the textures pixel ie
Cout=Ct * At * At + Cd * (1-At), where Ct (At) is color (alpha) of the texture, and Cd is the color of the destination, and Cout is the resulting color.
If I use the blend function
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
I get the behaviour I expect namely that the resulting color is Cout = Ct * At + Cd * (1-At).
If I draw an RGB-texture (ie non-alpha) and sets a half opaque yellow color using glColor4f(1,1,0,.5), then the former blend function
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
correctly blends my background to my half opaque yellow, and the latter blend function gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
correctly draws fully opaque yellow (regardless of the background).
It appears I can get away with using the latter blend function, by applying alpha values to the color component myself when setting colors on vertices and when setting the global glColor. However it frustrates me that I cannot understand whats going on.
According to the red book chapter 9 (Texture Functions) the GL_MODULATE should make the texture map onto the incoming fragment using the function:
C = CfCt,
A = AfAt
I’m not 100% certain what ‘the incoming fragment’ means, but my understanding is that Cf and Af refer to color and alpha of the object I’m texture mapping, and is thus defined by either glColor or the color of the vertices of my object, dependent upon whether i’m using color arrays. So I expect that if I map an RGBA texture on an object with the color 1,1,1,1, and then draw this object onto the screen, then I would get the blending I mentioned in the beginning, and not the dark colors where the texture contains semi-opaque colors.
On the left the texture can be seen. The texture is fully opaque yellow in the center and gradually fades. On the right there is an opengl render example where the texture is rendered on a black,yellow,blue and white square using the blend function GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA.
My Code:
I have the following code:
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
this.w = w;
this.h = h;
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glDisable(GL10.GL_DITHER);
gl.glDisable(GL10.GL_LIGHTING);
gl.glViewport(0, 0, w, h);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0.0f, w, h, 0.0f, 0.0f, 1.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glEnable(GL10.GL_TEXTURE_2D);
starTextureName = loadTexture(gl, R.drawable.yellow_alpha_squares);
redTextureName = loadTexture(gl, R.drawable.red);
greenTextureName = loadTexture(gl, R.drawable.green);
blueTextureName = loadTexture(gl, R.drawable.blue);
whiteTextureName = loadTexture(gl, R.drawable.white);
blackTextureName = loadTexture(gl, R.drawable.black);
yellowTextureName = loadTexture(gl, R.drawable.yellow);
checkNoError(gl);
}
private void checkNoError(GL10 gl) {
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
throw new RuntimeException("GLError: " + error + " (" + GLU.gluErrorString(error) + "): ");
}
}
private int loadTexture(GL10 gl, int resourceId) {
int[] mTextureNameWorkspace = new int[1];
gl.glGenTextures(1, mTextureNameWorkspace, 0);
int textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES,
new int[] { 0, bitmap.getHeight(), bitmap.getWidth(), -bitmap.getHeight() }, 0);
return textureName;
}
public void onDrawFrameSimple(GL10 gl) {
gl.glEnable(GL10.GL_BLEND);
gl.glLoadIdentity();
gl.glClearColor(0, 0, 0, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glColor4f(1, 1, 1, 1);
gl.glBindTexture(GL10.GL_TEXTURE_2D, redTextureName);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glBindTexture(GL10.GL_TEXTURE_2D, starTextureName);
((GL11Ext) gl).glDrawTexfOES(0, 0, 0, w/2,h/2);
}