Android OpenGL ES:创建窗口

640 阅读4分钟

Android OpenGL ES系列目录

本系列文章主要是基于LearnOpenGL和对应的中文教程。与原教程主要的差异是,该系列讲解的是基于Android设备环境的OpenGL ES,并提供对应的Java示例。

本文原文:Creating a windowHello Window

中文原文:创建窗口你好,窗口

在我们使用OpenGL渲染令人惊叹的图形前,第一件要做的事情就是设置OpenGL工作的上下文环境:OpenGL版本号、渲染器、渲染模式等。然后就是设置OpenGL渲染的窗口大小。

初始化OpenGL

创建一个自定义View,继承自GLSurfaceView

public class Window extends GLSurfaceView {}

初始化

Window的构造方法中,初始化OpenGL

public Window(Context context, AttributeSet attrs) {
    super(context, attrs);
    setEGLContextClientVersion(2);
    setEGLConfigChooser(8, 8, 8, 8, 16, 8);
    
    setRenderer(new WindowRender());
    setRenderMode(RENDERMODE_WHEN_DIRTY);
}
  • setEGLContextClientVersion(int version)
    • 设置创建哪个版本的OpenGL上下文。如果设置了不支持的版本,会导致crash。
    • 传参为2:OpenGL ES 2.0 context
    • 传参为3:OpenGL ES 3.0 context
  • setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)
    • OpenGLES默认会设置一块RGB888的surface和至少16bit的深度值。
    • 前4个参数分别对应RGBA占用的bit数。
    • 后2个参数是深度值和模板(Stencil)值,后面会讲。
  • setRenderer(Renderer renderer)
    • 为当前View设置渲染器,并且启动渲染线程,调用渲染器进行渲染。渲染器负责调用OpenGL来渲染帧。该方法在GLSurfaceView的生命周期中只能调用一次。
    • 以下的GLSurfaceView的方法只能在setRender之前调用:
      • setEGLConfigChooser(boolean)
      • setEGLConfigChooser(GLSurfaceView.EGLConfigChooser)
      • setEGLConfigChooser(int, int, int, int, int, int)
    • 以下的GLSurfaceView的方法只能在setRender之后调用:
      • getRenderMode()
      • onPause()
      • onResume()
      • queueEvent(Runnable)
      • requestRender()
      • setRenderMode(int)
  • setRenderMode(int renderMode)
    • 设置渲染模式,默认是RENDERMODE_CONTINUOUSLY。
    • 两种渲染模式:
      • RENDERMODE_CONTINUOUSLY:渲染器会被定时重复用于渲染,不管数据是否更新
      • RENDERMODE_WHEN_DIRTY:渲染器只会在surface被创建以及requestRender方法被调用时,才会进行渲染。即有新数据才会进行渲染。按需渲染,更省电量、CPU、GPU

我们可以通过下面的代码获取设备支持的OpenGL ES版本

final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
String version = configurationInfo.getGlEsVersion();

渲染器创建

创建渲染器WindowRender,继承自GLSurfaceView.Renderer接口,并实现接口的几个方法。注意,这几个方法由GLSurfaceView调用,是运行在名为GLThread的子线程的。

class WindowRender implements GLSurfaceView.Renderer {

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    }

    @Override
    public void onDrawFrame(GL10 gl) {
    }
}

Android有两套OpenGL ES的API:

包javax.microedition.khronos.opengles提供OpenGL ES 1.0/1.1标准实现,不支持2.0及以上。

包android.opengl,提供了一套静态函数接口,包括OpenGL ES 1.0/1.1、2.0和3.0/3.1/3.2等。

用哪一个?

Android2.2之后开始提供了android.opengl。现在基本没有低于2.2的手机了,所以我们采用android.opengl即可。注意:GLSurfaceView.Renderer接口的几个方法都有参数GL10 gl,这是包javax.microedition.khronos.opengles的,所以平常开发切勿使用。

视窗

在渲染之前,需要告诉OpenGL视窗起始坐标、大小,比如:

GLES20.glViewport(0, 0, 800, 600);
  • glViewport(int x,int y,int width,int height)
    • x即left,y即top。(x,y)为窗口的左上角坐标点

我们通常是在渲染器的onSurfaceChanged里调用:

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
}

运行代码后,看到的输出画面如下:

未设置窗口颜色-黑色.png

OpenGL使用glViewport中定义的位置和宽高进行2D坐标的转换,将OpenGL中的位置坐标转换为你的屏幕坐标。例如,OpenGL中的坐标(-0.5, 0.5)有可能(最终)被映射为屏幕中的坐标(width / 4,height * 3 / 4)。注意,处理过的OpenGL x、y坐标范围均为-1到1,因此我们事实上将(-1, 1)范围内的x、y坐标分别映射到(0, width)和(0, height)。

渲染

渲染放在渲染器的onDrawFrame里,比如我们希望设置窗口的颜色:

@Override
public void onDrawFrame(GL10 gl) {
    GLES20.glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
  • glClearColor(float red,float green,float blue,float alpha)
    • 4个参数分别代表R、G、B、A。取值范围是[0,1]。从测试结果来看,大于1按1算,小于0按0算。
  • glClear(int mask)
    • 接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有GL_COLOR_BUFFER_BITGL_DEPTH_BUFFER_BITGL_STENCIL_BUFFER_BIT

运行代码后,看到的输出画面如下:

设置窗口颜色-青色.png

相关源码在WindowWindowRender中。