Reputation: 5880
I am using OpenGL|ES 2.0 to create a simple 2D square. I am struggling to get the textures working. Please help me in this regard. I have attached the code below:
GFXUtils:
public class GFXUtils {
public static final String TAG = "GFXUtils";
public static final int COORDS_PER_VERTEX = 3;
public static final int COORDS_PER_TEXTURE = 2;
public static int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
public static int textureStride = COORDS_PER_TEXTURE * 4; // bytes per vertex
public static Context Context = null;
public static SparseIntArray textures = new SparseIntArray();
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
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);
}
}
public static void loadTexture(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);
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);
// 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.");
}
textures.append(resourceId, textureHandle[0]);
}
}
Vertex:
public class Vertex
{
public FloatBuffer floatBuffer; // buffer holding the vertices
public ShortBuffer indexBuffer;
public int numVertices;
public int numIndeces;
//public float vertex[];
public Vertex (float[] vertex, int coordsPerVertex)
{
//this.vertex = vertex;
this.setVertices(vertex, coordsPerVertex);
}
public Vertex (float[] vertex, short[] indices, int coordsPerVertex)
{
//this.vertex = vertex;
this.setVertices(vertex, coordsPerVertex);
this.setIndices(indices);
}
private void setVertices(float vertex[], int coordsPerVertex)
{
// a float has 4 bytes so we allocate for each coordinate 4 bytes
ByteBuffer factory = ByteBuffer.allocateDirect (vertex.length * 4);
factory.order (ByteOrder.nativeOrder ());
// allocates the memory from the byte buffer
floatBuffer = factory.asFloatBuffer ();
// fill the vertexBuffer with the vertices
floatBuffer.put (vertex);
// set the cursor position to the beginning of the buffer
floatBuffer.position (0);
numVertices = vertex.length / coordsPerVertex;
}
protected void setIndices(short[] indices) {
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
numIndeces = indices.length;
}
}
UPDATE Square:
public class Square{
private static final String TAG = "Square";
public float[] rotation = {0.0f,0.0f,45.0f};
public float[] scale = {100.0f,100f,100f};
public float[] position = {0.0f,0.0f,100f};
public float[] color = { 0.0f, 0.0f, 1.0f, 1.0f };
private int textureRef = -1;
private int mMVPMatrixHandle;
protected int DRAW_MODE = GLES20.GL_TRIANGLES;
protected int mProgram;
protected int mPositionHandle;
protected Vertex vertices;
protected Vertex texture;
private int mColorHandle;
private int vsTextureCoord;
private int fsTexture;
protected float[] result_matrix = new float[16];
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 TexCoordIn;" +
"varying vec2 TexCoordOut;" +
"void main() {" +
//the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition;" +
" TexCoordOut = TexCoordIn;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"uniform sampler2D Texture;" +
"varying lowp vec2 TexCoordOut;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
//I am fully aware that I am not using the texture by assigning the colour, but until I can actually SEND the texture through, there would be no point.
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
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
public Square(int textureResourceId) {
int vertexShader = GFXUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = GFXUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
textureRef = GFXUtils.textures.get(textureResourceId);
// initialize vertex byte buffer for shape coordinates
vertices = new Vertex(squareCoords, drawOrder, GFXUtils.COORDS_PER_VERTEX);
texture = new Vertex (new float[]
{
1.0f, 0.0f,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f,
}, GFXUtils.COORDS_PER_TEXTURE);
DRAW_MODE = GLES20.GL_TRIANGLE_FAN;
}
private void getHandles()
{
//get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
if (mPositionHandle == -1) Log.e(TAG, "vPosition not found");
//get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
if (mColorHandle == -1) Log.e(TAG, "vColor not found");
//get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
if (mMVPMatrixHandle == -1) Log.e(TAG, "uMVPMatrix not found");
//get handle to texture coordinate variable
vsTextureCoord = GLES20.glGetAttribLocation(mProgram, "TexCoordIn");
if (vsTextureCoord == -1) Log.e(TAG, "TexCoordIn not found");
//get handle to shape's texture reference
fsTexture = GLES20.glGetUniformLocation(mProgram, "Texture");
if (fsTexture == -1) Log.e(TAG, "Texture not found");
}
private void translateRotateScale(float[] matrix, float[] perspectiveMatrix)
{
for (int i= 0; i < perspectiveMatrix.length;i++)
matrix[i] = perspectiveMatrix[i];
Matrix.translateM(matrix, 0, position[0], position[1], position[2]);
Matrix.rotateM(matrix, 0, rotation[0], 1.0f, 0.0f, 0.0f);
Matrix.rotateM(matrix, 0, rotation[1], 0.0f, 1.0f, 0.0f);
Matrix.rotateM(matrix, 0, rotation[2], 0.0f, 0.0f, 1.0f);
Matrix.scaleM(matrix, 0, scale[0], scale[1], scale[2]);
}
public void draw(float[] mvpMatrix) {
rotation[2]+=0.5;
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
GFXUtils.checkGlError("using program");
//Housekeeping
getHandles();
translateRotateScale(result_matrix, mvpMatrix);
//end housekeeping
// Set color for drawing the shape
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, result_matrix, 0);
GFXUtils.checkGlError("glUniformMatrix4fv");
// Prepare the shape coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, GFXUtils.COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
GFXUtils.vertexStride, vertices.floatBuffer);
GFXUtils.checkGlError("load vertex buffer");
GLES20.glVertexAttribPointer(vsTextureCoord, GFXUtils.COORDS_PER_TEXTURE,
GLES20.GL_FLOAT, false,
GFXUtils.textureStride, texture.floatBuffer);
GFXUtils.checkGlError("load texture buffer - " + vsTextureCoord);
// Enable a handle to the shape vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
GFXUtils.checkGlError("enable position handle");
GLES20.glEnableVertexAttribArray(vsTextureCoord);
GFXUtils.checkGlError("enable texture handle");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GFXUtils.checkGlError("activtexture");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureRef);
GFXUtils.checkGlError("bindtexture");
GLES20.glUniform1i(fsTexture, 0);
GFXUtils.checkGlError("uniformi");
//Draw the shape
GLES20.glDrawElements(DRAW_MODE, vertices.numIndeces, GLES20.GL_UNSIGNED_SHORT, vertices.indexBuffer);
GFXUtils.checkGlError("glDrawArrays with " + vertices.numVertices + " vertices");
//Disable vertex array
GLES20.glDisableVertexAttribArray(vsTextureCoord);
GLES20.glDisableVertexAttribArray(mPositionHandle);
GFXUtils.checkGlError("glDisableVertexAttribArray for position");
}
}
UPDATE But it now cannot find the 'Texture' sampler2D parameter for fsTexture. The image is here:
I have looked at the following:
Android: OpenGL ES 2.0 - Texture always black
Draw multiple objects with textures
Android OpenGLES 2.0 Texture Mapping Does Not Work
Textures in OpenGL ES 2.0 for Android
http://www.learnopengles.com/android-lesson-six-an-introduction-to-texture-filtering/
http://www.learnopengles.com/android-lesson-four-introducing-basic-texturing/
http://blog.shayanjaved.com/2011/05/13/android-opengl-es-2-0-render-to-texture/
http://www.raywenderlich.com/4404/opengl-es-2-0-for-iphone-tutorial-part-2-textures
http://androgeek.info/?p=167
Upvotes: 1
Views: 9069
Reputation: 187
floatBuffer should contain the address of the first element in that array. you have errors after glVertexAttribPointer because the last parameter doesn't point to the vertex data. straight forward solution is to make floatbuffer = &vertexdata
my suggestion is to put the vertex data to the gl_array_buffer using glGenBuffer,glBindBuffer,glBufferData. after that, just bind the buffer before glVertexAttribPointer using 0 as the last parameter. this is faster than using the address of the vertex
Upvotes: 1
Reputation: 5919
ok then here is my guess at a solution:
the glVertexAttribPointer() calls seems to be fine
my guess:
vsTextureCoord == -1
glGetAttribLocation() does not generate an error if the attribute was not found. you have to check if the result is not -1 to make sure everything went fine.
source for the problem could be that the attribute TexCoordIn in your shader is a vec4!
Upvotes: 2