1

I am creating a simple traingle strip to cover the whole viewport with a single rectangle. Then I am applying a 100x100 texture to this surface which changes with every frame.

I set up viewPort and initialise my vertexBuffer etc. in the onSurfaceChanged method of my GLSurfaceView class, then call my rendering function from onDrawFrame.

This setup works as it should, but at random occasions only the right lower quarter of my rectangle gets rendered, the other 3/4th of the canvas is filled with background color! It doesn't happen every time, and the anomaly disappears after rotating the device back and forth (I guess because everything gets a reset in onSurfaceChanged)

I have tried to re-upload all vertices at every frame update with GLES20.glBufferData, which seems to get rid of this bug, although it might be that I just wasn't patient enough to observe it happening (as it is quite unpredictible). It's a very simple triangle strip, so I don't think that it consumes a lot of time, but it just feels bad practice to upload a data 60/sec which isn't changing at all!

//called from onSurfaceChanged
private fun initGL (side:Int) {

        /*======== Defining and storing the geometry ===========*/
        //vertices for TRIANGLE STRIP
        val verticesData = floatArrayOf(
            -1.0f,1.0f,//LU
            -1.0f,-1.0f,//LL
            1.0f,1.0f,//RU
            1.0f,-1.0f//RL
        )

        //float : 32 bit -> 4 bytes
        val vertexBuffer : FloatBuffer = ByteBuffer.allocateDirect(verticesData.size * 4)
            .order(ByteOrder.nativeOrder()).asFloatBuffer()
        vertexBuffer.put(verticesData).position(0)

        val buffers = IntArray(1)
        GLES20.glGenBuffers(1, buffers, 0)
        vertexBufferId = buffers[0]

        //upload vertices to GPU
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId)
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,
            vertexBuffer.capacity() * 4, // 4 = bytes per float
            vertexBuffer,
            GLES20.GL_STATIC_DRAW)
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)

        /*================ Shaders ====================*/

        // Vertex shader source code
        val vertCode =
                """
                attribute vec4 aPosition;
                void main(void) {
                    gl_Position = aPosition;
                }
                """

        val fragCode =
            """
            precision mediump float;

            varying vec2 vCoord;
            uniform sampler2D u_tex;
            void main(void) {
                //1-Y, because we need to flip the Y-axis!!
                vec4 color = texture2D(u_tex, vec2(gl_FragCoord.x/$side.0, 1.0-(gl_FragCoord.y/$side.0)));
                gl_FragColor = color;
            }
            """

        // Create a shader program object to store
        // the combined shader program
        val shaderProgram = createProgram(vertCode, fragCode)

        // Use the combined shader program object
        GLES20.glUseProgram(shaderProgram)

        val vertexCoordLocation = GLES20.glGetAttribLocation(shaderProgram, "aPosition")
        GLES20.glVertexAttribPointer(vertexCoordLocation, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer)
        GLES20.glEnableVertexAttribArray(vertexCoordLocation)

        //set ClearColor
        GLES20.glClearColor(1f,0.5f,0.5f,0.9f)

        //setup a texture buffer array
        val texArray = IntArray(1)
        GLES20.glGenTextures(1,texArray,0)
        textureId = texArray[0]
        if (texArray[0]==0) Log.e(TAG, "Error with Texture!")
        else Log.e(TAG, "Texture id $textureId created!")

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
        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)
        GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT,1)
    }

//called from onDrawFrame
private fun updateGLCanvas (matrix : ByteArray, side : Int) {
        //create ByteBuffer from updated texture matrix
        val textureImageBuffer : ByteBuffer = ByteBuffer.allocateDirect(matrix.size * 1)//Byte = 1 Byte
            .order(ByteOrder.nativeOrder())//.asFloatBuffer()
        textureImageBuffer.put(matrix).position(0)

        //do I need to bind the texture in every frame?? I am desperate XD 
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0,GLES20.GL_RGB, side, side, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, textureImageBuffer)

        //bind vertex buffer
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId)
        // Clear the color buffer bit
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
        //draw from vertex buffer
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4)
        //unbind vertex buffer
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)
    }

There are no error messages and most of the time the code is behaving as it should ... which makes this a bit difficult to track ...

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
ize8
  • 95
  • 7
  • *"the other 3/4th of the canvas is filled with background"* [...] *" the anomaly disappears after rotating the device back and forth"* - What is the projection matrix? Is the geometry clipped by the near or far plane of the projection? – Rabbid76 Jun 06 '19 at 08:46
  • I don't use a projection matrix as I thought that it won't be necessary for such a simple plane, which I essentially only use as a canvas to cover the screen. Is a projection matrix mandatory in ES 2? The only reason I am not using a "normal" android cancas is that my texture is made up from quite a lot(10000-100000) of random-colored rectangles and only openGL is fast enough to achieve acceptable frame-rates. – ize8 Jun 06 '19 at 09:30
  • What do you mean by *"rotating the device back and forth"*? There is a always a projection. If you don't set a projection explicitly, then the projection is the identity matrix. This means view space, clip space and normalized device space are the same, so the near plane is -1 and the far plane is 1. – Rabbid76 Jun 06 '19 at 09:55
  • I mean physically rotating the phone, from vertical to horizontal. My vertex "screen"isn't supposed to move in the 3D space, I am basically using it as a 2D canvas. I will read up on the projection matrix though! – ize8 Jun 06 '19 at 10:02
  • interestingly saving the vertexBuffer into a class variable fixed the problem, which is quite peculiar as I was under the impression that the data is uploaded onto the GPU with glBufferData and it can be disposed of after that ... – ize8 Jun 06 '19 at 12:50
  • *"I was under the impression that the data is uploaded onto the GPU"* - yes the data is uploaded to the GPU, but you don't use the uploaded data. See the answer. – Rabbid76 Jun 06 '19 at 13:06

1 Answers1

1

If you want to use a vertex buffer, then the buffer object has to be the currently bound to the target GLES20.GL_ARRAY_BUFFER, when the array of generic vertex attribute data is specified by glVertexAttribPointer. The vertex attribute specification refers to this buffer.

In this case the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store.
In your case this means the last parameter has to be 0.

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId)
GLES20.glVertexAttribPointer(vertexCoordLocation, 2, GLES20.GL_FLOAT, false, 0, 0)

Note, if no named buffer buffer object is bound (zero), then the last parameter is a pointer to the buffer memory. Every time when a draw call is performed, this buffer is read.
In your implementation, the data which was uploaded to the GPU is never used, because it isn't referenced by the vertex array specification.
See also Vertex Specification.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Awesome! Thanks for the 'bug hunt', it all makes sense now!! My scene got screwed as I was reading vertex data from a Buffer which at one random point got garbage collected ... silly me XD This also explains why it got fixed after saving the Buffer into a class variable! – ize8 Jun 06 '19 at 16:50
  • @ize8 Thank you for accepting the answer. You're welcome. – Rabbid76 Jun 06 '19 at 17:06