I'm trying to get my 2D-background image moving. In the fututre it will be implemented as automatic movement (looks like camera follow some object). A typical example - angry birds game. Camera follows the bird during attack.
Currently, I just change the values in setLookAtM(). Here is the code:
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE_MINUS_SRC_ALPHA);
// background
bg = new BackGround(mActivityContext);
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(mVMatrix, 0, -1f, 0f, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.setIdentityM(mTrMatrix, 0);
bg.draw(mMVPMatrix);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width , height );
float ratio = (float) width / height;
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
class BackGround {
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -15.5f, 15.5f, 0.0f, // top left
-15.5f, -15.5f, 0.0f, // bottom left
15.5f, -15.5f, 0.0f, // bottom right
15.5f, 15.5f, 0.0f }; // top right
int mProgram;
int mPositionHandle;
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final String vertexShaderCode = "attribute vec2 a_TexCoordinate;" +
"varying vec2 v_TexCoordinate;" +
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"v_TexCoordinate = a_TexCoordinate;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
"gl_FragColor = ( texture2D(u_Texture, v_TexCoordinate));" +
"}";
public BackGround(final Context context) {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
mActivityContext = context;
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
final float[] cubeTextureCoordinateData = {
-15.5f, 15.5f,
-15.5f, -15.5f,
15.5f, -15.5f,
15.5f, 15.5f
};
mCubeTextureCoordinates =
ByteBuffer.allocateDirect(cubeTextureCoordinateData.length *
4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glBindAttribLocation(mProgram, 0, "a_TexCoordinate");
GLES20.glLinkProgram(mProgram);
// load background bitmap 256x256px
mTextureDataHandle = loadTexture(mActivityContext, R.drawable.mud);
}
public void draw(float[] mvpMatrix) {
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
mTextureUniformHandle = GLES20.glGetAttribLocation(mProgram, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
GLES20.glUniform1i(mTextureUniformHandle, 0);
mCubeTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize,
GLES20.GL_FLOAT, false, 0, mCubeTextureCoordinates);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
The problem is that I don't know how to spread my background (256x256) over big area (not limited by screen size). currenty, texture is repeated in the screen edges only. And when im trying to move it (using SetLookAtm()) it moves ok, but leaves empty stripe behind:
Initial background image (bmp-reversed):
ab
cd
Background image at the screen:
--------
|ababab|
|cdcdcd|
|ababab|
|cdcdcd|
|ababab|
|cdcdcd|
--------
Background image after move (right ->)
--------
| abab|
| cdcd|
| abab|
| cdcd|
| abab|
| cdcd|
--------
Direction (left, right, up, down) doesnt matter. It only has effect on which side stripe appears
Either:
A) Define your background rectangle so that it extends beyond the ends of the screen (and "scroll" the background by animating its position)
--------
|ababab|ababab
|cdcdcd|cdcdcd
|ababab|ababab
|cdcdcd|cdcdcd
|ababab|ababab
|cdcdcd|cdcdcd
--------
or:
B) Define the bg rectangle filling up the screen as before, but animate the texture coordinates to make it scroll. Eg: If your texcoords form a rectangle (0.00,0.00)->(1.00,1.00), then scroll the bg left by changing texcoords to be (0.05,0.00)->(1.05,1.00). Make sure you've set glTexParameteri as follows:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
Related
I am new to android opengl development.I want to fill polygon with image as shown below using opengl-es 2.0 in android. I am able to draw square,triangle but not able to fill it with repeating image. Anyone know please help me.
Square.java
public class Square {
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_TextureCoordinates;" +
"varying vec2 v_TextureCoordinates;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_TextureCoordinates = a_TextureCoordinates;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D u_TextureUnit;"+
"varying vec2 v_TextureCoordinates;" +
"void main() {" +
" gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);" +
"}";
private final FloatBuffer vertexBuffer;
private final FloatBuffer textureBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-1.0f, 1.0f, 0.0f, // top left
1.0f, 1.0f, 0.0f, // top right
1.0f, -1.0f, 0.0f, // bottom right
-1.0f, -1.0f, 0.0f // bottom left
};
final float[] previewTextureCoordinateData =
{
0.0f,0.0f, // top left
1.0f,0.0f, // Top-right
1.0f,1.0f, // Bottom-right
0.0f,1.0f, // Bottom-left
};
private int textureDataHandle;
private int textureUniformHandle;
private int textureCoordinateHandle;
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 };
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
private 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]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
// Set filtering
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);
// 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];}
public Square(Context c) {
// 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);
ByteBuffer texCoordinates = ByteBuffer.allocateDirect(previewTextureCoordinateData.length * 4);
texCoordinates.order(ByteOrder.nativeOrder());
textureBuffer = texCoordinates.asFloatBuffer();
textureBuffer.put(previewTextureCoordinateData);
textureBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
textureDataHandle = loadTexture(c, R.drawable.air_hockey_surface);
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);
textureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TextureCoordinates");
GLES20.glVertexAttribPointer(textureCoordinateHandle, 2, GLES20.GL_FLOAT, false,
0, textureBuffer);
GLES20.glEnableVertexAttribArray(textureCoordinateHandle);
textureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_TextureUnit");
MyGLRenderer.checkGlError("glGetUniformLocation");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureDataHandle);
GLES20.glUniform1i(textureUniformHandle, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}}
If you want the texture to repeat you need to set such texture coordinates. A texture coordinate is a relative pixel position in the texture where (0.0, 0,0) is the top-left position on the image and (1.0, 1.0) is the bottom right. Now no matter the vertex coordinates the texture part shown will always be the same. In your case then
final float[] previewTextureCoordinateData =
{
0.0f,0.0f, // top left
1.0f,0.0f, // Top-right
1.0f,1.0f, // Bottom-right
0.0f,1.0f, // Bottom-left
};
means you will be drawing a whole texture on the object. To make it repeat for instance 3 times in each of dimensions you need to multiply these:
final float[] previewTextureCoordinateData =
{
0.0f,0.0f, // top left
1.0f*3.0,0.0f, // Top-right
1.0f*3.0,1.0f*3.0, // Bottom-right
0.0f,1.0f*3.0, // Bottom-left
};
The repeating is already enabled on the texture so that should be it.
While learning how OpenGL ES works, I'm trying to get a textured square to appear on my screen. Here is the relevant code to the square. The code is based on the google example:
public class Shape2Square {
private static final String TAG = "Shape2Square";
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_TexCoordinate;"+
"varying vec2 v_TexCoordinate;"+
"void main() {" +
"v_TexCoordinate = a_TexCoordinate;"+
"gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D u_Texture"+
"varying vec2 v_TexCoordinate;"+
"void main() {" +
"gl_FragColor = texture2D(u_Texture, v_TexCoordinate);"+
"}";
private final FloatBuffer vertexBuffer;
private final FloatBuffer textureBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mMVPMatrixHandle;
private int mtexture;
private int mtexCoordHandler;
private int mtextureHandler;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -0.3f, -0.3f, 0.0f, // top left
-0.3f, 0.3f, 0.0f, // bottom left
0.3f, 0.3f, 0.0f, // bottom right
0.3f, -0.3f, 0.0f }; // top right
// u,v
static float texturedata[] = {0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f};
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 };
private final int vertexStride = COORDS_PER_VERTEX * 4;
public Shape2Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
ByteBuffer bbtexture = ByteBuffer.allocateDirect(texturedata.length*4);
bbtexture.order(ByteOrder.nativeOrder());
textureBuffer = bbtexture.asFloatBuffer();
textureBuffer.put(texturedata);
textureBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = CommonMethods.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = CommonMethods.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram();
Shape2Square.checkGlError("glCreateProgram");
GLES20.glAttachShader(mProgram, vertexShader);
Shape2Square.checkGlError("glAttachShader");
GLES20.glAttachShader(mProgram, fragmentShader);
Shape2Square.checkGlError("glAttachShader");
GLES20.glLinkProgram(mProgram);
Shape2Square.checkGlError("glLinkProgram");
}
The trouble I get from logcat points to the "draw" method of this square. Note the CommonMethods.loadTexture simply loads a bitmap code into OPEN GL:
EDIT1: (added checkGlErrors all over the code)
public void draw(float[] mvpMatrix, int textureid) {
GLES20.glUseProgram(mProgram);
Shape2Square.checkGlError("glUseProgram");
vertexBuffer.position(0);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
Shape2Square.checkGlError("glVertexAttribPointer");
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
Shape2Square.checkGlError("glVertexAttribPointer");
GLES20.glEnableVertexAttribArray(mPositionHandle);
Shape2Square.checkGlError("glEnableVertexAttribArray");
textureBuffer.position(0);
mtexCoordHandler = GLES20.glGetAttribLocation(mProgram,"a_TexCoordinate");
Shape2Square.checkGlError("glGetAttribLocation");
GLES20.glEnableVertexAttribArray(mtexCoordHandler);
Shape2Square.checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(mtexCoordHandler, 2, GLES20.GL_FLOAT, false, 8, textureBuffer);
Shape2Square.checkGlError("glVertexAttribPointer");
mtextureHandler = GLES20.glGetUniformLocation(mProgram, "u_Texture");
Shape2Square.checkGlError("mtextureHandler");
mtexture = CommonMethods.loadTexture(textureid);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
Shape2Square.checkGlError("glActiveTexture");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mtexture);
Shape2Square.checkGlError("glBindTexture");
GLES20.glUniform1i(mtextureHandler, 0);
Shape2Square.checkGlError("glUniform1i");
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Shape2Square.checkGlError("glGetUniformLocation");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
Shape2Square.checkGlError("glUniformMatrix4fv");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
Shape2Square.checkGlError("glDrawElements");
GLES20.glDisableVertexAttribArray(mPositionHandle);
Shape2Square.checkGlError("glDisableVertexAttribArray");
}
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);
}
}
Doing this causes an error and forces the application to shut down. logcat leads to glGetUniformLocation with the most likely culprit of mMVPMatrixHandle after messing about with the code, which is strange, because it worked before. To prove my point, if I delete all the Texture code and add a vColor instead (both in the actual code and the fragmentShadercode) with all the usual relevance, then I will get the coloured square. I don't understand how the mistake lies with glGetUniformLocation or how to fix it so any help is appreciated.
EDIT1: So I tried to blank out the checkglerror of the "glgetuniformlocation" but this comes to a disappointment as now the error comes in the form of "glUniformMatrix4fv". Something is terribly wrong...
EDIT2: decided to use checkglerror all over the code, and so far it seems that the ENTIRE draw method (including the glUseProgram) has an error. The gl bits which occur before the draw method (which includes attaching shader and linking) are ok with no errors.
Ok I found out what i did wrong (im a noob...).
The fragmentShadercode after u_Texture should have ";" behind it. Can't believe that held me back 2 days :(. Anyways thanks for the help ClayMontgomery. I have drawn the shape upside down though, but i'm pretty sure I can fix that.
I've look everywhere to understand how to translate shapes with openGl es 2.0 but I can't find the right way. Rotation and scaling work fine.
I tried it with the android openGl es 2.0 tutorial but the shape is more distorded than translated.
Here's the code (almost the same than the android code sample http://developer.android.com/shareables/training/OpenGLES.zip, except of the line to translate:
public class MyGLRenderer 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];
// Declare as volatile because we are updating it from another thread
public volatile float mAngle;
#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);
// Draw square
mSquare.draw(mMVPMatrix);
// Create a rotation for the triangle
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
// Draw triangle
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);
}
}
}
And here's the square class with the translation transformation:
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 = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.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");
MyGLRenderer.checkGlError("glGetUniformLocation");
//TRANSLATION
float[] transMatrix = new float[16];
Matrix.setIdentityM(transMatrix,0);
Matrix.translateM(transMatrix,0,5.0f,0,0);
Matrix.multiplyMM(transMatrix,0,mvpMatrix,0,transMatrix,0);
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, transMatrix, 0);
MyGLRenderer.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 it's related with the vertex shader code but I can't figure it out?
I am doing the translation of the matrix within the onDrawFrame method of the Renderer, so my code looks like this:
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrixS, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//translate the Matrix
Matrix.translateM(mViewMatrixS, 0, 2f, 1f, 0);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrixS, 0, mProjectionMatrix, 0, mViewMatrixS, 0);
// Draw square
mSquare.draw(mMVPMatrix);
And this does the job of translating my shape correctly. I don't know if this was the problem.
I find the answer on this post: Is Google's Android OpenGL tutorial teaching incorrect linear algebra?
Just invert uMVPMatrix and vPosition in the vertexShaderCode string to:
" gl_Position = uMVPMatrix * vPosition;"
The following link contains the answer. Took over a day to locate it. Posting here to help others as I seen this post many times. OpenGL ES Android Matrix Transformations
Android exmaple project was wrong and has just been updated finally. FYI.
You might be seeing the distortion because of the perspective camera. Try reducing the distance you translate the objects by.
If I have custom renderer in opengl-es prepared:
public void onDrawFrame(GL10 gl)
{
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// here i want to draw line from [0, 0, 0] to [1, 0, 0]
}
What is the easiest and shortest way to draw line?
In ordinary OpenGL it is:
glBegin(GL_LINES);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glEnd();
But how can i get the same effect with OpenGL ES?
I am new to new to OpenGL ES 2.0 but I created a line class.
public class Line {
private FloatBuffer VertexBuffer;
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 = uMVPMatrix * vPosition;" +
"}";
private final String FragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
protected int GlProgram;
protected int PositionHandle;
protected int ColorHandle;
protected int MVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float LineCoords[] = {
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f
};
private final int VertexCount = LineCoords.length / COORDS_PER_VERTEX;
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.0f, 0.0f, 0.0f, 1.0f };
public Line() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
LineCoords.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(LineCoords);
// set the buffer to read the first coordinate
VertexBuffer.position(0);
int vertexShader = ArRenderer.loadShader(GLES20.GL_VERTEX_SHADER, VertexShaderCode);
int fragmentShader = ArRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, FragmentShaderCode);
GlProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(GlProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(GlProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(GlProgram); // creates OpenGL ES program executables
}
public void SetVerts(float v0, float v1, float v2, float v3, float v4, float v5) {
LineCoords[0] = v0;
LineCoords[1] = v1;
LineCoords[2] = v2;
LineCoords[3] = v3;
LineCoords[4] = v4;
LineCoords[5] = v5;
VertexBuffer.put(LineCoords);
// set the buffer to read the first coordinate
VertexBuffer.position(0);
}
public void SetColor(float red, float green, float blue, float alpha) {
color[0] = red;
color[1] = green;
color[2] = blue;
color[3] = alpha;
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL ES environment
GLES20.glUseProgram(GlProgram);
// get handle to vertex shader's vPosition member
PositionHandle = GLES20.glGetAttribLocation(GlProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(PositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(PositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
VertexStride, VertexBuffer);
// get handle to fragment shader's vColor member
ColorHandle = GLES20.glGetUniformLocation(GlProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(ColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
MVPMatrixHandle = GLES20.glGetUniformLocation(GlProgram, "uMVPMatrix");
ArRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(MVPMatrixHandle, 1, false, mvpMatrix, 0);
ArRenderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_LINES, 0, VertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(PositionHandle);
}
}
And then in my Render class I create my line objects and to a container so the get draw in the by iterating over the items and calling the Line.draw method in onDrawFrame.
Here are some lines I create to make a horizon:
Line eastHorz = new Line();
eastHorz.SetVerts(10f, 10f, 0f, 10f, -10f, 0f);
eastHorz.SetColor(.8f, .8f, 0f, 1.0f);
Line northHorz = new Line();
northHorz.SetVerts(-10f, 10f, 0f, 10f, 10f, 0f);
northHorz.SetColor(0.8f, 0.8f, 0f, 1.0f);
Line westHorz = new Line();
westHorz.SetVerts(-10f, -10f, 0f, -10f, 10f, 0f);
westHorz.SetColor(0.8f, 0.8f, 0f, 1.0f);
Line southHorz = new Line();
southHorz.SetVerts(-10f, -10f, 0f, 10f, -10f, 0f);
southHorz.SetColor(0.8f, 0.8f, 0f, 1.0f);
Lines.add(eastHorz);
Lines.add(northHorz);
Lines.add(westHorz);
Lines.add(southHorz);
ArRenderer is my render class which holds the Lines, camera position etc and implements GLSurfaceView.Renderer. The loadShader method is:
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;
}
Thanks Rodney Lambert for the Line class you´ve provided. However it also would be nice to provide a simpler call in onDrawFrame, something like:
Line vertLine = new Line();
vertLine.SetVerts(-0.5f, 0.5f, 0f, -0.5f, -0.5f, 0f);
vertLine.SetColor(.8f, .8f, 0f, 1.0f);
vertLine.draw(mMVPMatrix);
vertLine.SetVerts(-0.5f, 0.5f, 0f, -0.5f, -0.5f, 0f);
creates definitely a line visible inside the viewport
I started from the google's android tutorial on open gl and then used this tutorial:
to add textures. Since the class structures differ a bit, I had to do some code-moving-renaming. What i ended up is:
Here is the texture file i used.
As one can see, the texture is somehow stretched along (1, 1), although the underlying object is a square.
Here is my quad's code, any help is appreciated.
public class GLSquare implements IGLObject {
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 vec2 a_TexCoordinate;"
+ "varying vec2 v_TexCoordinate;" + "attribute vec4 vPosition;"
+ "void main() {"
+
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition;"
+ "v_TexCoordinate = a_TexCoordinate;" + "}";
private final String fragmentShaderCode = "precision mediump float;"
+ "uniform sampler2D u_Texture;" + "varying vec2 v_TexCoordinate;"
+ "void main() {"
+ " gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" + "}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgramHandle;
private int mPositionHandle;
private int mMVPMatrixHandle;
private int mTextureDataHandle;
/** The texture pointer */
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -10f, 10f, 0.0f, // top left
-10f, -10f, 0.0f, // bottom left
10f, -10f, 0.0f, // bottom right
10f, 10f, 0.0f }; // top right
private FloatBuffer textureBuffer; // buffer holding the texture coordinates
private float texture[] = {
// Mapping coordinates for the vertices
0.0f, 1.0f, // top left (V2)
0.0f, 0.0f, // bottom left (V1)
1.0f, 1.0f, // top right (V4)
1.0f, 0.0f // bottom right (V3)
};
static float[] mTranslate = new float[16];
static float[] translatedMVP = new float[16];
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 };
public GLSquare(float x, float y, float z, Context context) {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
bb = ByteBuffer.allocateDirect(texture.length * 4);
bb.order(ByteOrder.nativeOrder());
textureBuffer = bb.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.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);
Matrix.setIdentityM(mTranslate, 0);
Matrix.translateM(mTranslate, 0, x, y, z);
// prepare shaders and OpenGL program
final int vertexShaderHandle = GLTools.compileShader(
GLES20.GL_VERTEX_SHADER, vertexShaderCode);
final int fragmentShaderHandle = GLTools.compileShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgramHandle = GLTools.createAndLinkProgram(vertexShaderHandle,
fragmentShaderHandle, new String[] { "a_Position", "a_Color",
"a_TexCoordinate" });
// Load the texture
mTextureDataHandle = GLTools.loadGLTexture(context, R.raw.stars1024);
}
public void draw(float[] vpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgramHandle);
// Pass in the position information
vertexBuffer.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT,
false, 0, vertexBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
int mTextureCoordinateHandle = GLES20.glGetAttribLocation(
mProgramHandle, "a_TexCoordinate");
// Pass in the texture coordinate information
textureBuffer.position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2,
GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle,
"uMVPMatrix");
WideOpenRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
Matrix.multiplyMM(translatedMVP, 0, vpMatrix, 0, mTranslate, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, translatedMVP, 0);
// Draw the cube.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
}
}
here is the methode loading texture:
public static int loadGLTexture(Context context, final int resourceId) {
Log.d("GLTools", "Loading texture...");
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]);
Log.d("GLTools", "Binding texture, setting parameter" + resourceId);
// 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];
}
Ok, i am now able to answer my own question.
The texture was just scaled relative to its origin = left bottom point (u, v) = (0, 0), which has irritated me, since I am new to opengl, I was expecting that it would be scaled relative to at least world origin, or object's centroid.