To preface this: Yes, I have looked at the large amount of "Android OpenGL ES 2.0 black textures" questions asked previously on this site. No, none of them were helpful to my situation. No, I'm not sure if I can phrase the title any better in an appropriate amount of characters.
I followed a number of tutorials and was able to set up a very simple renderer class that properly loaded and rendered textures (project A). I then tried implementing this very simple rendering system in a game engine (project B). Everything works the exact same, except texture2D() returns black for some reason. I have tried much debugging and much googling all to no avail. And so I ask for assistance.
My vertex and fragment shaders. They worked just fine in project A, so I don't think this is the source of the problem, just including for the sake of completeness.
private static final String VERTEX_SHADER_SOURCE =
...
"attribute vec2 a_TexCoord;" +
"varying vec2 v_TexCoord;" +
"void main() {" +
" v_TexCoord = a_TexCoord;" +
...
"}";
private static final String FRAGMENT_SHADER_SOURCE =
"precision mediump float;" +
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoord;" +
"void main() {" +
" gl_FragColor = texture2D(u_Texture, v_TexCoord);" +
"}";
I create, compile, and attach these shaders to the program without error. After this, I set my handles accordingly - I also set u_Texture to point to texture unit 0 because this does not change:
...
sTexUniformHandle = GLES20.glGetUniformLocation(sProgramHandle, "u_Texture");
sMVPHandle = GLES20.glGetUniformLocation(sProgramHandle, "u_MVPMatrix");
sPositionHandle = GLES20.glGetAttribLocation(sProgramHandle, "a_Position");
sTexCoordHandle = GLES20.glGetAttribLocation(sProgramHandle, "a_TexCoord");
GLES20.glUseProgram(sProgramHandle);
GLES20.glUniform1i(sTexUniformHandle, 0);
GLES20.glUseProgram(0);
...
I then load my texture:
...
int[] texData = Utils.createTexture(context, resId);
mTexDataHandle = texData[0];
...
public static int[] createTexture(Context context, int resId) { // returns {textureHandle, width, height}
int width = -1;
int height = -1;
int[] texHandle = new int[1];
GLES20.glGenTextures(1, texHandle, 0);
if (texHandle[0] != 0) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = false;
final Bitmap bm = BitmapFactory.decodeResource(context.getResources(), resId, opts);
width = bm.getWidth();
height = bm.getHeight();
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texHandle[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bm, 0);
bm.recycle();
}
if (texHandle[0] == 0) {
throw new RuntimeException("texture load error");
}
return new int[]{texHandle[0], width, height};
}
I did not need to set texture wrapping in project A even though my texture is 32 x 19. I changed my texture to 32 x 32 and set texture wrapping to clamp just as a precautionary measure and so no one would try to tell me that was my error. The bitmap is loading - I wrote width, height, and a few select pixels to debug and they were spot on.
In my draw method, I enable the a_TexCoord attribute and point it to the data:
...
GLES20.glEnableVertexAttribArray(sTexCoordHandle);
...
GLES20.glVertexAttribPointer(sTexCoordHandle, mTexCoordDataSize, GLES20.GL_FLOAT, false, 0, mTexCoordBuffer);
...
I wrote the whole mTexCoordBuffer out to a debug message and it's correctly loaded with the texture coordinate data.
Finally, I set the active texture unit to 0, bind my texture data to it, and draw:
...
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexDataHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
...
I'm very new to the whole graphics library thing, but I've done my best to come to terms with what's actually going on here. I thought I understood it all, however that's obviously not the case. From what I can tell, the shaders are working properly - the black rectangle appears exactly where it should (and did in project A). The texture coordinate data that I'm passing in is the exact same as in project A and I changed nothing with how that is loaded. This leaves the actual texture data as the primary suspect, however I tried to set it up just like I did in project A and it seems correct. I would very much appreciate if someone more experienced than I could point out my mistake.
mTexCoordDataSize? - Reto Koradigl_FragColor = vec4(v_TexCoord, 0.0, 1.0). If this shows a nice green/red gradient, you know that you have valid texture coordinates going into the shader. - Reto KoradiglGetError()calls if you haven't done that yet, particularly aftertexImage2D(). - Reto Koradi