Related
I'm in troubles with Android, GL and Textures, I have a little example and I want to load two different textures and paint it on screen, my problem is that always draws the last texture loaded.
I'm noob with GL in android and have copied the code from some tutorials.
Here is the code:
GLRenderer:
public class GLRenderer implements Renderer {
// Our matrices
private final float[] mtrxProjection = new float[16];
private final float[] mtrxView = new float[16];
private final float[] mtrxProjectionAndView = new float[16];
// Our screenresolution
float mScreenWidth = 1280;
float mScreenHeight = 768;
// Misc
Context mContext;
long mLastTime;
int mProgram;
CardView card1;
CardView card2;
public GLRenderer(Context c)
{
mContext = c;
mLastTime = System.currentTimeMillis() + 100;
}
public void onPause()
{
/* Do stuff to pause the renderer */
}
public void onResume()
{
/* Do stuff to resume the renderer */
mLastTime = System.currentTimeMillis();
}
#Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the current time
long now = System.currentTimeMillis();
// We should make sure we are valid and sane
if (mLastTime > now) return;
// Get the amount of time the last frame took.
long elapsed = now - mLastTime;
// Update our example
// Render our example
card1.Draw(mtrxProjectionAndView);
card2.Draw(mtrxProjectionAndView);
// Save the current time to see how long it took :).
mLastTime = now;
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// We need to know the current width and height.
mScreenWidth = width;
mScreenHeight = height;
// Redo the Viewport, making it fullscreen.
GLES20.glViewport(0, 0, (int)mScreenWidth, (int)mScreenHeight);
// Clear our matrices
for(int i=0;i<16;i++)
{
mtrxProjection[i] = 0.0f;
mtrxView[i] = 0.0f;
mtrxProjectionAndView[i] = 0.0f;
}
// Setup our screen width and height for normal sprite translation.
Matrix.orthoM(mtrxProjection, 0, 0f, mScreenWidth, 0.0f, mScreenHeight, 0, 50);
// Set the camera position (View matrix)
Matrix.setLookAtM(mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
card1 = new CardView(mContext, 1);
card2 = new CardView(mContext, 2);
}
}
public class CardView {
// Misc
private Context mContext;
// Geometric variables
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static float vertices[] = {
10.0f, 300f, 0.0f,
10.0f, 100f, 0.0f,
450f, 100f, 0.0f,
450f, 300f, 0.0f,
};
private static float vertices2[] = {
450.0f, 600f, 0.0f,
450.0f, 300f, 0.0f,
900f, 300f, 0.0f,
900f, 600f, 0.0f,
};
private final short indices[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private static float uvs[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private FloatBuffer uvBuffer;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public CardView(Context c, int index)
{
// Save context reference
mContext = c;
// Create the triangles
SetupCard(index);
// Create the image information
SetupImage(index);
// Set the clear color to black
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);
// Create the shaders, solid color
int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);
riGraphicTools.sp_SolidColor = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_SolidColor); // creates OpenGL ES program executables
// Create the shaders, images
vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);
riGraphicTools.sp_Image = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_Image); // creates OpenGL ES program executables
// Set our shader programm
GLES20.glUseProgram(riGraphicTools.sp_Image);
}
public void SetupImage(int index)
{
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
// Generate Textures, if more needed, alter these numbers.
int[] texturenames = new int[1];
GLES20.glGenTextures(1, texturenames, 0);
// Retrieve our image from resources.
//int id = mContext.getResources().getIdentifier("drawable/test_backbground1", null, mContext.getPackageName());
if(index == 1) {
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground1, index);
}else{
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground2, index);
}
}
public void SetupCard(int index)
{
if( index == 1) {
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}else{
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices2.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices2);
vertexBuffer.position(0);
}
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void Draw(float[] mvpMatrix){
// clear Screen and Depth Buffer, we have set the clear color as black.
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// Get handle to texture coordinates location
int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" );
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray ( mTexCoordLoc );
// Prepare the texturecoordinates
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
false,
0, uvBuffer);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, mvpMatrix, 0);
// Get handle to textures locations
int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" );
// Set the sampler texture unit to 0, where we have saved the texture.
GLES20.glUniform1i ( mSamplerLoc, 0);
// Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
}
}
public class riGraphicTools {
private static final String TAG = "GraphicTools";
// Program variables
public static int sp_SolidColor;
public static int sp_Image;
/* SHADER Solid
*
* This shader is for rendering a colored primitive.
*
*/
public static final String vs_SolidColor =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
public static final String fs_SolidColor =
"precision mediump float;" +
"void main() {" +
" gl_FragColor = vec4(0.5,0,0,1);" +
"}";
/* SHADER Image
*
* This shader is for rendering 2D images straight from a texture
* No additional effects.
*
*/
public static final String vs_Image =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_texCoord;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_texCoord = a_texCoord;" +
"}";
public static final String fs_Image =
"precision mediump float;" +
"varying vec2 v_texCoord;" +
"uniform sampler2D s_texture;" +
"void main() {" +
" gl_FragColor = texture2D( s_texture, v_texCoord );" +
"}";
/**
* Utility method for compiling a OpenGL shader.
*
* <p><strong>Note:</strong> When developing shaders, use the checkGlError()
* method to debug shader coding errors.</p>
*
* #param type - Vertex or fragment shader type.
* #param shaderCode - String containing the shader code.
* #return - Returns an id for the shader.
*/
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 the shader
return shader;
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* FandeckRenderer.checkGlError("glGetUniformLocation");</pre>
*
* If the operation is not successful, the check throws an error.
*
* #param glOperation - Name of the OpenGL call to check.
*/
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 int loadTexture(final Context context, final int resourceId, int[] textureHandler, int index)
{
//GLES20.glGenTextures(1, textureHandler, index);
if (textureHandler[index] != 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, textureHandler[index]);
// 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 (textureHandler[index] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandler[0];
}
}
Ok, the problem was the textureHandle, I have moved it to upper class to keep data, and in the method Draw I have added:
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[textureIndex]);
and it works.
the final code is the next one:
public class GLRenderer implements Renderer {
// Our matrices
private final float[] mtrxProjection = new float[16];
private final float[] mtrxView = new float[16];
private final float[] mtrxProjectionAndView = new float[16];
// Our screenresolution
float mScreenWidth = 1280;
float mScreenHeight = 768;
// Misc
Context mContext;
long mLastTime;
int mProgram;
CardView card1;
CardView card2;
public GLRenderer(Context c)
{
mContext = c;
mLastTime = System.currentTimeMillis() + 100;
}
public void onPause()
{
/* Do stuff to pause the renderer */
}
public void onResume()
{
/* Do stuff to resume the renderer */
mLastTime = System.currentTimeMillis();
}
#Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the current time
long now = System.currentTimeMillis();
// We should make sure we are valid and sane
if (mLastTime > now) return;
// Get the amount of time the last frame took.
long elapsed = now - mLastTime;
// Update our example
// Render our example
card1.Draw(mtrxProjectionAndView, 0);
card2.Draw(mtrxProjectionAndView , 1);
// Save the current time to see how long it took :).
mLastTime = now;
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// We need to know the current width and height.
mScreenWidth = width;
mScreenHeight = height;
// Redo the Viewport, making it fullscreen.
GLES20.glViewport(0, 0, (int)mScreenWidth, (int)mScreenHeight);
// Clear our matrices
for(int i=0;i<16;i++)
{
mtrxProjection[i] = 0.0f;
mtrxView[i] = 0.0f;
mtrxProjectionAndView[i] = 0.0f;
}
// Setup our screen width and height for normal sprite translation.
Matrix.orthoM(mtrxProjection, 0, 0f, mScreenWidth, 0.0f, mScreenHeight, 0, 50);
// Set the camera position (View matrix)
Matrix.setLookAtM(mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
card1 = new CardView(mContext, 0);
card2 = new CardView(mContext, 1);
}
}
public class CardView {
// Misc
private Context mContext;
// Geometric variables
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static float vertices[] = {
10.0f, 300f, 0.0f,
10.0f, 100f, 0.0f,
450f, 100f, 0.0f,
450f, 300f, 0.0f,
};
private static float vertices2[] = {
450.0f, 600f, 0.0f,
450.0f, 300f, 0.0f,
900f, 300f, 0.0f,
900f, 600f, 0.0f,
};
private final short indices[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private static float uvs[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private FloatBuffer uvBuffer;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
final int[] textureHandle;
public CardView(Context c, int index)
{
textureHandle = new int[2];
// Save context reference
mContext = c;
// Create the triangles
SetupCard(index);
// Create the image information
SetupImage(index);
// Set the clear color to black
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);
// Create the shaders, solid color
int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);
riGraphicTools.sp_SolidColor = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_SolidColor); // creates OpenGL ES program executables
// Create the shaders, images
vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);
riGraphicTools.sp_Image = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_Image); // creates OpenGL ES program executables
// Set our shader programm
GLES20.glUseProgram(riGraphicTools.sp_Image);
}
public void SetupImage(int index)
{
GLES20.glGenTextures(2, textureHandle, 0);
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
// Generate Textures, if more needed, alter these numbers.
int[] texturenames = new int[1];
GLES20.glGenTextures(1, texturenames, 0);
// Retrieve our image from resources.
//int id = mContext.getResources().getIdentifier("drawable/test_backbground1", null, mContext.getPackageName());
if(index == 1) {
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground1, textureHandle, index);
}else{
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground2, textureHandle, index);
}
}
public void SetupCard(int index)
{
if( index == 1) {
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}else{
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices2.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices2);
vertexBuffer.position(0);
}
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void Draw(float[] mvpMatrix, int textureIndex){
// clear Screen and Depth Buffer, we have set the clear color as black.
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[textureIndex]);
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// Get handle to texture coordinates location
int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" );
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray ( mTexCoordLoc );
// Prepare the texturecoordinates
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
false,
0, uvBuffer);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, mvpMatrix, 0);
// Get handle to textures locations
int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" );
// Set the sampler texture unit to 0, where we have saved the texture.
GLES20.glUniform1i ( mSamplerLoc, 0);
// Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
}
}
public class riGraphicTools {
// Program variables
public static int sp_SolidColor;
public static int sp_Image;
/* SHADER Solid
*
* This shader is for rendering a colored primitive.
*
*/
public static final String vs_SolidColor =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
public static final String fs_SolidColor =
"precision mediump float;" +
"void main() {" +
" gl_FragColor = vec4(0.5,0,0,1);" +
"}";
/* SHADER Image
*
* This shader is for rendering 2D images straight from a texture
* No additional effects.
*
*/
public static final String vs_Image =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_texCoord;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_texCoord = a_texCoord;" +
"}";
public static final String fs_Image =
"precision mediump float;" +
"varying vec2 v_texCoord;" +
"uniform sampler2D s_texture;" +
"void main() {" +
" gl_FragColor = texture2D( s_texture, v_texCoord );" +
"}";
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 the shader
return shader;
}
public static int loadTexture(final Context context, final int resourceId, int[] textureHandle , int index)
{
if (textureHandle[index] != 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[index]);
// 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[index] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[index];
}
}
I hope that will help someone.
Ok so I found this thread, which is what I believe I need to do. However, I am not using GL10 but GLES20. glOrthof isn't a part of the GLES20 API.
What I want to do is change the coordinate system's range from [-1, 1] to [0,1].
This is the normal system.
I would like to change it so that it is this.(Below)
The center of the screen is 0.5f, 0.5f, bottom left is 0f,0f, etc. It is only 2D.
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aPosition;" +
"attribute vec2 aTexCoordinate;" +
"varying vec2 vTexCoordinate;" +
"void main() {" +
" gl_Position = uMVPMatrix * aPosition;" +
" vTexCoordinate = aTexCoordinate;" +
"}";
Here is my Renderer class
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import com.matt.world.World;
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "OpenGLRenderer"; // Tag for the logcat error message
private final float[] mMVPMatrix = new float[16]; // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private final float[] mTranslationMatrix = new float[16];
private World mWorld; // World object
private long mStartTime; // Start time
public volatile float mPlayerInputX; // Variable to receive player coordinate input
public volatile float mPlayerInputY; // Variable to receive player coordinate input
public volatile boolean mFireWeapon = false; // Variable to handle player fire commands
public volatile boolean mToggleShield = false; // Variable to handle player shield toggle
public volatile boolean mToggleLaser = false; // Variable to handle player laser toggle
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) { // Called once when the surface is created
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set the background frame color
GLES20.glEnable(GLES20.GL_TEXTURE_2D); // Enable textures
mWorld = new World(this); // Initialize World object
}
#Override
public void onDrawFrame(GL10 unused) { // Called every frame
float[] scratch = new float[16]; // Container for matrix manipulations
float decimal = 1000000000.0f; // Divisor to move the decimal point for delta time
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | // Draw background color
GLES20.GL_DEPTH_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, // Set the camera position (View matrix)
0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, // Calculate the projection and view transformation
0, mProjectionMatrix, 0, mViewMatrix, 0);
float deltaTime = (System.nanoTime()-mStartTime)/decimal; // Calculate delta time
mStartTime = System.nanoTime(); // Get the time now for next frame calculations
mWorld.update(mMVPMatrix, deltaTime); // Update World
mWorld.draw(); // Draw World
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0,0, width, height); // Adjust the viewport based on geometry changes,
float ratio = (float) height / width; // such as screen rotation
Matrix.setIdentityM(mProjectionMatrix, 0); // Set this matrix to the identity matrix
Matrix.frustumM(mProjectionMatrix, // this projection matrix is applied to object coordinates
0,-ratio, ratio, 0, 1, 3, 7); // in the onDrawFrame() method*/
mStartTime = System.nanoTime(); // keep record of what the current time is
}
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type); // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
GLES20.glShaderSource(shader, shaderCode); // add the source code to the shader and
GLES20.glCompileShader(shader); // compile it
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);
}
}
}
The Ortho is still a way to go but you need to use a matrix. So in your shader you would use a matrix as a uniform which then multiplies the input position.
In general there is no way at all you can change the coordinate system. The openGL works with normalized coordinates so no matter what you do you will always have a 0 in the center of the buffer and borders are [-1, 1] from left to right and bottom to top and the same goes for depth when using depth buffer.
So what the orthographical matrix does is scale your input coordinates to the openGL internal coordinate system and this is the procedure that even ES1 does but the difference is that the matrices are already generated for you and all the coordinates are multiplied for you in the fixed pipeline.
I'm making an app for android, and I have drawn a triangle, I can rotate it but not move it!
my question why?
Say if you need more info!
Source Code:
package com.uraniumdevs.projectx;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
public class MyGL20Renderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private Triangle mTriangle;
private Square mSquare;
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private final float[] mTranslationMatrix = new float[16];
// Declare as volatile because we are updating it from another thread
public volatile float Angle;
public volatile float Tempx;
public volatile float Tempy;
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mTriangle = new Triangle();
mSquare = new Square();
}
#Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Create a rotation for the triangle
Matrix.setRotateM(mRotationMatrix, 0, Angle, 0, 0, -1.0f);
Matrix.translateM(mTranslationMatrix, 0, Tempx, Tempy, 0);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTranslationMatrix, 0, mMVPMatrix, 0);
mTriangle.draw(mMVPMatrix);
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
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;
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* MyGLRenderer.checkGlError("glGetUniformLocation");
* </pre>
*
* If the operation is not successful, the check throws an error.
*
* #param glOperation
* - Name of the OpenGL call to check.
*/
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);
}
}
}
class Triangle {
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;" + "void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" + "}";
private final String fragmentShaderCode = "precision mediump float;"
+ "uniform vec4 vColor;" + "void main() {"
+ " gl_FragColor = vColor;" + "}";
private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
public static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.1f, 0.0f, // top
-0.1f, -0.1f, 0.0f, // bottom left
0.1f, -0.1f, 0.0f // bottom right
};
private final int vertexCount;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
// vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGL20Renderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
// to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 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, vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGL20Renderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGL20Renderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
class Square {
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;" + "void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" + "}";
private final String fragmentShaderCode = "precision mediump float;"
+ "uniform vec4 vColor;" + "void main() {"
+ " gl_FragColor = vColor;" + "}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
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 final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw
// vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
// vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.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);
// prepare shaders and OpenGL program
int vertexShader = MyGL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGL20Renderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
// to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 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, vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGL20Renderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGL20Renderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I think we are modifying the same OpenGLES20Activity from the Android developer training guide
http://developer.android.com/training/graphics/opengl/environment.html
In order to move and rotate the triangle, you may do this:
(In order to make the variables accessible from other class object, I made them public.)
Add a mModelMatrix for the Triangle object. This is used to set the location and direction of the triangle. Initialize it in the constructor.
public class Triangle {
...
public float[] mModelMatrix = new float[16];
...
public Triangle() {
...
Matrix.setIdentityM(mModelMatrix, 0);
...
}
Use Matrix.translateM and Matrix.rotateM (not Matrix.setRotateM) to move and rotate the triangle. You can do these as many times as you like.
Matrix.translateM(mTriangle.mModelMatrix, 0, 0.5f, 0f, 0f);
Matrix.rotateM(mTriangle.mModelMatrix, 0, -45f, 0, 0, -1.0f);
Multiply the mViewMatrix and the mProjectMatrix to create a mMVPMatrix, same as in the example. Then multiply the mMVPMatrix with the triangle's mModelMatrix.
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
}
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Combine the model's translation & rotation matrix
// with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mTriangle.mModelMatrix, 0);
// Draw triangle
mTriangle.draw(scratch);
}
I am surprised this works at all. In OpenGL if you use proper column-major matrices, you should multiply: ModelViewProjectionMatrix * VertexPosition for transforming your vertices.
This is very likely the cause of your problem.
In other words, in both of your shaders
gl_Position = vPosition * uMVPMatrix;
should probably be:
gl_Position = uMVPMatrix * vPosition;
Your original matrix multiplication would work in Direct3D, because it uses row-major matrices. This is something to keep in mind if you are porting code from HLSL.
I have created a pinch zoom with a scale detector which in turn calls the following renderer.
This uses the projection matrix to do the zoom and then scales the eye per the zoom when panning.
public class vboCustomGLRenderer implements GLSurfaceView.Renderer {
// Store the model matrix. This matrix is used to move models from object space (where each model can be thought
// of being located at the center of the universe) to world space.
private float[] mModelMatrix = new float[16];
// Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
// it positions things relative to our eye.
private float[] mViewMatrix = new float[16];
// Store the projection matrix. This is used to project the scene onto a 2D viewport.
private float[] mProjectionMatrix = new float[16];
// Allocate storage for the final combined matrix. This will be passed into the shader program.
private float[] mMVPMatrix = new float[16];
// This will be used to pass in the transformation matrix.
private int mMVPMatrixHandle;
// This will be used to pass in model position information.
private int mPositionHandle;
// This will be used to pass in model color information.
private int mColorUniformLocation;
// How many bytes per float.
private final int mBytesPerFloat = 4;
// Offset of the position data.
private final int mPositionOffset = 0;
// Size of the position data in elements.
private final int mPositionDataSize = 3;
// How many elements per vertex for double values.
private final int mPositionFloatStrideBytes = mPositionDataSize * mBytesPerFloat;
// Position the eye behind the origin.
public double eyeX = default_settings.mbrMinX + ((default_settings.mbrMaxX - default_settings.mbrMinX)/2);
public double eyeY = default_settings.mbrMinY + ((default_settings.mbrMaxY - default_settings.mbrMinY)/2);
// Position the eye behind the origin.
//final float eyeZ = 1.5f;
public float eyeZ = 1.5f;
// We are looking toward the distance
public double lookX = eyeX;
public double lookY = eyeY;
public float lookZ = 0.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
public float upX = 0.0f;
public float upY = 1.0f;
public float upZ = 0.0f;
public double mScaleFactor = 1;
public double mScrnVsMapScaleFactor = 0;
public vboCustomGLRenderer() {}
public void setEye(double x, double y){
eyeX -= (x / screen_vs_map_horz_ratio);
lookX = eyeX;
eyeY += (y / screen_vs_map_vert_ratio);
lookY = eyeY;
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, (float)eyeX, (float)eyeY, eyeZ, (float)lookX, (float)lookY, lookZ, upX, upY, upZ);
}
public void setScaleFactor(float scaleFactor, float gdx, float gdy){
mScaleFactor *= scaleFactor;
mRight = mRight / scaleFactor;
mLeft = -mRight;
mTop = mTop / scaleFactor;
mBottom = -mTop;
//Need to calculate the shift in the eye when zooming on a particular spot.
//So get the distance between the zoom point and eye point, figure out the
//new eye point by getting the factor of this distance.
double eyeXShift = (((mWidth / 2) - gdx) - (((mWidth / 2) - gdx) / scaleFactor));
double eyeYShift = (((mHeight / 2) - gdy) - (((mHeight / 2) - gdy) / scaleFactor));
screen_vs_map_horz_ratio = (mWidth/(mRight-mLeft));
screen_vs_map_vert_ratio = (mHeight/(mTop-mBottom));
eyeX -= (eyeXShift / screen_vs_map_horz_ratio);
lookX = eyeX;
eyeY += (eyeYShift / screen_vs_map_vert_ratio);
lookY = eyeY;
// Set the scale (Projection matrix)
Matrix.frustumM(mProjectionMatrix, 0, (float)mLeft, (float)mRight, (float)mBottom, (float)mTop, near, far);
}
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
//White
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
// Set the view matrix. This matrix can be said to represent the camera position.
// NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
// view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
Matrix.setLookAtM(mViewMatrix, 0, (float)eyeX, (float)eyeY, eyeZ, (float)lookX, (float)lookY, lookZ, upX, upY, upZ);
final String vertexShader =
"uniform mat4 u_MVPMatrix; \n" // A constant representing the combined model/view/projection matrix.
+ "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.
+ "void main() \n" // The entry point for our vertex shader.
+ "{ \n"
+ " v_Color = a_Color; \n" // Pass the color 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"; // normalized screen coordinates.
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 vec4 u_Color; \n" // This is the color from the vertex shader interpolated across the
// triangle per fragment.
+ "void main() \n" // The entry point for our fragment shader.
+ "{ \n"
+ " gl_FragColor = u_Color; \n" // Pass the color directly through the pipeline.
+ "} \n";
// Load in the vertex shader.
int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
if (vertexShaderHandle != 0)
{
// Pass in the shader source.
GLES20.glShaderSource(vertexShaderHandle, vertexShader);
// Compile the shader.
GLES20.glCompileShader(vertexShaderHandle);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
}
}
if (vertexShaderHandle == 0)
{
throw new RuntimeException("Error creating vertex shader.");
}
// Load in the fragment shader shader.
int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if (fragmentShaderHandle != 0)
{
// Pass in the shader source.
GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);
// Compile the shader.
GLES20.glCompileShader(fragmentShaderHandle);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(fragmentShaderHandle);
fragmentShaderHandle = 0;
}
}
if (fragmentShaderHandle == 0)
{
throw new RuntimeException("Error creating fragment shader.");
}
// Create a program object and store the handle to it.
int programHandle = GLES20.glCreateProgram();
if (programHandle != 0)
{
// Bind the vertex shader to the program.
GLES20.glAttachShader(programHandle, vertexShaderHandle);
// Bind the fragment shader to the program.
GLES20.glAttachShader(programHandle, fragmentShaderHandle);
// Bind attributes
GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
GLES20.glBindAttribLocation(programHandle, 1, "a_Color");
// Link the two shaders together into a program.
GLES20.glLinkProgram(programHandle);
// Get the link status.
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
// If the link failed, delete the program.
if (linkStatus[0] == 0)
{
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0)
{
throw new RuntimeException("Error creating program.");
}
// Set program handles. These will later be used to pass in values to the program.
mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
mColorUniformLocation = GLES20.glGetUniformLocation(programHandle, "u_Color");
// Tell OpenGL to use this program when rendering.
GLES20.glUseProgram(programHandle);
}
static double mWidth = 0;
static double mHeight = 0;
static double mLeft = 0;
static double mRight = 0;
static double mTop = 0;
static double mBottom = 0;
static double mRatio = 0;
double screen_width_height_ratio;
double screen_height_width_ratio;
final float near = 1.5f;
final float far = 10.0f;
double screen_vs_map_horz_ratio = 0;
double screen_vs_map_vert_ratio = 0;
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
// Set the OpenGL viewport to the same size as the surface.
GLES20.glViewport(0, 0, width, height);
screen_width_height_ratio = (double) width / height;
screen_height_width_ratio = (double) height / width;
//Initialize
if (mRatio == 0){
mWidth = (double) width;
mHeight = (double) height;
//map height to width ratio
double map_extents_width = default_settings.mbrMaxX - default_settings.mbrMinX;
double map_extents_height = default_settings.mbrMaxY - default_settings.mbrMinY;
double map_width_height_ratio = map_extents_width/map_extents_height;
if (screen_width_height_ratio > map_width_height_ratio){
mRight = (screen_width_height_ratio * map_extents_height)/2;
mLeft = -mRight;
mTop = map_extents_height/2;
mBottom = -mTop;
}
else{
mRight = map_extents_width/2;
mLeft = -mRight;
mTop = (screen_height_width_ratio * map_extents_width)/2;
mBottom = -mTop;
}
mRatio = screen_width_height_ratio;
}
if (screen_width_height_ratio != mRatio){
final double wRatio = width/mWidth;
final double oldWidth = mRight - mLeft;
final double newWidth = wRatio * oldWidth;
final double widthDiff = (newWidth - oldWidth)/2;
mLeft = mLeft - widthDiff;
mRight = mRight + widthDiff;
final double hRatio = height/mHeight;
final double oldHeight = mTop - mBottom;
final double newHeight = hRatio * oldHeight;
final double heightDiff = (newHeight - oldHeight)/2;
mBottom = mBottom - heightDiff;
mTop = mTop + heightDiff;
mWidth = (double) width;
mHeight = (double) height;
mRatio = screen_width_height_ratio;
}
screen_vs_map_horz_ratio = (mWidth/(mRight-mLeft));
screen_vs_map_vert_ratio = (mHeight/(mTop-mBottom));
Matrix.frustumM(mProjectionMatrix, 0, (float)mLeft, (float)mRight, (float)mBottom, (float)mTop, near, far);
}
ListIterator<mapLayer> orgNonAssetCatLayersList_it;
ListIterator<FloatBuffer> mapLayerObjectList_it;
ListIterator<Byte> mapLayerObjectTypeList_it;
mapLayer MapLayer;
#Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
drawPreset();
orgNonAssetCatLayersList_it = default_settings.orgNonAssetCatMappableLayers.listIterator();
while (orgNonAssetCatLayersList_it.hasNext()) {
MapLayer = orgNonAssetCatLayersList_it.next();
if (MapLayer.BatchedPointVBO != null){
}
if (MapLayer.BatchedLineVBO != null){
drawLineString(MapLayer.BatchedLineVBO, MapLayer.lineStringObjColor);
}
if (MapLayer.BatchedPolygonVBO != null){
drawPolygon(MapLayer.BatchedPolygonVBO, MapLayer.polygonObjColor);
}
}
}
private void drawPreset()
{
Matrix.setIdentityM(mModelMatrix, 0);
// 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);
}
private void drawLineString(final FloatBuffer geometryBuffer, final float[] colorArray)
{
// Pass in the position information
geometryBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, mPositionFloatStrideBytes, geometryBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glUniform4f(mColorUniformLocation, colorArray[0], colorArray[1], colorArray[2], 1f);
GLES20.glLineWidth(2.0f);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, geometryBuffer.capacity()/mPositionDataSize);
}
private void drawPolygon(final FloatBuffer geometryBuffer, final float[] colorArray)
{
// Pass in the position information
geometryBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, mPositionFloatStrideBytes, geometryBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glUniform4f(mColorUniformLocation, colorArray[0], colorArray[1], colorArray[2], 1f);
GLES20.glLineWidth(1.0f);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, geometryBuffer.capacity()/mPositionDataSize);
}
}
This works very well up until it gets to a certain level then the panning starts jumping. After testing I found that it was because the floating point value of the eye, could not cope with such a small shift in position. I keep my x and y eye values in doubles so it continues to calculate shifting positions, then when calling setLookAtM() I convert them to floats.
So need I need to change the way the zoom works. I was thinking instead of zooming with the projection, scaling the model larger or smaller.
The setScaleFactor() function in my code will change, by removing the projection and eye shifting.
There is a Matrix.scaleM(m,Offset,x,y,z) function but I am unsure how or where to implement this.
Could use some suggestions on how to accomplish this.
[Edit] 24/7/2013
I tried altering setScaleFactor() like so:
public void setScaleFactor(float scaleFactor, float gdx, float gdy){
mScaleFactor *= scaleFactor;
}
and in drawPreset()
private void drawPreset()
{
Matrix.setIdentityM(mModelMatrix, 0);
//*****Added scaleM
Matrix.scaleM(mModelMatrix, 0, (float)mScaleFactor, (float)mScaleFactor, 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);
}
Now as soon as you do a zoom the image disappears from the screen.
Actually I found it right off to the right hand side. I could still pan over to it.
Still not sure on what I should be scaling to zoom, is it the model, view or view-model?
I have found out that if you take the center of your model back to the origin (0,0) it allows you to extend your zoom capabilities. With my x coord data which was between 152.6 and 152.7.
Taking it back to the origin by the offset 152.65, which needs to be applied to the data before loading it into the floatbuffer.
So the width of the data becomes 0.1 or 0.05 on each side, allowing for more precision on the trailing end of the value.
I've been following the tutorial at http://developer.android.com/resources/tutorials/opengl/opengl-es20.html for OpenGL ES on android. I've gotten to the, "Apply Projection and Camera View" section however I always seem to get a blank screen with no triangle, the previous section worked perfectly fine. I also tried just copy pasting the entire tutorial into my code but got the same result. Changing the line:
gl_Position = uMVPMatrix * vPosition;
to:
gl_Position = vPosition;
puts the application back to the first section (triangle stretches depending on screen orientation). Any idea what the problem is? Here's the code I have so far just in case I missed something:
public class GLTest20Renderer implements Renderer {
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; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
"} \n";
private FloatBuffer triangleVB;
private int mProgram;
private int maPositionHandle;
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mMMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
initShapes();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL 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 program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
}
public void onDrawFrame(GL10 unused) {
GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT );
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the triangle data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, triangleVB);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// Apply a ModelView Projection transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
private void initShapes() {
float triangleCoords[] = {
// X, Y, Z
-0.5f, -0.25f, 0,
0.5f, -0.25f, 0,
0.0f, 0.559016994f, 0
};
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer
triangleVB.position(0); // set the buffer to read the first coordinate
}
private 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;
}
}
I'm running all this on a Samsung Galaxy S2.
Fixed, just changed the near point in the lookat to be under 3:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 7);