Two textures with different texture coordinates in OpenGL ES 2.0 - android

Brief: when I apply to one fragment shader two textures with two different texture coordinates, I see only first texture. But when I use one texture coordinate for two textures it works fine and I can see both textures.
I work with photo filters and use OpenGL ES 2.0 to make filters. Some filters have an advance texture. First texture is a photo and second is a tracery.
Here is my vertext shader
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
attribute vec4 inputTextureCoordinate2;
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
void main() {
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
textureCoordinate2 = inputTextureCoordinate2.xy;
}
Here is my fragment shader
precision mediump float;
uniform sampler2D inputImageTexture1;
uniform sampler2D inputImageTexture2;
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
void main() {
mediump vec4 color1 = texture2D(inputImageTexture1, textureCoordinate);
mediump vec4 color2 = texture2D(inputImageTexture2, textureCoordinate2);
mediump vec3 colorResult = mix(color1.rgb, color2.rgb, 0.5);
gl_FragColor = vec4(colorResult, 1.0);
}
In my code I use GLSurfaceView.Render implementation.
Initialization of coordinates:
static final float CUBE[] = {-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,};
public static final float COORDINATES1[] = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,};
public static final float COORDINATES2[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,};
...
mGLCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mGLCubeBuffer.put(CUBE).position(0);
mGLTextureCoordinates1 = ByteBuffer.allocateDirect(COORDINATES1.length * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer();
mGLTextureCoordinates1.clear();
mGLTextureCoordinates1.put(COORDINATES1).position(0);
mGLTextureCoordinates2 = ByteBuffer.allocateDirect(COORDINATES2.length * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer();
mGLTextureCoordinates2.clear();
mGLTextureCoordinates1.put(COORDINATES2).position(0);
onSurfaceCreate method:
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_DEPTH_BITS);
String vertexShader = RawResourceReader.readTextFileFromRawResource(mContext, R.raw.test_vertex);
String fragmentShader = RawResourceReader.readTextFileFromRawResource(mContext, R.raw.test_fragment);
mGLProgId = loadProgram(vertexShader, fragmentShader);
mGLAttribPosition = GLES20.glGetAttribLocation(mGLProgId, "position");
mGLAttribTextureCoordinate = GLES20.glGetAttribLocation(mGLProgId, "inputTextureCoordinate");
mGLAttribTextureCoordinate2 = GLES20.glGetAttribLocation(mGLProgId, "inputTextureCoordinate2");
mGLUniformTexture1 = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture1");
mGLUniformTexture2 = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture2");
mTexture1 = loadTexture(mContext, R.drawable.photo);
mTexture2 = loadTexture(mContext, R.drawable.formula1);
}
onDrawFrame method:
#Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(mGLProgId);
mGLCubeBuffer.position(0);
GLES20.glVertexAttribPointer(mGLAttribPosition, 2, GLES20.GL_FLOAT, false, 0, mGLCubeBuffer);
GLES20.glEnableVertexAttribArray(mGLAttribPosition);
//set first coordinates
mGLTextureCoordinates1.position(0);
GLES20.glVertexAttribPointer(mGLAttribTextureCoordinate, 2, GLES20.GL_FLOAT, false, 0, mGLTextureCoordinates1);
GLES20.glEnableVertexAttribArray(mGLAttribTextureCoordinate);
//set second coordinates
mGLTextureCoordinates2.position(0);
GLES20.glVertexAttribPointer(mGLAttribTextureCoordinate2, 2, GLES20.GL_FLOAT, false, 0, mGLTextureCoordinates2);
GLES20.glEnableVertexAttribArray(mGLAttribTextureCoordinate2);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture1);
GLES20.glUniform1i(mGLUniformTexture1, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture2);
GLES20.glUniform1i(mGLUniformTexture2, 1);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(mGLAttribPosition);
GLES20.glDisableVertexAttribArray(mGLAttribTextureCoordinate);
GLES20.glDisableVertexAttribArray(mGLAttribTextureCoordinate2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
Significant part of loadTexture method:
GLES20.glGenTextures(1, textureHandle, 0);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_CONSTANT_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
Note that on iOs it works fine, but there is used some library. I tried to use library jp.co.cyberagent.android.gpuimage but it has a few bugs and doesn't work properly with this problem.
I want to know how solve this problem. It mays be some property which I don't know or something else. I'm new in OpenGL and hope to your help.

You can't use GLUtils.texImage2D() to load alpha textures on Android. This is a common problem that Google really should document better. The problem is that the Bitmap class converts all images into pre-multiplied format, but that does not work with OpenGL ES unless the images are completely opaque. The best solution is to use native code. This article gives more detail on this:
http://software.intel.com/en-us/articles/porting-opengl-games-to-android-on-intel-atom-processors-part-1

Related

GL_INVALID_OPERAION in glDrawElements while rendering to Framebuffer from external texture

I am trying to render the contents of an external texture (which gets its data from android MediaCodec) to a framebuffer with a texture atttachment (first pass) and then using that texture to render to display (second pass)
My overall goal is to do some operation in the shader in the first pass and some more operation in the second pass. But I am getting GL_INVALID_OPERAION in the first pass and hence texture associated with the Framebuffer is not populated.
Below is the summary of the code
OpenGL environment initialization
GLES20.glGenFramebuffers(1,frameBufferHandles,0);
GLES20.glGenBuffers(2, bufferHandles, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Utils.VERTICES.length * FLOAT_SIZE_BYTES, Utils.getVertexBuffer(), GLES20.GL_DYNAMIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, Utils.INDICES.length * FLOAT_SIZE_BYTES, Utils.getIndicesBuffer(), GLES20.GL_DYNAMIC_DRAW);
GLES20.glGenTextures(2, textureHandles, 0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureHandles[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
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.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[1]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
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.glTexImage2D(GLES20.GL_TEXTURE_2D,0,GLES20.GL_RGBA,width,height,0,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE,null); //width.height are of that viewport and are initialized properly
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D,textureHandles[1],0);
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if(status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.d(TAG,"Frame Buffer Incomplete : " + status);
} else
Log.d(TAG,"Frame Buffer Complete");
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
Vertex Shader setup
public static final String DEFAULT_VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n"
+ "uniform mat4 uSTMatrix;\n"
+ "attribute vec4 aPosition;\n"
+ "attribute vec4 aTextureCoord;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " gl_Position = uMVPMatrix * aPosition;\n"
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n"
+ "}\n";
Fragment shader setup
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform sampler2D sTextureSampler;
uniform float r;
uniform int pass;
void main() {
if(pass == 0) {
gl_FragColor = texture2D(sTexture, vTextureCoord);
//gl_FragColor = vec4(1.0,1.0,0.0,1.0);
}
else if(pass == 1) {
gl_FragColor = texture2D(sTextureSampler, vTextureCoord);
}
}
Vertex Attributes and Index buffer are defined like below
public static float[] VERTICES = {
-1.0f, -1.0f, 0.0f, 0f, 0f,
-1.0f, 1.0f, 0.0f, 0f, 1f,
1.0f, 1.0f, 0.0f, 1f, 1f,
1.0f, -1.0f, 0.0f, 1f, 0f
};
public static int[] INDICES = {
2, 1, 0, 0, 3, 2
};
And finally my draw() function looks like this
protected void draw() {
Matrix.setIdentityM(mvpMatrix, 0);
if(mProgram != -1)
mProgram = createProgram();
int vertexHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
int uvsHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
int texMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
int mvpMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
int textureHandle = GLES20.glGetUniformLocation(mProgram, "sTextureSampler");
int pass = GLES20.glGetUniformLocation(mProgram, "pass");
GLES20.glUseProgram(mProgram);
GLES20.glUniformMatrix4fv(texMatrixHandle, 1, false, texMatrix, 0);
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]);
GLES20.glEnableVertexAttribArray(vertexHandle);
GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 4 * 5, 0);
GLES20.glEnableVertexAttribArray(uvsHandle);
GLES20.glVertexAttribPointer(uvsHandle, 2, GLES20.GL_FLOAT, false, 4 * 5, 3 * 4);
//Pass 0 - Render to framebuffer , gives GL_INVALID_OPERAION error
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]);
GLES20.glUniform1i(pass, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0);
//Pass 1 - Render to display
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
GLES20.glUniform1i(textureHandle, 2);
GLES20.glUniform1i(pass, 1);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0);
}
I am guessing it is related to vTextureCoord, because if I dont use vTextureCoord and render a constant color to the framebuffer during first pass , then I can read it back during the second pass from the texture associated with the framebuffer.

