0

I'm new in openGL ES programming, so I followed the guide provided by Android Developers site. It shows how draw a simple green triangle that rotate on the screen following the touch point. I tried it on my Tablet (Galaxy Tab A10, Android Oreo - 8) and it worked, also on a Huawei (Android Marhmallow - 6.0); the problem is that the triangle is not shown on my Galaxy J3 (Android Lollipop - 5.1) without log errors and 0 as return of all glGetError() call, the only thing I can see is the color change of the background.

I couldn't find similar problems here in SO and on the web, has someone had the same problem?

(The language used is Kotlin, but I think it's a conceptual question, so please take a look also if the code is slightly different from Java).

Game Activity:

import android.content.Context
import android.opengl.GLSurfaceView
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.MotionEvent

class GameActivity : AppCompatActivity() {

    private lateinit var mGLView: GLSurfaceView

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Create a GLSurfaceView instance and set it
        // as the ContentView for this Activity.
        mGLView = MyGLSurfaceView(this)
        setContentView(mGLView)
    }

    class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {

        private val mRenderer: MyGLRenderer

        init {

            // Create an OpenGL ES 2.0 context
            setEGLContextClientVersion(2)

            mRenderer = MyGLRenderer()

            // Set the Renderer for drawing on the GLSurfaceView
            setRenderer(mRenderer)
            renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
        }

        private val TOUCH_SCALE_FACTOR: Float = 180.0f / 320f

        private var previousX: Float = 0f
        private var previousY: Float = 0f

        override fun onTouchEvent(e: MotionEvent): Boolean {
            // MotionEvent reports input details from the touch screen
            // and other input controls. In this case, you are only
            // interested in events where the touch position changed.

            val x: Float = e.x
            val y: Float = e.y

            when (e.action) {
                MotionEvent.ACTION_MOVE -> {

                    var dx: Float = x - previousX
                    var dy: Float = y - previousY

                    // reverse direction of rotation above the mid-line
                    if (y > height / 2) {
                        dx *= -1
                    }

                    // reverse direction of rotation to left of the mid-line
                    if (x < width / 2) {
                        dy *= -1
                    }

                    mRenderer.angle += (dx + dy) * TOUCH_SCALE_FACTOR
                    requestRender()
                }
            }

            previousX = x
            previousY = y
            return true
        }
    }
}

Custom Renderer Class:

import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

class MyGLRenderer : GLSurfaceView.Renderer {

    val TAG = MyGLRenderer::class.java.name

    @Volatile
    var angle: Float = 0f

    private lateinit var mTriangle: Triangle

    private val mRotationMatrix = FloatArray(16)

    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private val mMVPMatrix = FloatArray(16)
    private val mProjectionMatrix = FloatArray(16)
    private val mViewMatrix = FloatArray(16)

    override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
        // Set the background frame color
        GLES20.glClearColor(0.8f, 0.2f, 0.2f, 1.0f)

        // initialize a triangle
        mTriangle = Triangle()
    }

    override fun onDrawFrame(unused: GL10) {
        val scratch = FloatArray(16)

        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
        // enable face culling feature
        //GLES20.glEnable(GL10.GL_CULL_FACE)
        // specify which faces to not draw
        //GLES20.glCullFace(GL10.GL_BACK)

        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)

        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0)

        // Create a rotation transformation for the triangle
        /*val time = SystemClock.uptimeMillis() % 4000L
        val angle = 0.090f * time.toInt()*/
        Matrix.setRotateM(mRotationMatrix, 0, -angle, 0f, 0f, -1.0f)

        // Combine the rotation matrix with the projection and camera view
        // Note that the mMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0)

        // Draw shape
        mTriangle.draw(scratch)
    }

    override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
        GLES20.glViewport(0, 0, width, height)

        val ratio: Float = width.toFloat() / height.toFloat()

        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
    }
}

Triangle Class:

import android.opengl.GLES20
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer

class Triangle {

    val TAG = Triangle::class.java.name

    // number of coordinates per vertex in this array
    val COORDS_PER_VERTEX = 3

    var triangleCoords = floatArrayOf(     // in counterclockwise order:
        0.0f, 0.622008459f, 0.0f,      // top
        -0.5f, -0.311004243f, 0.0f,    // bottom left
        0.5f, -0.311004243f, 0.0f      // bottom right
    )

    private val fragmentShaderCode =
        "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor;" +
                "}"

