前言
在使用OpenGL ES 繪制前,我先概括下接下來要做的工作:我先借用一個博主kiffa舉的的一個栗子,我覺得說的恰到好處,遂稍稍概括下來給大家分享分享:
我們想像一下畫家繪畫的過程,在作畫之前他是不是應該先準備準備呢,首先他得準備作畫的畫布(對應TexureView),一些筆,顏料以及輔助工具比如調色板等等(OpenGL ES API),然后他就可以在畫出他的作品了(SurfaceTexture)。
ok,假如他現在希望將它繪制好的畫上傳到電腦上通過顯示器來展示,那他先是不是得要獲取到要顯示設備,好為他的畫創造一個展示的環境,同時我們知道你在顯示器上所看到的一副圖像都是由無數個像素點(其實是一個很小很小的矩形)組成,而每幅畫都又是由各種顏色搭配組合而成,在顯示器上的繪制其實就是將畫的顏色往這些小矩形里面去填充,然而這些顏色都是畫家在調色板上一點點調出來的,與之相對應的是,這些顏色在顯示器上是可以用RGB(三原色)調制出來的,好了這些準備都到位以后,畫家繪制的畫就可以在屏幕上顯示出來了。若畫家以至少24幀/s的頻率將畫的畫不斷的輸送到屏幕上時就形成了一個連續的影像了。
為了將畫能夠呈現給大家,同時考慮到每個人顯示設備都可能不一樣的情況下,維護OpenGL 的khronos組織提供了一個專門抽象層接口(EGL API OpenGL ES 和底層 Native 平臺視窗系統之間的接口) 去適配不同的設備,以保證opengl es的平臺無關性。
下面我就要看看這個EGL是怎么配置的了,OK,又到了“talk is cheap,show me the code“的時刻了
代碼
/**
* 繪制前,renderer的配置,初始化EGL,開始一個繪制線程.
* 這個類需要子類去實現相應的繪制工作.
*/
public abstract class TextureSurfaceRenderer implements Runnable{
public static String LOG_TAG = TextureSurfaceRenderer.class.getSimpleName();
protected final SurfaceTexture surfaceTexture;
protected int width;
protected int height;
private EGL10 egl;
private EGLContext eglContext;
private EGLDisplay eglDisplay;
private EGLSurface eglSurface;
/***
* 是否正在繪制(draw)
*/
private boolean running = false;
public TextureSurfaceRenderer(SurfaceTexture surfaceTexture, int width, int height) {
this.surfaceTexture = surfaceTexture;
this.width = width;
this.height = height;
this.running = true;
/**開個線程來進行繪制工作*/
Thread thread = new Thread(this);
thread.start();
}
/**
* 初始化EGL環境
* 1.獲取display 2.初始畫egl
* 3.選擇config 4.構造surface
* 5.創建context 5.進行繪制
*/
private void initEGL() {
egl = (EGL10)EGLContext.getEGL();
//獲取顯示設備
eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
//version中存放EGL 版本號,int[0]為主版本號,int[1]為子版本號
int version[] = new int[2];
egl.eglInitialize(eglDisplay, version);
EGLConfig eglConfig = chooseEglConfig();
//創建EGL 的window surface 并且返回它的handles(eslSurface)
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null);
eglContext = createContext(egl, eglDisplay, eglConfig);
/**綁定context到當前渲染線程并且去繪制(通過opengl去繪制)和讀取surface(通過eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)來顯示)*/
try {
if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
throw new RuntimeException("GL error:" + GLUtils.getEGLErrorString(egl.eglGetError()));
}
if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
throw new RuntimeException("GL Make current Error"+ GLUtils.getEGLErrorString(egl.eglGetError()));
}
}catch (Exception e) {
e.printStackTrace();
}
@Override
public void run() {
initEGL();
initGLComponents();
Log.d(LOG_TAG, "OpenGL init OK. start draw...");
while (running) {
if (draw()) {
egl.eglSwapBuffers(eglDisplay, eglSurface);
}
}
deinitGLComponents();
deinitEGL();
}
private void deinitEGL() {
egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(eglDisplay, eglSurface);
egl.eglDestroyContext(eglDisplay, eglContext);
egl.eglTerminate(eglDisplay);
Log.d(LOG_TAG, "OpenGL deinit OK.");
}
/**
* 主要的繪制函數, 需在子類中去實現繪制
*/
protected abstract boolean draw();
/***
* 抽象函數,初始化opengl繪制所必需的一些組件比如vertextBuffer,sharders,textures等,
* 通常在Opengl context 初始化以后被調用,需要子類去實現
*/
protected abstract void initGLComponents();
/***
* 于init操作相反,做一些釋放資源,停止繪制工作
*/
protected abstract void deinitGLComponents();
/***
* 獲取生產的 SurfaceTexture,在子類中實現
*/
public abstract SurfaceTexture getSurfaceTexture();
/**
* 為當前渲染的API創建一個渲染上下文
* @return a handle to the context
*/
private EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int[] attrs = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrs);
}
/***
* 選擇一個你所期望的配置.
* @return 一個與你所指定最相近的一個EGL 幀緩存配置.
*/
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
int[] attributes = getAttributes();
int confSize = 1;
if (!egl.eglChooseConfig(eglDisplay, attributes, configs, confSize, configsCount)) { //獲取滿足attributes的config個數
throw new IllegalArgumentException("Failed to choose config:"+ GLUtils.getEGLErrorString(egl.eglGetError()));
}
else if (configsCount[0] > 0) {
return configs[0];
}
return null;
}
/**
* 構造你期望的繪制時所需要的特性配置,如ARGB,DEPTH...
*/
private int[] getAttributes()
{
return new int[] {
EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, //指定渲染api類別
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_STENCIL_SIZE, 0,
EGL10.EGL_NONE //總是以EGL10.EGL_NONE結尾
};
}
/**
* 當 當前activity停止時調用,用來停止渲染線程,并釋放資源.
*/
public void onPause()
{
running = false;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
running = false;
}
}
OK,代碼就先甩到這兒了,注釋也算很詳細了,很多EGL相關的API以及EGL的介紹強烈推薦你去官網去查查。其它的有什么問題敬請指正,感激不僅啊! 還有上篇鏈接 TextureView+SurfaceTexture+OpenGL ES來播放視頻(一),
以及下篇TextureView+SurfaceTexture+OpenGL ES來播放視頻(三),若是等不了博客的更新速度,可以先看看整個項目clone下來先睹為快,項目地址在here。