Android OpenGLES Brown square

So I'm trying to learn openGLES 2.0 and create a textured rectangle. Apparently I didn't follow all the instructions and now I've ended up with just a odd color square.
heres my shaders.
final String vertexShader =
"uniform mat4 u_MVPMatrix; \n" // A constant representing the combined model/view/projection matrix.
+ "attribute vec2 a_TexCoordinate;\n" // Per-vertex texture coordinate information we will pass in.
+ "attribute vec4 a_Position; \n" // Per-vertex position information we will pass in.
// + "attribute vec4 a_Color; \n" // Per-vertex color information we will pass in.
// + "varying vec4 v_Color; \n" // This will be passed into the fragment shader.
+ "varying vec2 v_TexCoordinate; \n" // This will be passed into the fragment shader.
+ "void main() \n" // The entry point for our vertex shader.
+ "{ \n"
+ " v_TexCoordinate = a_TexCoordinate;\n" // Pass the texture coordinate through to the fragment shader.
// It will be interpolated across the triangle.
+ " gl_Position = u_MVPMatrix \n" // gl_Position is a special variable used to store the final position.
+ " * a_Position; \n" // Multiply the vertex by the matrix to get the final point in
+ "} \n";
final String fragmentShader =
"precision mediump float; \n" // Set the default precision to medium. We don't need as high of a
// precision in the fragment shader.
+ "uniform sampler2D u_Texture; \n" // The input texture.
+ "uniform vec4 u_Color; \n" // This is the color from the vertex shader interpolated across the
// triangle per fragment.
+ "varying vec2 v_TexCoordinate; \n" // Interpolated texture coordinate per fragment.
+ "void main() \n" // The entry point for our fragment shader.
+ "{ \n"
+ " gl_FragColor = texture2D(u_Texture, v_TexCoordinate); \n"
+ "} \n";
Here is the load texture function
public static int loadTexture(final Context context, final int resourceId)
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
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);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
And here is the drawFrame
#Override
public void onDrawFrame(GL10 gl) {
// Set the background clear color to gray.
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
mTextureUniformHandle = GLES20.glGetUniformLocation(programHandle, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(programHandle, "a_TexCoordinate");
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
// Draw the triangle facing straight on.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, Fields.screen_width / 2, Fields.screen_height / 2, 0);
Matrix.scaleM(mModelMatrix, 0, Fields.screen_width / 4, Fields.screen_width / 4, 0);
drawTriangle(mTriangle1Vertices);
}
Any advice for getting rid of the wierd brown square and actually displaying the texture?
Edit: Here are the texture coordinates
public GL20Renderer(Context context) {
final float[] triangle1VerticesData = {
// X, Y, Z,
// R, G, B, A
-1f, 1f, 0.0f,
-1, -1, 0.0f,
1f, 1f, 0.0f,
-1f, -1f, 0.0f,
1f, -1f, 0.0f,
1f, 1f, 0.0f,
};
// Initialize the buffers.
mTriangle1Vertices = ByteBuffer.allocateDirect(triangle1VerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangle1Vertices.put(triangle1VerticesData).position(0);
final float[] triangle1TextureCoordinateData =
{
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
mTriangleTextureCoordinates = ByteBuffer.allocateDirect(triangle1TextureCoordinateData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleTextureCoordinates.put(triangle1TextureCoordinateData).position(0);
this.context = context;
}
and here is the drawtriangle function
private void drawTriangle(final FloatBuffer aTriangleBuffer)
{
// Pass in the position information
aTriangleBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aTriangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
int colorHandle = GLES20.glGetUniformLocation(programHandle, "u_Color");
// Set color for drawing the triangle
GLES20.glUniform4f(colorHandle, 0.0f, 0.8f, 1.0f, 1.0f);
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
Your drawTriangle code doesn't set the texture co-ordinates anywhere. Add some code something like this:
triangle1TextureCoordinateData.position(mPositionOffset * 2 / 3);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false,
0, triangle1TextureCoordinateData);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
Note you can set the stride to zero if your data is packed in the buffer.

Opengl ES texture is not applied to object

I am new to opengl es on android and learning opengl by doing some examples. I am using two program to draw 3 objects. The following code loads the texture and draws a square. But it is appearing as a black square instead of applying the texture to the body.
my fragment-shader code
precision mediump float;
uniform sampler2D u_Texture;
varying vec2 v_TexCoordinate;
void main() {
gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
}
my vertex-shader code
attribute vec2 a_TexCoordinate;
varying vec2 v_TexCoordinate;
attribute vec4 a_Position;
uniform mat4 u_Matrix;
void main() {
gl_Position = u_Matrix * a_Position;
v_TexCoordinate = a_TexCoordinate;
}
my object vertex buffer
float [] vBufferFloat = new float[] {
-0.2f, -0.2f, 1f,
0.2f, -0.2f, 1f,
0.2f, 0.2f, 1f,
-0.2f, 0.2f, 1f,
-0.2f, -0.2f, 1f,
};
my texture buffer
float [] texCoordinate = new float[] {
-0.2f, -0.2f,
0.2f, -0.2f,
0.2f, 0.2f,
-0.2f, 0.2f,
-0.2f, -0.2f,
};
my onSurfaceCreated && onDrawFrame code
public void onSurfaceCreated() {
cloudRendereProgram = ShaderHelper.createProgram(mContext, R.raw.sky_texture_vertex_shader, R.raw.sky_texture_fragment_shader);
cloudTextureId = Utils.loadTexture(mContext, com.elpis.gamecontroller.R.drawable.cloud);
aTextureLocation = GLES20.glGetAttribLocation(cloudRendereProgram, "a_TexCoordinate");
uMatrixLocation = GLES20.glGetUniformLocation(cloudRendereProgram, "u_Matrix");
aPositionLocation = GLES20.glGetAttribLocation(cloudRendereProgram, "a_Position");
uTextureLocation = GLES20.glGetUniformLocation(cloudRendereProgram, "u_Texture");
}
public void onDrawFrame() {
float [] mVMatrix = new float[16];
GLES20.glUseProgram(cloudRendereProgram);
GLES20.glVertexAttribPointer(aPositionLocation, 3, GLES20.GL_FLOAT, false, 0, vBuff.buffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
Matrix.multiplyMM(mVMatrix, 0, modelMatrix, 0, projectionMatrix, 0);
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mVMatrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.cloudTextureId);
GLES20.glUniform1i(uTextureLocation, 0);
GLES20.glVertexAttribPointer(aTextureLocation, 2, GLES20.GL_FLOAT, false, 0, texBuff.buffer);
GLES20.glEnableVertexAttribArray(aTextureLocation);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 5);
}
and my texture loader helper code
public static int loadTexture(Context ctx, int resId) {
final int [] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] == 0)
return 0;
final BitmapFactory.Options options = new Options();
options.inScaled = false;
final Bitmap imgTexture = BitmapFactory.decodeResource(ctx.getResources(), resId);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
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, imgTexture, 0);
imgTexture.recycle();
return textureHandle[0];
}
When I run the android application all I see is a black square instead of seeing a texture of cloud. So, I would appreciate if anyone point me in the right direction.
Two quick questions; Is it valid to create multiple opengl program objects with different shaders and run them concurrently?
[UPDATE]
The problem was on the onDrawFrame(). I had to use vBuff.buffer.position(0) and texBuff.buffer.position(0) to be able to draw the texture correctly.
public void onDrawFrame() {
float [] mVMatrix = new float[16];
GLES20.glUseProgram(cloudRendereProgram);
// FIX
vBuff.buffer.position(0);
texBuff.buffer.position(0);
// END FIX
GLES20.glVertexAttribPointer(aPositionLocation, 3, GLES20.GL_FLOAT, false, 0, vBuff.buffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
Matrix.multiplyMM(mVMatrix, 0, modelMatrix, 0, projectionMatrix, 0);
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mVMatrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.cloudTextureId);
GLES20.glUniform1i(uTextureLocation, 0);
GLES20.glVertexAttribPointer(aTextureLocation, 2, GLES20.GL_FLOAT, false, 0, texBuff.buffer);
GLES20.glEnableVertexAttribArray(aTextureLocation);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 5);
}
You're not calling glEnableVertexAttribArray for your texture coords. Add this line to your onDrawFrame():
GLES20.glEnableVertexAttribArray(aTextureLocation);
Call glGenerateMipmap after you upload the texture texImage2D() in your loadTexture() function, to make sure all mip-map levels are valid:
glGenerateMipmap(GLES20.GL_TEXTURE_2D);
Also, move these calls from your surfaceCreated() function to the start of drawFrame():
aTextureLocation = GLES20.glGetAttribLocation(cloudRendereProgram, "a_TexCoordinate");
uMatrixLocation = GLES20.glGetUniformLocation(cloudRendereProgram, "u_Matrix");
aPositionLocation = GLES20.glGetAttribLocation(cloudRendereProgram, "a_Position");
uTextureLocation = GLES20.glGetUniformLocation(cloudRendereProgram, "u_Texture");
(it could be that these variables are not bound properly or the GL context is not yet properly set up in surfaceCreated())
A debugging tip for OpenGLES. Add this function to your code (it's from the Android OpenGLES samples):
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
you can call it after each OpenGLES call, and pass in a String that can be whatever debugging message you want. If anything goes wrong then you'll get an exception instead of just silent failure which will leave you scratching your head trying to figure out what went wrong. Make sure to remove it from your final build to avoid force closes.

Android OpenGL ES 2.0 - Black Textures On Some Devices

SOLVED: Turns out, I was passing the OpenGL named texture to the shader instead of the active texture number, fixed it with:
GLES20.glUniform1i(sTextureHandle, 0); // 0 means GLES20.GL_TEXTURE0
UPDATE: glGetError() returns 0, after calling glTexImage2D(). So there are no OpenGL flagged errors...
I've been working on a game using OpenGL 2.0 for the rendering.
I've been testing it on my Samsung Galaxy Nexus, and haven't had any texturing errors. All of my textures are powers of 2, in a .png format, and stored in the No -DPI folder. All textures load, and render perfectly using or not using alpha.
I tried the game on the Samsung Galaxy S4 and a Droid Bionic, both of which resulted in black textures (with and without alpha blending).
I have scoured the internet for people with the same problem, but all of their fixes were already reflected in my code.
I'm thinking it must be something with the way I create the texture, or my OpenGL setup because why else would it only work on the Galaxy Nexus?
OpenGL Setup:
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); //Black Background
GLES20.glClearDepthf(1.0f); //Depth Buffer Setup
GLES20.glEnable(GLES20.GL_DEPTH_TEST); //Enables Depth Testing
GLES20.glDepthFunc(GLES20.GL_LEQUAL); //The Type Of Depth Testing To Do
GLES20.glDisable(GLES20.GL_BLEND);
Matrix.orthoM(sProjectionMatrix, 0, 0.0f, width, height, 0.0f, 0.0f, MAX_DEPTH);
Matrix.setLookAtM(sViewMatrix, 0, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
// Get the texturing handles
GLES20.glBindAttribLocation(sShaderProgram, 2, "a_tex_coords");
sTexCoordsHandle = GLES20.glGetAttribLocation(sShaderProgram, "a_tex_coords");
sTextureHandle = GLES20.glGetUniformLocation(sShaderProgram, "u_texture");
GLES20.glUseProgram(sShaderProgram);
GLES20.glEnableVertexAttribArray(sPositionHandle);
GLES20.glEnableVertexAttribArray(sColorHandle);
GLES20.glEnableVertexAttribArray(sTexCoordsHandle);
Texture Loading Code:
Bitmap bitmap = BitmapFactory.decodeStream(sGame.CONTEXT.getResources().openRawResource(R.drawable.circle));
LAST_TEXTURE_INDEX++;
GLES20.glGenTextures(1, Texture.IDS, LAST_TEXTURE_INDEX);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, IDS[LAST_TEXTURE_INDEX]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
Other Texture Attempt (In replace of GLUtils.texImage2D and the bitmap):
int width = 128;
int height = 128;
ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(width * height * 3).order(ByteOrder.nativeOrder());
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
pixelBuffer.put((byte) 0x00);
pixelBuffer.put((byte) 0xFF);
pixelBuffer.put((byte) 0xFF);
}
}
pixelBuffer.position(0);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
Before rendering I call these:
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, sTextureIndex);
GLES20.glUniform1i(sTextureHandle, sTextureIndex);
Vertex Shader:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_tex_coords;
uniform mat4 u_model_view_projection;
varying vec4 v_color;
varying vec2 v_tex_coords;
void main() {
v_color = a_color;
v_tex_coords = a_tex_coords;
gl_Position = u_model_view_projection * a_position;
}
Fragment Shader:
varying vec2 v_tex_coords;
uniform sampler2D u_texture;
void main() {
vec4 color = texture2D(u_texture, v_tex_coords);
gl_FragColor = color;
}
And for the sake of solving the bug, I disabled blending because I know the alpha can often cause a headache aswell.
Both types of texturing (Using an image or using pixel data) works on my Galaxy Nexus, but not on the S4. I don't know if it's my texturing code that's broken, or some other OpenGL setting, but I'll try to post all relevant code.
Turns out, I was passing the OpenGL named texture to the shader instead of the active texture number, fixed it with:
GLES20.glUniform1i(sTextureHandle, 0); // 0 means GLES20.GL_TEXTURE0
instead of:
GLES20.glUniform1i(sTextureHandle, sTextureIndex); // sTextureIndex = generated texture ID