    private val 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
                // Note that the uMVPMatrix factor *must be first* in order
                // for the matrix multiplication product to be correct.
                "  gl_Position = uMVPMatrix * vPosition;" +
                "}"

    // Use to access and set the view transformation
    private var mMVPMatrixHandle: Int = 0

    // Set color with red, green, blue and alpha (opacity) values
    val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)

    private var vertexBuffer: FloatBuffer =
    // (number of coordinate values * 4 bytes per float)
        ByteBuffer.allocateDirect(triangleCoords.size * 4).run {
            // use the device hardware's native byte order
            order(ByteOrder.nativeOrder())

            // create a floating point buffer from the ByteBuffer
            asFloatBuffer().apply {
                // add the coordinates to the FloatBuffer
                put(triangleCoords)
                // set the buffer to read the first coordinate
                position(0)
            }
        }

    private var mProgram: Int

    init {
        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram().also {

        // add the vertex shader to program
        GLES20.glAttachShader(it, vertexShader)
        Log.d(TAG, "glAttachShader: ${GLES20.glGetError()}")

        // add the fragment shader to program
        GLES20.glAttachShader(it, fragmentShader)
        Log.d(TAG, "glAttachShader: ${GLES20.glGetError()}")

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(it)
        Log.d(TAG, "glLinkProgram: ${GLES20.glGetError()}")
    }
    Log.d(TAG, "glCreateProgram: ${GLES20.glGetError()}")
    }

    fun loadShader(type: Int, shaderCode: String): Int {

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        return GLES20.glCreateShader(type).also { shader ->

            // add the source code to the shader and compile it
            GLES20.glShaderSource(shader, shaderCode)
            GLES20.glCompileShader(shader)
        }
    }

    private var mPositionHandle: Int = 0
    private var mColorHandle: Int = 0

    private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX
    private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex

    fun draw(mvpMatrix: FloatArray) { // pass in the calculated transformation matrix
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram)
        Log.d(TAG, "glUseProgram: ${GLES20.glGetError()}")

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {

            // Enable a handle to the triangle vertices
            GLES20.glEnableVertexAttribArray(it)
            Log.d(TAG, "glEnableVertexAttribArray: ${GLES20.glGetError()}")

            // Prepare the triangle coordinate data
            GLES20.glVertexAttribPointer(
                it,
                COORDS_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride,
                vertexBuffer
            )
            Log.d(TAG, "glVertexAttribPointer: ${GLES20.glGetError()}")

            // get handle to fragment shader's vColor member
            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor").also { colorHandle ->

                // Set color for drawing the triangle
                GLES20.glUniform4fv(colorHandle, 1, color, 0)
            }
            Log.d(TAG, "glGetUniformLocation: ${GLES20.glGetError()}")

            // get handle to shape's transformation matrix
            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
            Log.d(TAG, "glGetUniformLocation: ${GLES20.glGetError()}")

            // Pass the projection and view transformation to the shader
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0)
            Log.d(TAG, "glUniformMatrix4fv: ${GLES20.glGetError()}")

            // Draw the triangle
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
            Log.d(TAG, "glDrawArrays: ${GLES20.glGetError()}")

            // Disable vertex array
            GLES20.glDisableVertexAttribArray(it)
            Log.d(TAG, "glDisableVertexAttribArray: ${GLES20.glGetError()}")
        }
        Log.d(TAG, "glGetAttribLocation: ${GLES20.glGetError()}")
    }
}

Of course I added this line in the manifest.xml:

<uses-feature android:glEsVersion="0x00020000" android:required="true"/>

UPDATE:

I discovered that if I try to run the app while the phone (the Lollipop) is disconnected from PC, it'll fail to run, with the toast message: "Authorization denied" (the Italian message is "Autorizzazione negata", so in English it could be also "Permission denied"), and I don't know what it means, but maybe it could be helpful for someone.

3
  • That's a lot of code for one question. I'd suggest you to check for GL errors (use GLES20.glGetError) everywhere after major GL calls. Example might have one tiny thing missing, and the common issue with OpenGL is that it may run OK at defaults on one GPU+driver combination but fail on another one. This will help you to narrow down the problematic part.
    – keaukraine
    Commented Jan 22, 2019 at 15:21
  • Try to rotate screen portrait / album? I see on problem but not sure.
    – Style-7
    Commented Jan 22, 2019 at 16:29
  • Yes I tried rotating screen; on Tablet and Huawei it works, on the Galaxy the shape is always invisible
    – Luca Murra
    Commented Jan 23, 2019 at 14:31

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.