第一个Android OpenGL程序


如果还不知道自己想要做什么,那么就努力去做到最好,等到有一天你想得到什么的时候,不会懊悔于自己没有足够的资本伸手追逐。

近期笔者将会从零开始系统的学习Android OpenGL ES相关知识,本专栏将会不定期更新,主要是对自己学习过程和心得的一个记录。同时也希望能够通过该笔记对后面想要学习Android OpenGL ES的同学提供一些帮助。

本篇我们将直奔主题,创建我们第一个Android OpenGL应用,本篇以及后续我们使用的ES版本为2.0。第一个OpenGL应用,它将初始化OpenGL,并且对Android的Activity的生命周期进行处理,并且会以一个RGB颜色来循环清空屏幕。这些将作为我们需要在屏幕上绘制东西时的基础。

为什么要清空屏幕?
如果已经在每一帧的屏幕上都绘制了内容,清空屏幕好像是浪费的,那为什么还要这样做呢?
回到一切都用软件渲染的时代,清空屏幕总是一种浪费;开发者总是要假定所有东西都已经绘制好了,并能覆盖前一帧的内容,而不必清空它,这样就能达到优化的目的;可以节省清空屏幕的处理时间。有时,这种优化会导致一些游戏中常见的著名的“镜子大厅”(Hall Of Mirrors)效应,如Doom:它导致的视觉效果就像站在镜子大厅中间,旧的内容一遍又一遍重复出现。
这种优化不再有效了;最新的GPU以不同的方式工作,他们使用特殊的渲染技术,如果屏幕是干净的,他们能工作得更快。通过让GPU清空屏幕,可以节省帧拷贝浪费的时间。因为GPU的这种工作方式,清空屏幕可以帮助避免很多问题,如闪烁或者有物品没有绘制;保留旧的内容容易看到不期望或者不理想的结果。

初始化OpenGL

我们将使用一个特殊的类GLSurfaceView初始化OpenGL。GLSurfaceView是SurfaceView的一个子类,内部会处理OpenGL初始化过程中比较基本的操作,如配置显示设备(display)以及在后台线程中渲染;渲染是在显示设备中一个称为“surface”的特定区域完成的,有时也称为视窗(viewport)。
其次是GLSurfaceView类也针对于Android Activity生命周期提供了对应的封装接口,使得在使用OpenGL时对于页面生命周期变化处理变的更容易。

使用GLSurfaceView

GLSurfaceView与其他View的使用其实基本上一致,分为两种方式一种是代码中直接创建,一种是在布局文件中添加。这里我们选择后者,在布局文件里面添加即可。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.opengl.GLSurfaceView
        android:id="@+id/glSurfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>

Activity通过findViewById获取布局中配置的GLSurfaceView对象

import android.app.ActivityManager
import android.content.Context
import android.opengl.GLSurfaceView
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast

class MainActivity : AppCompatActivity() {

    lateinit var glSurfaceView:GLSurfaceView
    var rendererSet = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        glSurfaceView = findViewById(R.id.glSurfaceView)

检查是否支持OpenGL ES2.0

因为我们学习过程中将只会使用2.0的版本进行代码编写,因此我们需要知道在运行设备上面是否支持ES2.0。

首先需要获取Android ActivityManager的引用以获取设备配置信息,然后通过取出配置信息中的reqGlEsVersion变量以确定OpenGL ES版本号。如果版本号为0x20000或更高版本,则可以使用OpenGL ES 2.0的API。

由于模拟器GPU模拟部分存在缺陷,所以还需要代码测试当前设备是否为模拟器,如果是模拟器,代码将假定它支持OpenGL ES 2.0。为了确保程序能在模拟器上运行,需要确保模拟器配置了OpenGL ES 2.0

    fun isSupportGLES2(context:Context):Boolean{
        var activityMnger = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        var configurationInfo = activityMnger.deviceConfigurationInfo
        return configurationInfo.reqGlEsVersion >= 0x20000 ||
                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 &&
                        (Build.FINGERPRINT.startsWith("generic")||
                         Build.FINGERPRINT.startsWith("unknown")||
                         Build.MODEL.contains("google_sdk")||
                         Build.MODEL.contains("Emulator")||
                         Build.MODEL.contains("Android SDK built for x86")))
    }

渲染器配置

如果设备支持OpenGL ES 2.0,通过GLSurfaceView调用setEGLContextClientVersion(2)配置GLSurfaceView以使用兼容的上下文为ES2.0。
然后通过setRenderer()方法设置自定义的Renderer类,本篇我们使用的是FirstOpenGLDemoRenderer(后面会讲到)。
之后设置rendererSet变量为true以记住渲染器已经被设置的状态。

        if(isSupportGLES2(this)){
            glSurfaceView.setEGLContextClientVersion(2)
            glSurfaceView.setRenderer(FirstOpenGLDemoRenderer())
            rendererSet = true
        }else{
            Toast.makeText(this,"this device does not support opengl es 2.0",Toast.LENGTH_LONG).show()
        }