Android OpenGLES 2.0 Texture Mapping Does Not Work

I'm trying to texture map some quads. I can render the quads with solid colors just fine. I've been following this page:
http://www.learnopengles.com/android-lesson-four-introducing-basic-texturing/
Everything compiles and runs, but the texture is simply not being applied.
The only difference between my code and the page linked is that I do not do glBindAttribLocation, and I use glDrawElements instead of glDrawArrays.
EDIT: Converting my vertex and tex coord data to use glDrawArrays did not fix anything.
All of my shader handles seem correct. The problem must be in one of my draws. If anyone can help me debug this, that would be great. I've tried setting gl_FragColor = vec4(texCoord[0], texCoord[1], 1.0f, 1.0f) just to see if tex coordinates are making it to the shader but that crashes.
Initialization of vertex data:
static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
static float textureCoords[] = { 0.0f, 1.0f, // top left
0.0f, 0.0f, // bottom left
1.0f, 0.0f, // bottom right
1.0f, 1.0f}; // top right
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4); // (# of coordinate values * 4 bytes per float)
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
ByteBuffer tcbb = ByteBuffer.allocateDirect(textureCoords.length * 4);
tcbb.order(ByteOrder.nativeOrder());
texCoordBuffer = tcbb.asFloatBuffer();
texCoordBuffer.put(textureCoords);
texCoordBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
In my attach shader code:
// get handle to texture coords
mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "texCoord");
MyGLRenderer.checkGlError("glGetAttribLocation");
Log.i("TEXCOORD SHADER HANDLE", " " + mTexCoordHandle);
//get handle to texture
mTexHandle = GLES20.glGetUniformLocation(mProgram, "u_texture");
MyGLRenderer.checkGlError("glGetUniformLocation");
Log.i("TEX SHADER HANDLE", " " + mTexHandle);
In my main (ParticleEngine) draw:
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
0, vertexBuffer);
GLES20.glEnableVertexAttribArray(mTexCoordHandle);
GLES20.glVertexAttribPointer(mTexCoordHandle, TEX_COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
0, texCoordBuffer);
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureDataHandle);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTexHandle, 0);
for (int t=0;t<mChildsCount;t++) {
Particle child = (Particle)mChilds[t];
child.draw(mVMatrix, mProjMatrix);
}
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordHandle);
In an individual particles draw:
//... calculate model-view-projection matrix, send to shader
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mPEngine.drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, mPEngine.drawListBuffer);
In my load_texture:
public static int loadTexture(final Context context, final int resourceId)
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
if (bitmap == null) {
throw new RuntimeException("Error decoding bitmap");
}
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
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);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
In my shaders:
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 texCoord;" +
"varying vec2 texCoordOut;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" texCoordOut = texCoord; \n" +
" gl_Position = uMVPMatrix * vPosition; \n" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec2 texCoordOut;" +
"uniform sampler2D u_texture;" +
"void main() {" +
" vec4 texColor = texture2D(u_texture, texCoordOut);\n" +
" gl_FragColor = texColor;" +
"}";
SOLVED:
Android OpenGL2.0 showing black textures
NEEDED TO ADD 2 LINES TO LOAD_TEXTURE:
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
Turns out that is required if your texture sizes are not powers of 2.
For anyone who is coming here for the iOS side of this, the other answer provided here also applies. Only difference is the commands are like this:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Also, make sure that you enable filtering:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
which I didn't think was so practical in 2D, but you need them

Categories

Resources