Question

What I've managed to accomplish so far is:

  • Initialise the GLSurfaceView/Renderer
  • Draw a triangle on the screen
  • Render a square/rectangle on the screen
  • Add a bitmap texture to the screen
  • Ensure PNG transparency is honoured when rendering
  • Automatically scale the triangles so they show up correctly for all screen sizes

However, after fixing the scaled triangles, the background rectangle (with texture) no longer fills up the screen.

The background no longer fills up the screen

I've been stuck on this for a while now and absolutely baffled to the point I have thrown in the towel.

The main parts I'm unsure about is the use of glFrustumf() and gluLookAt().

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    gl.glViewport(0, 0, width, height);

    ratio = (float) width / height;
    gl.glMatrixMode(GL10.GL_PROJECTION);        // set matrix to projection mode
    gl.glLoadIdentity();                        // reset the matrix to its default state
    gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);  // apply the projection matrix
}

@Override
public void onDrawFrame(GL10 gl) {
    // Clear the screen
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

    // Set GL_MODELVIEW transformation mode
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity(); // reset the matrix to its default state

    // When using GL_MODELVIEW, you must set the camera view
    GLU.gluLookAt(gl, 0, 0, -5f, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f);

    bg.draw(gl);
    // ...
}

If anybody has a moment to take a look at the problem, I've uploaded the files to https://bitbucket.org/koonkii/test_opengl/src so you don't have to recreate the code by copy-pasting.

Was it helpful?

Solution

GLU.gluLookAt(gl, 0, 0, -5f, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f);

Try to change -5f to 0, what you're saying here is displace the camera 5 units back, therefore unless you're doing an orthogonal projection (which I think you're not, try checking out this) what OpenGL is doing is scaling your background polygon according to your perspective view, and you see it as 'smaller'.

If you do an orthogonal projection, no matter how much you move your camera in the z axis, you will always see it the same size. This is useful for 2D OpenGL-based games, so do check out the link above.

EDIT: gluPerspective and glOrtho

gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

gluPerspective has a parameter called 'fovy', which is basically the 'Field of View in the Y axis'. The field of view expresses the amount of space the camera can see, basically 'expanding' or 'contracting' whatever vertices happen to be before it. A typical human eye has a 45º FOV.

The zNear and zFar express the near and far frustum limits, the frustum being an invisible 'box' which determines which vertices are outside the viewing area.

Aspect determines the ratio between the width and height of the camera.

glOrtho is a special case of gluPerspective in the sense that the FOV is always 0.

gl.glOrthof(0.0f, (float) width, (float) height, 0.0f, 1.0f, -1.0f);

The first four parameters specify the size of the clipping plane (normally the size of the screen), the other two values specifiy the frustum near and far (which you don't need unless you want to hide objects by placing them 'far away'.

I hope this cleared it up a bit for you.

OTHER TIPS

Alright, after a good nights sleep and applying RedOrav's advice regarding orthogonal projection, I did more browsing into it and got it working!

The code snippets given by RedOrav did actually work, however after switching to the orthogonal projection I was still drawing the squares and triangles as small as 0.15f in width. Barely be visible as it's less than 1 pixel wide!

After changing the background/square/triangle code to something more reasonable (30.0f), they showed up!

Played around with the code a bit more and got positioning working properly. I've submitted the code to bitbucket for those who want to check out a working copy of the project.

The reason why I needed G.getYPos() is because the bottom coordinate = 0, and top is screen height. Couldn't figure out a nicer way of inverting it without turning all the textures upside-down.

The important initialisation parts are:

Global helper

public class G {
    public static float ratio;
    public static int width, height;

    /** The texture pointer */
    public static int[] textures = new int[3];

    final static int TEXTURE_DEFAULT = 0;
    final static int TEXTURE_BG = 1;
    final static int TEXTURE_ANDROID = 2;
    final static int TEXTURE_TURTLE = 3;

    /**
     * Since (bottom = 0, top = height), we need to invert the values so they make sense logically.
     */
    public static int getYPos(int top) {
        return G.height - top;
    }
}

Renderer class

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    this.loadGLTextures(gl);

    gl.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);    //Red Background
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    gl.glViewport(0, 0, width, height);


    // Save these for global use.
    G.width = width;
    G.height = height;
    G.ratio = (float) width / height;


    // Set up orthogonal viewport and make adjustments for screen ratio
    gl.glViewport(0, 0, width, height);
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    GLU.gluOrtho2D(gl, 0, width, 0, height); // The parameters are weird but bottom = 0 so we need an inverter function G.

    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();


    // Start setting up the constructs we need
    bg = new Background();

    squares = new ArrayList<Square>();
    squares.add(new Square(width / 2, G.getYPos(0))); // middle/top of the screen
    squares.add(new Square(width / 2, G.height /2)); // center of the screen

    triangles = new ArrayList<Triangle>();
    triangles.add(new Triangle(0, G.getYPos(0))); // top left
    triangles.add(new Triangle(width, G.getYPos(height))); // bottom right
    triangles.add(new Triangle(width /2, height /2)); // middle
}

