For Android I've seen several examples of adding bitmap textures to a surface in OpenGL ES1.x and 2.x but it seems that the APIs changed somewhat for ES 3.x (e.g. glEnableClientState() was removed) and I'm wondering what needs to be added/changed to the following code in order to be able to push bitmap images to my cube surfaces. I made some modifications to this code I found online and it already successfully draws and rotates a cube with different colours on each surface, however, now I'd like to draw specific resource images onto each surface instead of just plain colours.
This is the class Cube.java :
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLUtils;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Cube {
private int mProgramObject;
private int mMVPMatrixHandle;
private int mColorHandle;
private FloatBuffer mVertices;
//initial size of the cube. set here, so it is easier to change later.
float size = 0.7f;
//this is the initial data, which will need to translated into the mVertices variable in the constructor.
float[] mVerticesData = new float[]{
////////////////////////////////////////////////////////////////////
// FRONT
////////////////////////////////////////////////////////////////////
// Triangle 1
-size, size, size, // top-left
-size, -size, size, // bottom-left
size, -size, size, // bottom-right
// Triangle 2
size, -size, size, // bottom-right
size, size, size, // top-right
-size, size, size, // top-left
////////////////////////////////////////////////////////////////////
// BACK
////////////////////////////////////////////////////////////////////
// Triangle 1
-size, size, -size, // top-left
-size, -size, -size, // bottom-left
size, -size, -size, // bottom-right
// Triangle 2
size, -size, -size, // bottom-right
size, size, -size, // top-right
-size, size, -size, // top-left
////////////////////////////////////////////////////////////////////
// LEFT
////////////////////////////////////////////////////////////////////
// Triangle 1
-size, size, -size, // top-left
-size, -size, -size, // bottom-left
-size, -size, size, // bottom-right
// Triangle 2
-size, -size, size, // bottom-right
-size, size, size, // top-right
-size, size, -size, // top-left
////////////////////////////////////////////////////////////////////
// RIGHT
////////////////////////////////////////////////////////////////////
// Triangle 1
size, size, -size, // top-left
size, -size, -size, // bottom-left
size, -size, size, // bottom-right
// Triangle 2
size, -size, size, // bottom-right
size, size, size, // top-right
size, size, -size, // top-left
////////////////////////////////////////////////////////////////////
// TOP
////////////////////////////////////////////////////////////////////
// Triangle 1
-size, size, -size, // top-left
-size, size, size, // bottom-left
size, size, size, // bottom-right
// Triangle 2
size, size, size, // bottom-right
size, size, -size, // top-right
-size, size, -size, // top-left
////////////////////////////////////////////////////////////////////
// BOTTOM
////////////////////////////////////////////////////////////////////
// Triangle 1
-size, -size, -size, // top-left
-size, -size, size, // bottom-left
size, -size, size, // bottom-right
// Triangle 2
size, -size, size, // bottom-right
size, -size, -size, // top-right
-size, -size, -size // top-left
};
float colorcyan[] = myColor.cyan();
float colorblue[] = myColor.blue();
float colorred[] = myColor.red();
float colorgray[] = myColor.gray();
float colorgreen[] = myColor.green();
float coloryellow[] = myColor.yellow();
private int numFaces = 6;
private int[] imageFileIDs = { // Image file IDs
R.drawable.geo1,
R.drawable.geo2,
R.drawable.geo3,
R.drawable.geo4,
R.drawable.geo5,
R.drawable.geo6
};
private int[] textureIDs = new int[numFaces];
private Bitmap[] bitmap = new Bitmap[numFaces];
//vertex shader code
String vShaderStr =
"#version 300 es \n"
+ "uniform mat4 uMVPMatrix; \n"
+ "in vec4 vPosition; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = uMVPMatrix * vPosition; \n"
+ "} \n";
//fragment shader code.
String fShaderStr =
"#version 300 es \n"
+ "precision mediump float; \n"
+ "uniform vec4 vColor; \n"
+ "out vec4 fragColor; \n"
+ "void main() \n"
+ "{ \n"
+ " fragColor = vColor; \n"
+ "} \n";
String TAG = "Cube";
//finally some methods
//constructor
public Cube(Context ctx) {
if (ctx == null)
throw new NullPointerException("3D rendering needs valid context.");
//first setup the mVertices correctly.
mVertices = ByteBuffer
.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(mVerticesData);
mVertices.position(0);
//setup the shaders
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the vertex/fragment shaders
vertexShader = PrimaryRenderer.LoadShader(GLES30.GL_VERTEX_SHADER, vShaderStr);
fragmentShader = PrimaryRenderer.LoadShader(GLES30.GL_FRAGMENT_SHADER, fShaderStr);
// Create the program object
programObject = GLES30.glCreateProgram();
if (programObject == 0) {
Log.e(TAG, "So some kind of error, but what?");
return;
}
GLES30.glAttachShader(programObject, vertexShader);
GLES30.glAttachShader(programObject, fragmentShader);
// Bind vPosition to attribute 0
GLES30.glBindAttribLocation(programObject, 0, "vPosition");
// Link the program
GLES30.glLinkProgram(programObject);
// Check the link status
GLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e(TAG, "Error linking program:");
Log.e(TAG, GLES30.glGetProgramInfoLog(programObject));
GLES30.glDeleteProgram(programObject);
return;
}
// Store the program object
mProgramObject = programObject;
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
for (int face = 0; face < numFaces; face++) {
bitmap[face] = BitmapFactory.decodeStream(
ctx.getResources().openRawResource(imageFileIDs[face]), null, options);
}
//now everything is setup and ready to draw.
}
public void draw(float[] mvpMatrix) {
// Use the program object
GLES30.glUseProgram(mProgramObject);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES30.glGetUniformLocation(mProgramObject, "uMVPMatrix");
PrimaryRenderer.checkGlError("glGetUniformLocation");
// get handle to fragment shader's vColor member
mColorHandle = GLES30.glGetUniformLocation(mProgramObject, "vColor");
// Apply the projection and view transformation
GLES30.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
PrimaryRenderer.checkGlError("glUniformMatrix4fv");
int VERTEX_POS_INDX = 0;
mVertices.position(VERTEX_POS_INDX); //just in case. We did it already though.
//add all the points to the space, so they can be correct by the transformations.
//would need to do this even if there were no transformations actually.
GLES30.glVertexAttribPointer(VERTEX_POS_INDX, 3, GLES30.GL_FLOAT,
false, 0, mVertices);
GLES30.glEnableVertexAttribArray(VERTEX_POS_INDX);
//Now we are ready to draw the cube finally.
int startPos = 0;
int verticesPerface = 6;
//draw front face
GLES30.glUniform4fv(mColorHandle, 1, colorblue, 0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIDs[0]);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
startPos += verticesPerface;
//draw back face
GLES30.glUniform4fv(mColorHandle, 1, colorcyan, 0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIDs[1]);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
startPos += verticesPerface;
//draw left face
GLES30.glUniform4fv(mColorHandle, 1, colorred, 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
startPos += verticesPerface;
//draw right face
GLES30.glUniform4fv(mColorHandle, 1, colorgray, 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
startPos += verticesPerface;
//draw top face
GLES30.glUniform4fv(mColorHandle, 1, colorgreen, 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
startPos += verticesPerface;
//draw bottom face
GLES30.glUniform4fv(mColorHandle, 1, coloryellow, 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, startPos, verticesPerface);
//last face, so no need to increment.
}
public void loadTexture(GL10 gl) {
gl.glGenTextures(6, textureIDs, 0); // Generate texture-ID array for 6 IDs
// Generate OpenGL texture images
for (int face = 0; face < numFaces; face++) {
gl.glBindTexture(GLES30.GL_TEXTURE_2D, textureIDs[face]);
gl.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
gl.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap[face], 0);
bitmap[face].recycle();
}
}
}
======================
The public method loadTexture() is called from my renderer class as part of the Cube instance initialization. It associates each of the 6 bitmaps with a 2D texture however I'm missing the code to successfully feed the textures to the engine. All the examples I've seen so far deal only with ES 1.x and ES 2.x and don't seem to be compatible with the above code. Any help please.
You've to add a texture coordinate attribute to the vertex shader (vUV
). Of course you've to st the texture coordinate attribute (similar the vertex coordinates). Note texture coordinates are in range [0.0, 1.0] and you need one texture coordinate attribute (u, v) vor each vertex coordinate (x, y, z):
GLES30.glVertexAttribPointer(TEX_COORD_INDX, 2, GLES30.GL_FLOAT, false, 0, mTexCoords);
GLES30.glEnableVertexAttribArray(TEX_COORD_INDX);
Pass the texture coordinates to the fragment shader (out vec2 uv;
):
#version 300 es
uniform mat4 uMVPMatrix;
in vec4 vPosition;
in vec2 vUV;
out vec2 uv;
void main()
{
uv = vUV;
gl_Position = uMVPMatrix * vPosition;
}
Add a texture sampler uniform in the fragment shader (u_texture
). The uniform has to be set by the texture unit (e.g. 0 for GL_TEXTURE0
). Since you use texture unit 0 and 0 is the default initialization, you can skip this. Look up the texture by texture
and assigne the color to the fragment shader output:
#version 300 es
precision mediump float;
in vec2 vUV;
out vec4 fragColor;
uniform sampler2D u_texture;
void main()
{
vec4 color = texture(u_texture, uv.xy);
fragColor = color;
}