当GLSurfaceView中的surface创建、改变或者需渲染时会调用传入的Renderer相应接口。

Activity生命周期处理

在Activity类中加入处理生命周期事件的方法,以确保应用在用户切换应用时不会崩溃。

在onPause()方法中,如果渲染器(renderer)已设置,则调用GLSurfaceView的onPause()方法。

在onResume()方法中,如果渲染器已设置,则调用GLSurfaceView的onResume()方法。

这些方法对于正确暂停和恢复后台渲染线程,以及释放和续用OpenGL上下文至关重要。

如果没有正确处理这些生命周期事件,应用程序可能会崩溃,并被Android系统终止。

此外需要确保渲染器已经设置,否则调用这些生命周期方法也会引起程序崩溃。

    override fun onPause() {
        super.onPause()
        if(rendererSet) glSurfaceView.onPause()
    }

    override fun onResume() {
        super.onResume()
        if(rendererSet) glSurfaceView.onResume()
    }

创建Renderer类

1. 接口

我们先了解下Renderer都有哪些接口。

onSurfaceCreated(GL10 glUnused, EGLConfig config)
这个方法在Surface被创建时被调用,通常在应用程序首次运行时发生。也可能在设备被唤醒或用户从其他Activity切换回来时被调用。这意味着在应用程序运行期间,这个方法可能会被多次调用。

onSurfaceChanged(GL10 glUnused, int width, int height)
每次Surface尺寸变化时,这个方法都会被调用。例如,当用户在横屏和竖屏之间切换时,Surface的尺寸会发生变化。

onDrawFrame(GL10 glUnused)
每次需要绘制一帧时,这个方法都会被调用。必须在这个方法中绘制一些内容,哪怕是清空屏幕。如果什么都不绘制,可能会导致屏幕闪烁效果。

所有接口中的GL10是一个未被使用的参数,是OpenGL ES 1.0 API的遗留。对于OpenGL ES2.0,使用GLES20类提供的静态方法来访问。

2. 新建渲染器

只需要新建一个类实现Renderer接口即可,我们这里还是以FirstOpenGLDemoRenderer为例。

实现onSurfaceCreated()函数

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

class FirstOpenGLDemoRenderer:Renderer {

    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        GLES20.glClearColor(0f,1f,0f,1f)
    }

在onSurfaceCreated()方法中调用GLES20.glClearColor()函数来设置清空屏幕时使用的颜色。该函数的参数分别是红色、绿色、蓝色和透明度(alpha)值。

我们这里将绿色设置为1,其余颜色设置为0,透明度设置为1,在屏幕清空时,它将显示出来是绿色不透明。

实现onSurfaceChanged()函数

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

在该函数内我们调用了GLES20.glViewport()函数来设置视窗的尺寸,第一个参数(x)和第二个参数(y)指定视窗在窗口中的位置。第三个参数(width)和第四个参数(height)指定视窗的宽度和高度。

设置视窗尺寸告诉OpenGL渲染时应该在屏幕上的哪个区域进行渲染。这确保了渲染内容正确地映射到屏幕上的特定区域。

通常在onSurfaceChanged()方法中调用glViewport(),以响应Surface尺寸的变化。这些内容主要涉及在OpenGL ES应用程序中如何设置视窗尺寸,以便正确地将渲染内容映射到屏幕上。

实现onDrawFrame()函数

    override fun onDrawFrame(p0: GL10?) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
    }

在onDrawFrame()方法中调用glClear(GL_COLOR_BUFFER_BIT)来清空屏幕。清空屏幕上的所有颜色。使用之前通过glClearColor()调用定义的颜色填充整个屏幕。

GL_COLOR_BUFFER_BIT是一个标志,指示glClear()函数应清空颜色缓冲区。

在每次绘制新帧时,onDrawFrame()会被调用。在该方法中,首先清空屏幕,然后进行其他绘制操作。

运行

运行之后将会看到一个绿色的空白界面,当然可以修改清屏颜色看看效果,预期屏幕上面的颜色会与代码中设置的一致。

~~ 这里就不贴图了 ~~

小结

本章介绍了如何从头开始创建一个新的OpenGL项目。介绍了如何初始化OpenGL项目,包括设置必要的组件。讨论了如何使OpenGL项目响应Android Activity的生命周期事件,例如暂停、恢复等。详细讲解了如何在OpenGL中清空屏幕,使用glClearColor()和glClear()函数。这些内容为读者提供了一个OpenGL项目的基本框架,并为进一步的学习打下了基础。后续章节中,我们将会继续在这个基础上构建项目,学习如何为GPU编程添加更多特性。

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值