@Override
public void onDrawFrame(GL10 gl) {
    // Clear the screen
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

    bg.draw(gl);

    for (Square s : squares) {
        s.draw(gl);
    }

    // Draw correctly scaled triangles
    for (Triangle t : triangles) {
        t.draw(gl);
    }

    try {
        Thread.sleep(400);
    }
    catch (InterruptedException e) {
    }
}


/**
 * Loads the textures up.
 */
public void loadGLTextures(GL10 gl) {
    int[] texture_map = new int[] { R.drawable.bg_game, R.drawable.ic_launcher };
    Bitmap bitmap;

    // generate one texture pointer, keep 0 as blank/default
    gl.glGenTextures(texture_map.length, G.textures, 0);

    for (int i = 0; i < texture_map.length; i++) {
        // loading texture
        bitmap = BitmapFactory.decodeResource(context.getResources(), texture_map[i]);

        // ...and bind it to our array
        gl.glBindTexture(GL10.GL_TEXTURE_2D, G.textures[i +1]);

        // create nearest filtered texture
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

        // Clean up
        bitmap.recycle();
    }
}

Background class p

ublic class Background {
    private FloatBuffer vertexBuffer;   // buffer holding the vertices
    private float vertices[] = {
            -1.0f, -1.0f,  0.0f,        // V1 - bottom left
            -1.0f,  1.0f,  0.0f,        // V2 - top left
            1.0f, -1.0f,  0.0f,        // V3 - bottom right
            1.0f,  1.0f,  0.0f         // V4 - 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)
    };


    public Background() {
        // Recalculate the vertices so they fit the screen
        vertices[0] = 0;        // v1 left
        vertices[1] = G.height; // v1 bottom

        vertices[3] = 0;        // v2 left
        vertices[4] = 0;        // v2 top

        vertices[6] = G.width;  // v3 right
        vertices[7] = G.height; // v3 bottom

        vertices[9] = G.width;  // v4 right
        vertices[10] = 0;       // v4 top


        // a float has 4 bytes so we allocate for each coordinate 4 bytes
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());

        // allocates the memory from the byte buffer
        vertexBuffer = byteBuffer.asFloatBuffer();

        // fill the vertexBuffer with the vertices
        vertexBuffer.put(vertices);

        // set the cursor position to the beginning of the buffer
        vertexBuffer.position(0);


        byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        textureBuffer = byteBuffer.asFloatBuffer();
        textureBuffer.put(texture);
        textureBuffer.position(0);
    }


    public void draw(GL10 gl) {
        gl.glEnable(GL10.GL_TEXTURE_2D); // Twig;

        // Bind the previously generated texture
        gl.glBindTexture(GL10.GL_TEXTURE_2D, G.textures[G.TEXTURE_BG]);

        // Point to our buffers
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // Point to our vertex buffer
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

        // Draw the vertices as triangle strip
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

        //Disable the client state before leaving
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        gl.glDisable(GL10.GL_TEXTURE_2D); // twig;
    }
}

