如何处理 Opengl ES 中的方向变化

How to deal with orientation change in Open GL ES

我正在为 android 试用 Open GL 2。到目前为止,我已经能够初始化 GLES20 并在其上绘制一些简单的形状。问题是我找不到关于应该如何为屏幕方向更改配置 GLES20 的信息,因为现在我只是在设备旋转后出现黑屏。 GLSurfaceView 甚至可以在屏幕旋转后旋转,还是我必须使用一些矩阵手动旋转?

打开 GL 初始化代码:

object OpenGLBuilder {

val vertexShaderCode =
    "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = vPosition;" +
            "}"

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

var mProgram = -1;
init {
    val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
    val fragmentShader = 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)

        // add the fragment shader to program
        GLES20.glAttachShader(it, fragmentShader)

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(it)
    }
}

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

}

我的形状class:

class Triangle(val triangleCoords: FloatArray) {

// 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 val COORDS_PER_VERTEX = 3
private var positionHandle = 0
private var mColorHandle = 0;

private val vertexCount = triangleCoords.size / COORDS_PER_VERTEX
private val vertexStride = COORDS_PER_VERTEX * 4

fun draw(program: Int) {
    GLES20.glUseProgram(program)

    positionHandle = GLES20.glGetAttribLocation(program, "vPosition").also {
        GLES20.glEnableVertexAttribArray(it)
        GLES20.glVertexAttribPointer(
            it,
            COORDS_PER_VERTEX,
            GLES20.GL_FLOAT,
            false,
            vertexStride,
            vertexBuffer
        )
    }

    mColorHandle = GLES20.glGetUniformLocation(program, "vColor").also{
        GLES20.glUniform4fv(mColorHandle, 1, color, 0)
    }

    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)

    GLES20.glDisableVertexAttribArray(positionHandle)
}
}

然后我只创建并使用这样的单个形状实例:

object SceneObjects {
    val triangle1 = Triangle(floatArrayOf(
        0f, 0.5f, 0f,
        -0.5f, -0.3f, 0f,
        0.5f, -0.3f, 0f
    ))

    val square1 = Square2(floatArrayOf(
        -0.5f, 0.5f, 0f,
        -0.5f, -0.5f, 0f,
        0.5f, -0.5f, 0f,
        0.5f, 0.5f, 0f
    ))

}

和 GLSurfaceView:

class MyGLSurfaceView(context: Context) : GLSurfaceView(context), GLSurfaceView.Renderer {
    init{
        setEGLContextClientVersion(2)
        setRenderer(this)
        // Render the view only when there is a change in the drawing data
        renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    }

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

    override fun onDrawFrame(unused: GL10) {
        // Redraw background color
        SceneObjects.triangle1.draw(OpenGLBuilder.mProgram)
    }

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

我认为您需要做两件事来巧妙地处理方向变化。

首先,默认情况下,您的 OpenGL 上下文和所有创建的对象在进入后台时都会被销毁。有一种观点认为您应该让它发生并添加代码以重新创建所有资源,但如果您希望过上轻松的生活,那么只需使用 setPreserveEGLContextOnPause,再也不用担心它了。

您可能会想“但这是方向改变,我的应用程序没有进入后台”。好吧,默认情况下 Android activity 会在方向改变后重新启动,这可能会把事情搞砸。此行为详细 here。您可能想在您的 activity 的清单中使用此批次:android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"