I am not getting expected coordinate values from gluUnProject function.
I will put some code first. Here is the function which get called on touch event
public float[] getWorldSpaceFromMouseCoordinates(float mouseX, float mouseY)
{
float[] finalCoord = { 0.0f, 0.0f, 0.0f, 0.0f };
// mouse Y needs to be inverted
mouseY = (float)_viewport[3] - mouseY;
float[] mouseZ = new float[1];
FloatBuffer fb = FloatBuffer.allocate(1);
GLES20.glReadPixels((int)mouseX, (int)mouseY, 1, 1, GLES20.GL_DEPTH_COMPONENT, GLES20.GL_FLOAT, fb);
int result = GLU.gluUnProject(mouseX, mouseY, fb.get(0), mViewMatrix, 0, mProjectionMatrix, 0, _viewport, 0, finalCoord, 0);
float[] temp2 = new float[4];
Matrix.multiplyMV(temp2, 0, mViewMatrix, 0, finalCoord, 0);
if(result == GL10.GL_TRUE){
finalCoord[0] = temp2[0] / temp2[3];
finalCoord[1] = temp2[1] / temp2[3];
finalCoord[2] = temp2[2] / temp2[3];
}
Log.d("Coordinate:", "" + temp2[0] + "," + temp2[1] + "," + temp2[2]);
return finalCoord;
}
here is setting up matrices
#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);
_viewport = new int[] { 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, 2, 7);
}
setting up modelview matrix (note that model matrix is just an identity.)
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
So as per my understanding my expectation from this function is that it will give me world coordinates w.r.t origin which is not happening. I am creating a square with following coordinates
_vertices = new float [] { -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
however I am getting X values ranging from (.3, -.3) Y values ranging in (.5,-.5) and Z always -1.0 for whole viewport. X values in (0.2,-0.2) when touching corners of square and Y values in (0.15, -0.15).
Let me know if any more code s required.
So I found out what the problem was. glReadPixels() with GL_DEPTH_COMPONENT is not supported in OpenGL ES 2.0. That is because I was always getting wrong depth value and hence wrong coordinates. Now I had two choices whether to use an FBO and store depth using a shader OR I could do Ray Picking(Since I had only one object in scene Si was hoping that gluUnProject() will do). I chose former here is my code I hope it will help somebody (Its not not generic and geometry is hard coded)
public float[] getWorldSpaceFromMouseCoordinates(float mouseX, float mouseY)
{
float[] farCoord = { 0.0f, 0.0f, 0.0f, 0.0f };
float[] nearCoord = { 0.0f, 0.0f, 0.0f, 0.0f };
// mouse Y needs to be inverted
//mouseY = (float) _viewport[3] - mouseY;
// calling glReadPixels() with GL_DEPTH_COMPONENT is not supported in
// GLES so now i will try to implement ray picking
int result = GLU.gluUnProject(mouseX, mouseY, 1.0f, mViewMatrix, 0, mProjectionMatrix, 0, _viewport, 0,
farCoord, 0);
if (result == GL10.GL_TRUE)
{
farCoord[0] = farCoord[0] / farCoord[3];
farCoord[1] = farCoord[1] / farCoord[3];
farCoord[2] = farCoord[2] / farCoord[3];
}
result = GLU.gluUnProject(mouseX, mouseY, 0.0f, mViewMatrix, 0, mProjectionMatrix, 0, _viewport, 0, nearCoord,
0);
if (result == GL10.GL_TRUE)
{
nearCoord[0] = nearCoord[0] / nearCoord[3];
nearCoord[1] = nearCoord[1] / nearCoord[3];
nearCoord[2] = nearCoord[2] / nearCoord[3];
}
float [] dirVector = Vector.normalize(Vector.minus(farCoord, nearCoord));
float [] rayOrigin = {0.0f, 0.0f, 3.0f};
Log.d("Far Coordinate:", "" + farCoord[0] + "," + farCoord[1] + "," + farCoord[2]);
Log.d("Near Coordinate:", "" + nearCoord[0] + "," + nearCoord[1] + "," + nearCoord[2]);
float [] vertices = { -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
// calculate normal for square
float[] v1 = { vertices[3] - vertices[0], vertices[4] - vertices[1], vertices[5] - vertices[2]};
float[] v2 = { vertices[9] - vertices[0], vertices[10] - vertices[1], vertices[11] - vertices[2]};
float[] n = Vector.normalize(Vector.crossProduct(v1, v2));
// now calculate intersection point as per following link
// http://antongerdelan.net/opengl/raycasting.html
// our plane passes through origin so findint 't' ll be
float t = -(Vector.dot(rayOrigin, n) / Vector.dot(dirVector, n));
// now substitute above t in ray equation gives us intersection point
float [] intersectionPoint = Vector.addition(rayOrigin, Vector.scalarProduct(t, dirVector));
Log.d("Ipoint:", "" + intersectionPoint[0] + "," + intersectionPoint[1] + "," + intersectionPoint[2]);
return intersectionPoint;
}
Related
I need to plot few coordinates on screen, the coordinates without Z axis get displayed but the coordinates with Z axis values aren't being displayed. I tried normalizing the coordinates before plotting them which worked. When normalized all the coordinates get plotted. But in the case of unnormalized coordinates the vertices with Z axis are hidden.
OpenGL Version : ES 2.0
Coordinates:
float squareCoords[] = {
202.00002f, 244.00002f, 0.0f,
440.00003f, 625.00006f, 0.0f,
440.00003f, 625.00006f, 0.0f,
690.00006f, 186.0f,0.0f,
202.00002f, 244.00002f, 50.0f,
440.00003f, 625.00006f, 50.0f,
440.00003f, 625.00006f, 50.0f,
690.00006f, 186.0f, 50.0f
};
indices:
short[] drawOrder = {
0,1,2,3,
0,4,
1,5,
2,6,
4,5,6,7
};
Draw Code:
GLES20.glDrawElements(
GLES20.GL_LINES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
On Surface Changed code:
public void onSurfaceChanged(GL10 unused, int width, int height) {
mWidth = width;
mHeight = height;
GLES20.glViewport(0, 0, mWidth, mHeight);
float ratio = (float) mWidth / mHeight;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.orthoM(mProjMatrix, 0, 0f, width, 0.0f, height, 0, 50);
}
OnDraw:
public void onDrawFrame(GL10 unused) {
Square square = new Square();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
if (mFirstDraw)
mFirstDraw = false;
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
// float angle = 90;
// Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);
// angle += 0.7f;
if (angle > 360f)
angle = 0f;
Matrix.setLookAtM(mVMatrix, 0, 0f, 0f, 4f, 0f, 0f, 0f, 0f, 1f, 0f);
// projection x view = modelView
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Creating rotation matrix
Matrix.setRotateM(rotationMatrix, 0, angle, -1f, 0f, 0f);
// rotation x camera = modelView
float[] duplicateMatrix = Arrays.copyOf(mMVPMatrix, 16);
Matrix.multiplyMM(mMVPMatrix, 0, duplicateMatrix, 0, rotationMatrix, 0);
square.draw(mMVPMatrix);
}
I'm rotating the diagram to figure out whether the vertices on Z axis are drawn.
I personally think this line is the culprit, here I've given far value 50 and near value 0. I wonder what these values should be
Matrix.orthoM(mProjMatrix, 0, 0f, width, 0.0f, height, 0, 50);
The problem here was the value of far wasn't higher enough. I put far as 500
Matrix.orthoM(mProjectionMatrix, 0, 0f, width, 0.0f, height,0, 500);
and changed the coordinates to:
float squareCoords[] = {
202.00002f, 244.00002f, 0.0f,
440.00003f, 625.00006f, 0.0f,
440.00003f, 625.00006f, 0.0f,
690.00006f, 186.0f,0.0f,
202.00002f, 244.00002f, 200.0f,
440.00003f, 625.00006f, 200.0f,
440.00003f, 625.00006f, 200.0f,
690.00006f, 186.0f, 200.0f
};
Its working now.
I'm reading this awesome Beginning Android Games book, and I'm trying now to implement some tests myself.
I'm using OpenGl ES 1.0, and I'm OK now manipulating the view frustum, projections, translation, rotation, scale etc.
What I'm trying to do:
a) render a rocket to the screen, add some velocity and acceleration to it (using Euler's integration - add the acceleration to the velocity, and the velocity to the position) to simulate a path (parabola). - This is done, implemented without any issue.
b) Rotate the rocket, so that we can simulate also the inclination of the object along its path. - That's the problem.
To be clear, I'm adding the image below.
I can't figure out what's the correct angle to add to the rocket, between one frame and the next one.
I tried to get that with some geometry.
Obj Pos 1 is the rocket representation at frame 1.
Obj Pos 2 is the rocket representation, at the next frame (frame 2).
V1 is the vector that holds the center X and Y coordinates of the Obj Pos 1.
V2 i the vector that holds the center X and Y coordinates of the Obj Pos 2.
Tangent line 1 is the tangent line to the parabola, to where V1 points.
Tangent line 2 is the tangent line to the parabola, to where V2 points.
A1 is the angle between both vectors.
A2 is the angle between both tangent lines.
As far as I can see the correct angle to apply to the rocket, from frame 1 to frame 2 is angle A2. But how can I calculate it?
And, is this correct for game purposes? I mean, we don't need to be exact on the physics concept, we just need to be good enough to simulate animation and 'cheat' the user.
Followd the code below:
public class PersonalTest008Rocket extends GLGame {
#Override
public Screen getStartScreen() {
return new RocketScreen(this);
}
class RocketScreen extends Screen {
GLGraphics glGraphics;
Camera2D camera;
final float WORLD_WIDTH = 60;
final float WORLD_HEIGHT = 36;
float[] rocketRawData;
short[] rocketRawIndices;
BindableVertices rocketVertices;
DynamicGameObject rocket;
float angle;
Vector2 gravity;
public RocketScreen(Game game) {
super(game);
glGraphics = ((GLGame) game).getGLGraphics();
camera = new Camera2D(glGraphics, WORLD_WIDTH, WORLD_HEIGHT);
rocketRawData = new float[]{
// x, y, r, g, b, a
+4.0f, +0.0f, 1.0f, 0.0f, 0.0f, 1, // 0
+2.0f, +1.0f, 0.5f, 0.0f, 0.0f, 1, // 1
+2.0f, -1.0f, 0.5f, 0.0f, 0.0f, 1, // 2
-2.0f, +1.0f, 0.0f, 0.5f, 0.5f, 1, // 3
-2.0f, -1.0f, 0.0f, 0.5f, 0.5f, 1, // 4
-3.0f, +1.0f, 0.0f, 0.5f, 0.5f, 1, // 5
-3.0f, -1.0f, 0.0f, 0.5f, 0.5f, 1, // 6
-4.0f, +3.0f, 0.0f, 0.0f, 1.0f, 1, // 7
-5.0f, +0.0f, 0.0f, 0.0f, 1.0f, 1, // 8
-4.0f, -3.0f, 0.0f, 0.0f, 1.0f, 1 // 9
};
rocketRawIndices = new short[]{
0, 1, 2,
1, 4, 2,
1, 3, 4,
3, 4, 6,
3, 5, 6,
3, 7, 5,
5, 8, 6,
6, 9, 4
};
rocketVertices = new BindableVertices(glGraphics, 10, 3 * 8, true, false);
rocketVertices.setVertices(rocketRawData, 0, rocketRawData.length);
rocketVertices.setIndices(rocketRawIndices, 0, rocketRawIndices.length);
int velocity = 30;
angle = 45;
rocket = new DynamicGameObject(0, 0, 9, 6);
rocket.position.add(1, 1);
rocket.velocity.x = (float) Math.cos(Math.toRadians(angle)) * velocity;
rocket.velocity.y = (float) Math.sin(Math.toRadians(angle)) * velocity;
gravity = new Vector2(0, -10);
}
#Override
public void update(float deltaTime) {
rocket.velocity.add(gravity.x * deltaTime, gravity.y * deltaTime);
rocket.position.add(rocket.velocity.x * deltaTime, rocket.velocity.y * deltaTime);
}
#Override
public void present(float deltaTime) {
GL10 gl = glGraphics.getGL();
gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.setViewportAndMatrices();
gl.glTranslatef(rocket.position.x, rocket.position.y, 0);
gl.glRotatef(angle, 0, 0, 1);
rocketVertices.bind();
rocketVertices.draw(GL10.GL_TRIANGLES, 0, rocketRawIndices.length);
rocketVertices.unbind();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
}
You can calculate the angle based on the instantaneous velocity vector:
// Get direction to point the ship
mangleInDeg = (float) (Math.atan2(mRelSpeed.y, mRelSpeed.x) * 180 / Math.PI);
mangleInDeg += 90.0; // offset the angle to coincide with angle of the base ship image
In open GL there is a term called picking. Which is used to determine which object on the screen was selected. Can someone explain to me what the difference between using picking and putting a touch based listener in each and every instance of a object ex. A Cube class.
Hypothetically; What i want to do is demonstrate multiple cubes on a screen randomly. I figured if I give the Cube class a listener, upon touching the cube the listener should fire off accordingly for each cube pressed.
This is the code I would add the listener to.
Would this be possible or is picking necessary?
public class Cube extends Shapes {
private FloatBuffer mVertexBuffer;
private FloatBuffer mColorBuffer;
private ByteBuffer mIndexBuffer;
private Triangle[] normTris = new Triangle[12];
private Triangle[] transTris = new Triangle[12];
// every 3 entries represent the position of one vertex
private float[] vertices =
{
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f
};
// every 4 entries represent the color (r,g,b,a) of the corresponding vertex in vertices
private float[] colors =
{
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
// every 3 entries make up a triangle, every 6 entries make up a side
private byte[] indices =
{
0, 4, 5, 0, 5, 1,
1, 5, 6, 1, 6, 2,
2, 6, 7, 2, 7, 3,
3, 7, 4, 3, 4, 0,
4, 7, 6, 4, 6, 5,
3, 0, 1, 3, 1, 2
};
private float[] createVertex(int Index)
{
float[] vertex = new float[3];
int properIndex = Index * 3;
vertex[0] = vertices[properIndex];
vertex[1] = vertices[properIndex + 1];
vertex[2] = vertices[properIndex + 2];
return vertex;
}
public Triangle getTriangle(int index){
Triangle tri = null;
//if(index >= 0 && index < indices.length){
float[] v1 = createVertex(indices[(index * 3) + 0]);
float[] v2 = createVertex(indices[(index * 3) + 1]);
float[] v3 = createVertex(indices[(index * 3) + 2]);
tri = new Triangle(v1, v2, v3);
// }
return tri;
}
public int getNumberOfTriangles(){
return indices.length / 3;
}
public boolean checkCollision(Ray r, OpenGLRenderer renderer){
boolean isCollide = false;
int i = 0;
while(i < getNumberOfTriangles() && !isCollide){
float[] I = new float[3];
if(Shapes.intersectRayAndTriangle(r, transTris[i], I) > 0){
isCollide = true;
}
i++;
}
return isCollide;
}
public void translate(float[] trans){
for(int i = 0; i < getNumberOfTriangles(); i++){
transTris[i].setV1(Vector.addition(transTris[i].getV1(), trans));
transTris[i].setV2(Vector.addition(transTris[i].getV2(), trans));
transTris[i].setV3(Vector.addition(transTris[i].getV3(), trans));
}
}
public void scale(float[] scale){
for(int i = 0; i < getNumberOfTriangles(); i++){
transTris[i].setV1(Vector.scalePoint(transTris[i].getV1(), scale));
transTris[i].setV2(Vector.scalePoint(transTris[i].getV2(), scale));
transTris[i].setV3(Vector.scalePoint(transTris[i].getV3(), scale));
}
}
public void resetTransfomations(){
for(int i = 0; i < getNumberOfTriangles(); i++){
transTris[i].setV1(normTris[i].getV1().clone());
transTris[i].setV2(normTris[i].getV2().clone());
transTris[i].setV3(normTris[i].getV3().clone());
}
}
public Cube()
{
Buffer[] buffers = super.getBuffers(vertices, colors, indices);
mVertexBuffer = (FloatBuffer) buffers[0];
mColorBuffer = (FloatBuffer) buffers[1];
mIndexBuffer = (ByteBuffer) buffers[2];
}
public Cube(float[] vertices, float[] colors, byte[] indices)
{
if(vertices != null) {
this.vertices = vertices;
}
if(colors != null) {
this.colors = colors;
}
if(indices != null) {
this.indices = indices;
}
Buffer[] buffers = getBuffers(this.vertices, this.colors, this.indices);
mVertexBuffer = (FloatBuffer) buffers[0];
mColorBuffer = (FloatBuffer) buffers[1];
mIndexBuffer = (ByteBuffer) buffers[2];
for(int i = 0; i < getNumberOfTriangles(); i++){
normTris[i] = getTriangle(i);
transTris[i] = getTriangle(i);
}
}
public void draw(GL10 gl)
{
gl.glFrontFace(GL10.GL_CW);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
// draw all 36 triangles
gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
}
Using a Listener does not work in this case.
If you for example take a look at the onTouchListener. This is basically an interface providing just a single method onTouch(). Now when android is processing touch inputs and the target view was touched it knows that your listener can be informed about the touch by calling onTouch() of your listener.
When using OpenGL you have the problem, that noone handles the touch input inside your opengl surface. You have to do it yourself. So there is noone who will call your listener.
Why? What you render inside your gl surface is up to you. You only know what the actual geometry is and therefore you are the only one who can decide which object was selected.
You basically have two options to do the selection:
Ray Shooting - Shoot a ray through the eye of the viewer and the touched point into the scene and check which object was hit.
Color Picking - assign ids to your objects, encode id as a color, render scene with this color. finally check the color at the touch position and decode the color to get the id of the object.
For most applications I would prefer the second solution.
So i took code from http://www.learnopengles.com/android-lesson-one-getting-started/ and have been trying to draw a square (while still keeping everything else in the background). For whatever reason, it shows up as a triangle. My code will be provided below. I knew to change GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 4), but I can't find out what else I'm missing.
Furthermore, is there a better way to find out how to use functions within the class GLES20? http://developer.android.com/reference/android/opengl/GLES20.html does a good job providing parameters, but doesn't explain what each parameter does.
I would imagine my error is at the bottom of my code, probably in the function drawSquare, but the entire file will be pasted here in case I'm mistaken.
public class LessonOneRenderer 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];
/** Store our model data in a float buffer. */
private final FloatBuffer mTriangle1Vertices;
private final FloatBuffer mTriangle2Vertices;
private final FloatBuffer mTriangle3Vertices;
private final FloatBuffer mSquare1Vertices;
/** 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 mColorHandle;
/** How many bytes per float. */
private final int mBytesPerFloat = 4;
/** How many elements per vertex. */
private final int mStrideBytes = 7 * mBytesPerFloat;
/** Offset of the position data. */
private final int mPositionOffset = 0;
/** Size of the position data in elements. */
private final int mPositionDataSize = 3;
/** Offset of the color data. */
private final int mColorOffset = 3;
/** Size of the color data in elements. */
private final int mColorDataSize = 4;
/**
* Initialize the model data.
*/
public LessonOneRenderer()
{
// Define points for equilateral triangles.
// This triangle is red, green, and blue.
final float[] triangle1VerticesData = {
// X, Y, Z,
// R, G, B, A
-0.5f, -0.25f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.25f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.559016994f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f};
// This triangle is yellow, cyan, and magenta.
final float[] triangle2VerticesData = {
// X, Y, Z,
// R, G, B, A
-0.5f, -0.25f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.25f, 0.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.559016994f, 0.0f,
1.0f, 0.0f, 1.0f, 1.0f};
// This triangle is white, gray, and black.
final float[] triangle3VerticesData = {
// X, Y, Z,
// R, G, B, A
-0.5f, -0.25f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.25f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f,
0.0f, 0.559016994f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
final float[] square1VerticesData = {
//topleft
-0.25f, 0.25f, -.5f,
1.0f, 0.0f, 0.0f, 1.0f,
//top right
0.25f,0.25f, -.5f,
0.0f, 1.0f, 0.0f, 1.0f,
//bottom left
-0.25f, -0.25f, -.5f,
1.0f, 0.0f, 0.0f, 1.0f,
//right
0.25f, -0.25f, -.5f,
1.0f, 0.0f, 0.0f, 1.0f};
// Initialize the buffers.
mTriangle1Vertices = ByteBuffer.allocateDirect(triangle1VerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangle2Vertices = ByteBuffer.allocateDirect(triangle2VerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangle3Vertices = ByteBuffer.allocateDirect(triangle3VerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mSquare1Vertices = ByteBuffer.allocateDirect(square1VerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangle1Vertices.put(triangle1VerticesData).position(0);
mTriangle2Vertices.put(triangle2VerticesData).position(0);
mTriangle3Vertices.put(triangle3VerticesData).position(0);
mSquare1Vertices.put(square1VerticesData).position(0);
}
#Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{
// Set the background clear color to gray.
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
// Position the eye behind the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = 1.5f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = -5.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.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, eyeX, eyeY, eyeZ, lookX, 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.
+ "varying vec4 v_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 = v_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");
mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");
// Tell OpenGL to use this program when rendering.
GLES20.glUseProgram(programHandle);
}
#Override
public void onSurfaceChanged(GL10 glUnused, int width, int height)
{
// Set the OpenGL viewport to the same size as the surface.
GLES20.glViewport(0, 0, width, height);
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
#Override
public void onDrawFrame(GL10 glUnused)
{
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
// Do a complete rotation every 10 seconds.
long time = SystemClock.uptimeMillis() % 10000L;
float angleInDegrees = (360.0f / 10000.0f) * ((int) time);
// Draw the triangle facing straight on.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
drawTriangle(mTriangle1Vertices);
// Draw one translated a bit down and rotated to be flat on the ground.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, -1.0f, 0.0f);
Matrix.rotateM(mModelMatrix, 0, 90.0f, 1.0f, 0.0f, 0.0f);
Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
drawTriangle(mTriangle2Vertices);
// Draw one translated a bit to the right and rotated to be facing to the left.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 1.0f, 0.0f, 0.0f);
Matrix.rotateM(mModelMatrix, 0, 90.0f, 0.0f, 1.0f, 0.0f);
Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
drawTriangle(mTriangle3Vertices);
// Draw square facing strait on
float smallerAngle = -angleInDegrees;
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0, 0, 0.0f);
Matrix.rotateM(mModelMatrix, 0, smallerAngle, 0.0f, 0.0f, 1.0f);
drawSquare(mSquare1Vertices);
}
/**
* Draws a triangle from the given vertex data.
*
* #param aTriangleBuffer The buffer containing the vertex data.
*/
private void drawTriangle(final FloatBuffer aTriangleBuffer)
{
// Pass in the position information
aTriangleBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aTriangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Pass in the color information
aTriangleBuffer.position(mColorOffset);
GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aTriangleBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
private void drawSquare(final FloatBuffer aSquareBuffer) {
// Pass in the position information
aSquareBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aSquareBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Pass in the color information
aSquareBuffer.position(mColorOffset);
GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aSquareBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 4);
}
}
I'm assuming that you are very new to OpenGL ES 2.0 programming. So, before you start playing around with 3.D-transformation matrices (such as, for modeling, viewing, or projection) please try this basic example first (to render a rectangle) -
public class GLES20Renderer implements Renderer {
private int _rectangleProgram;
private int _rectangleAPositionLocation;
private FloatBuffer _rectangleVFB;
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1);
initShapes();
int _rectangleVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, _rectangleVertexShaderCode);
int _rectangleFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, _rectangleFragmentShaderCode);
_rectangleProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(_rectangleProgram, _rectangleVertexShader);
GLES20.glAttachShader(_rectangleProgram, _rectangleFragmentShader);
GLES20.glLinkProgram(_rectangleProgram);
_rectangleAPositionLocation = GLES20.glGetAttribLocation(_rectangleProgram, "aPosition");
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(_rectangleProgram);
GLES20.glVertexAttribPointer(_rectangleAPositionLocation, 3, GLES20.GL_FLOAT, false, 12, _rectangleVFB);
GLES20.glEnableVertexAttribArray(_rectangleAPositionLocation);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
private void initShapes() {
float rectangleVFA[] = {
0, 0, 0,
0, 0.5f, 0,
0.75f, 0.5f, 0,
0.75f, 0.5f, 0,
0.75f, 0, 0,
0, 0, 0,
};
ByteBuffer rectangleVBB = ByteBuffer.allocateDirect(rectangleVFA.length * 4);
rectangleVBB.order(ByteOrder.nativeOrder());
_rectangleVFB = rectangleVBB.asFloatBuffer();
_rectangleVFB.put(rectangleVFA);
_rectangleVFB.position(0);
}
private final String _rectangleVertexShaderCode =
"attribute vec4 aPosition; \n"
+ "void main() { \n"
+ " gl_Position = aPosition; \n"
+ "} \n";
private final String _rectangleFragmentShaderCode =
"void main() { \n"
+ " gl_FragColor = vec4(1,1,1,1); \n"
+ "} \n";
private int loadShader(int type, String source) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
return shader;
}
}
More of these at -
http://www.apress.com/9781430250531
Define your square as two triangles. I do it this way (I give you an example without color data, only vertices):
final float[] squareCoords = {
//first triangle
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
//second triangle
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
And then, in drawSquare() use
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
It will draw two triangles with your square coords.
I have a simple 2D engine that renders 2D textured quads, and right now I can scale the quad or rotate it, but when I try to translate it I have a strange distortion (the quad is squashed in the half left part of the screen with an infinite perspective effect), here's the code :
private final float quad_vertex[] = {
-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
};
final float left = -width/2.0f;
final float right = width/2.0f;
final float bottom = -height/2.0f;
final float top = height/2.0f;
final float near = 0.1f;
final float far = 200.0f;
Matrix.orthoM(projection_matrix, 0, left, right, top, bottom, near, far);
Matrix.setLookAtM(view_matrix, 0, 0, 0, 1.0f, 0.0f, 0f, 0f, 0f, 1.0f, 0.0f);
...
Matrix.setIdentityM(model_matrix, 0);
Matrix.scaleM(model_matrix, 0, scale_width, scale_height, 1.0f);
Matrix.translateM(model_matrix, 0, x, 0, 0);
//Matrix.rotateM(model_matrix, 0, x, 1, 0, 0);
x = x + 1.0f;
Matrix.multiplyMM(viewprojection_matrix, 0, projection_matrix, 0, view_matrix, 0);
Matrix.multiplyMM(modelviewprojection_matrix, 0, viewprojection_matrix, 0, model_matrix, 0);
So, any idea what is the problem ? Thanks in advance :)
Sounds like a similar problem that I ran into. I was using the tutorial code at http://developer.android.com/training/graphics/opengl/index.html. Changing a line in the shader code from gl_Position = vPosition * uMVPMatrix; to gl_Position = uMVPMatrix * vPosition; fixed the problem.
Matrix multiplication is a non-commutative operation - the order of the operands is important!