Square class Very similar to background except it has a position and applies alpha transparency.

public class Square {
    private FloatBuffer vertexBuffer;   // buffer holding the vertices
    private float vertices[] = {
            -1.0f, -1.0f,  0.0f,        // V1 - bottom left
            -1.0f,  1.0f,  0.0f,        // V2 - top left
            1.0f, -1.0f,  0.0f,        // V3 - bottom right
            1.0f,  1.0f,  0.0f         // V4 - 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)
    };


    public Square(float posX, float posY) {
        float w = 30f;
        float h = w;

        vertices[0] = posX - w; // left
        vertices[3] = posX - w;
        vertices[6] = posX + w; // right
        vertices[9] = posX + w;

        vertices[1] = posY - h; // top
        vertices[4] = posY + h;
        vertices[7] = posY - h; // bottom
        vertices[10] = posY + h;

        // a float has 4 bytes so we allocate for each coordinate 4 bytes
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());

        // allocates the memory from the byte buffer
        vertexBuffer = byteBuffer.asFloatBuffer();

        // fill the vertexBuffer with the vertices
        vertexBuffer.put(vertices);

        // set the cursor position to the beginning of the buffer
        vertexBuffer.position(0);



        byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        textureBuffer = byteBuffer.asFloatBuffer();
        textureBuffer.put(texture);
        textureBuffer.position(0);
    }


    /** The draw method for the square with the GL context */
    public void draw(GL10 gl) {
        // Enable alpha transparency
        gl.glEnable(GL10.GL_BLEND);
        gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);

        // bind the previously generated texture
        gl.glEnable(GL10.GL_TEXTURE_2D); // Twig;

        gl.glBindTexture(GL10.GL_TEXTURE_2D, G.textures[G.TEXTURE_ANDROID]);

        // Point to our buffers
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // reset the colour for the square
        gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

        // Point to our vertex buffer
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

        // Draw the vertices as triangle strip
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

        //Disable the client state before leaving
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // Disable alpha transparency
        gl.glDisable(GL10.GL_BLEND);
        gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ZERO);

        gl.glDisable(GL10.GL_TEXTURE_2D); // twig;
    }
}

Triangle class

public class Triangle {
    private FloatBuffer vertexBuffer;   // buffer holding the vertices

    private float vertices[] = {
            -0.5f, -0.5f,  0.0f,        // V1 - first vertex (x,y,z)
             0.5f, -0.5f,  0.0f,        // V2 - second vertex
             0.0f,  0.5f,  0.0f         // V3 - third vertex
    };

    public Triangle(float posX, float posY) {
        int w = 30;
        int h = w;

        vertices[0] = posX - (w/2); // left
        vertices[3] = posX + (w/2); // right
        vertices[6] = posX;         // middle

        vertices[1] = posY - (h/2); // bottom
        vertices[4] = posY - (h/2); // bottom
        vertices[7] = posY + (h/2); // top

        // a float has 4 bytes so we allocate for each coordinate 4 bytes
        ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        vertexByteBuffer.order(ByteOrder.nativeOrder());

        // allocates the memory from the byte buffer
        vertexBuffer = vertexByteBuffer.asFloatBuffer();

        // fill the vertexBuffer with the vertices
        vertexBuffer.put(vertices);

        // set the cursor position to the beginning of the buffer
        vertexBuffer.position(0);
    }

    public void draw(GL10 gl) {
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

        // set the colour for the triangle
        gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);

        // Point to our vertex buffer
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

        // Draw the vertices as triangle strip
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

        // Reset the colour
        gl.glColor4f(1.0f, 1.0f, 1.0f, 0.0f);

        //Disable the client state before leaving
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }
}

Hope this helps anyone else having similar issues with starting OpenGL.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top