OpenGL ES 2.0 не рисует в Android Lollipop (5.1) - PullRequest
0 голосов
/ 22 января 2019

Я новичок в программировании openGL ES, поэтому я следовал руководству, предоставленному на сайте разработчиков Android. Он показывает, как нарисовать простой зеленый треугольник, который вращается на экране после точки касания. Я попробовал это на своем планшете (Galaxy Tab A10, Android Oreo - 8), и он работал, также на Huawei (Android Marhmallow - 6.0); проблема в том, что треугольник не отображается на моем Galaxy J3 (Android Lollipop - 5.1) без ошибок журнала и 0 как возврат всех вызовов glGetError(), единственное, что я вижу, это изменение цвета фона.

Я не мог найти подобные проблемы здесь, в SO и в Интернете, у кого-то была такая же проблема?

(Используемый язык - Kotlin, но я думаю, что это концептуальный вопрос, поэтому, пожалуйста, обратите внимание, если код немного отличается от Java).

Активность в игре:

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
        }
    }
}

Пользовательский класс рендерера:

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)
    }
}

Класс треугольника:

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()}")
    }
}

Конечно, я добавил эту строку в manifest.xml:

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

UPDATE:

Я обнаружил, что если я попытаюсь запустить приложение, пока телефон (Lollipop) отключен от ПК, оно не будет работать с всплывающим сообщением: «Авторизация запрещена» (итальянское сообщение «Autorizzazione negata») на английском языке это также может быть «Отказано в доступе»), и я не знаю, что это значит, но, возможно, это может быть полезно для кого-то.

...