本系列文章主要是基于LearnOpenGL和对应的中文教程。与原教程主要的差异是,该系列讲解的是基于Android设备环境的OpenGL ES,并提供对应的Java示例。
在我们使用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);
}
运行代码后,看到的输出画面如下:
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算。
- 4个参数分别代表R、G、B、A。取值范围是
- glClear(int mask)
- 接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有
GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。
- 接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有
运行代码后,看到的输出画面如下:
相关源码在Window和WindowRender中。