Android開發之OpenGL、OpenGL ES的概念和實例講解【轉】

轉自https://www.2cto.com/kf/201806/752471.html

 

什麼是OpenGL?

Open Graphics Library (OpenGL) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to interact with a graphics processing unit (GPU), to achieve hardware-accelerated rendering.

OpenGL是和編程語言,平臺無關的一套interface,主要是爲了rendering 2D和3D圖形等。一般這套接口是用來和GPU進行交互的,使用GPU進行rendering硬件加速。

什麼是OpenGL ES?

Android includes support for high performance 2D and 3D graphics with the Open Graphics Library (OpenGL), specifically, the OpenGL ES API. OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL specification intended for embedded devices.

OpenGL ES就是專門爲嵌入式設備設計的,當然售後機也是嵌入式,那麼OpenGL ES和OpenGL中的函數接口肯定有些是不一樣的,因爲嵌入式設備和pc等的硬件處理能力還是有差距的,不然手機卡死了。

既然OpenGL ES只是一組函數接口,那麼如何使用呢?我們肯定首先要去實現這些函數接口,而android提供了兩種類型的實現:軟件實現,硬件實現。

a, 硬件實現,前面提到這組函數接口主要是爲了和GPU這個硬件進行打交道的。所以各個硬件廠商會提供相關的實現,例如高通平臺的adreno解決方案;

b,軟件實現,android也提供了一套OpenGL ES的軟件實現,就是說不用GPU了,完全用軟件實現畫圖的相關功能,也就是libagl,代碼在frameworks\native\opengl\libagl,其makefile中,

?

1

2

3

/軟件實現最終編譯完保存在system\lib\egl\libGLES_android.so

LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl

LOCAL_MODULE:= libGLES_android

到此,已經有了OpenGL ES的具體實現,但是由於其實現的平臺無關係,所以在android上還不能使用,必須藉助EGL。

EGL

EGL - Native Platform Interface

EGL is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system.

It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs.

EGL,它是圖形渲染API(如OpenGL ES)與本地平臺窗口系統的一層接口,保證了OpenGL ES的平臺獨立性。EGL提供了若干的功能:創建rendering surface,創建graphics context,同步應用程序和本地平臺渲染API,提供對顯示設備的訪問,提供對渲染配置的管理等。

EGL提供了一種方法用於通過客戶端API和本地窗口系統進行渲染,客戶端API包括用於嵌入式系統的3D渲染器和OpenGLES,用於桌面系統的OpenGL ES的超集OpenGL,2D矢量圖形渲染器OpenVG,本地窗口系統包括Windows,X

那麼什麼是EGL?EGL是OpenGL ES和底層的native window system之間的接口,承上啓下。

EGL is a complement to OpenGL ES. EGL is used for getting surfaces to render to using functions like eglCreateWindowSurface, and you can then draw to that surface with OpenGL ES. Its role is similar to GLX/WGL/CGL.

Whether or not EGL can give you a context that supports OpenGL ES 2.0 may vary by platform, but if the Android device supports ES 2.0 and EGL, you should be able to get such a context from EGL. Take a look at the EGL_RENDERABLE_TYPE attribute and the EGL_OPENGL_ES2_BIT when requesting an EGLConfig.

在Android上,EGL完善了OpenGL ES。利用類似eglCreateWindowSurface的EGL函數可以創建surface 用來render ,有了這個surface你就能往這個surface中利用OpenGL ES函數去畫圖了。

EGL要做什麼

EGL既然做平臺和OpenGL ES的中間層,那EGL做的肯定就是和平臺息息相關的事:

創建繪圖窗口

也就是所謂的FrameBuffer,FrameBuffer可以先到到屏幕上(SurfaceView) 創建渲染環境(Context上下文)

渲染環境是指OpenGL ES的所有項目運行需要的 數據結構。如頂點,片段着色器,頂點數據矩陣

OpenGL渲染一般流程:

這裏寫圖片描述

EGLConfig屬性

屬性 描述 默認值
EGL_BUFFER_SIZE 顏色緩衝器中所有組成演的的位數 0
EGL_RED_SIZE 顏色緩衝區中紅色位數 0
EGL_LUMINANCE_SIZE 顏色緩衝區中透明度的位數 0
EGL_ALPTHA_SIZE 顏色緩衝區中透明位數 0
EGL_ALPTHHA_MASK_SIZE 遮擋緩衝區透明度掩碼位數 0
EGL_BIND_TO_TEXTURE_RGB 綁定到RGB貼圖使能爲真 EGL_DONT_CARE
EGL_BIND_TO_TEXTURE_RGBA 綁定到RGBA貼圖使能爲真 EGL_DONT_CARE
EGL_COLOR_BUFFER_TYPE 顏色緩衝區類型 EGL_RGB_BUFFER, 或者EGL_LUMINANCE_BUFFER EGL_RGB_BUFFER
EGL_CONFIG_CAVEAT 配置有關的警告信息 EGL_DONT_CARE
EGL_CONFIG_ID 唯一的 EGLConfig 標示值 EGL_DONT_CARE
EGL_CONFORMANT 使用EGLConfig 創建的上下文符合要求時爲真 -
EGL_DEPTH_SIZE 深度緩衝區位數 0
EGL_LEVEL 幀緩衝區水平 0
EGL_MAX_PBUFFER_WIDTH 使用EGLConfig 創建的PBuffer的最大寬度
EGL_MAX_PBUFFER_HEIGHT 使用EGLConfig 創建的PBuffer最大高度
EGL_MAX_PBUFFER_PIXELS 使用EGLConfig 創建的PBuffer最大尺寸
EGL_MAX_SWAP_INTERVAL 最大緩衝區交換間隔 EGL_DONT_CARE
EGL_MIN_SWAP_INTERVAL 最小緩衝區交換間隔 EGL_DONT_CARE
EGL_NATIVE_RENDERABLE 如果操作系統渲染庫能夠使用EGLConfig 創建渲染渲染窗口 EGL_DONT_CARE
EGL_NATIVE_VISUAL_ID 與操作系統通訊的可視ID句柄 EGL_DONT_CARE
EGL_NATIVE_VISUAL_TYPE 與操作系統通訊的可視ID類型 EGL_DONT_CARE
EGL_RENDERABLE_TYPE 渲染窗口支持的佈局組成標示符的遮擋位EGL_OPENGL_ES_BIT, EGL_OPENGL_ES2_BIT, orEGL_OPENVG_BIT that EGL_OPENGL_ES_BIT
EGL_SAMPLE_BUFFERS 可用的多重採樣緩衝區位數 0
EGL_SAMPLES 每像素多重採樣數 0
EGL_S TENCIL_SIZE 模板緩衝區位數 0
EGL_SURFACE_TYPE EGL 窗口支持的類型EGL_WINDOW_BIT, EGL_PIXMAP_BIT,或EGL_PBUFFER_BIT EGL_WINDOW_BIT
EGL_TRANSPARENT_TYPE 支持的透明度類型 EGL_NONE
EGL_TRANSPARENT_RED_VALUE 透明度的紅色解釋 EGL_DONT_CARE
EGL_TRANSPARENT_GRE EN_VALUE 透明度的綠色解釋 EGL_DONT_CARE
EGL_TRANSPARENT_BLUE_VALUE 透明度的蘭色解釋 EGL_DONT_CARE

一個簡單例子:

OpenGL的渲染是基於線程的,所以我們要創建一個GLLRenderer類繼承於HandlerThread:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

public class GLRenderer extends HandlerThread {

 

 private static final String TAG = "GLRenderer";

 private EGLConfig eglConfig = null;

 private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY;

 private EGLContext eglContext = EGL14.EGL_NO_CONTEXT;

 

 private int program;

 private int vPosition;

 private int uColor;

 

 

 public GLRenderer() {

  super("GLRenderer");

 }

 

 private void createGL(){

  eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

 

  int[] version = new int[2];

  if (!EGL14.eglInitialize(eglDisplay, version,0,version,1)) {

throw new RuntimeException("EGL error "+EGL14.eglGetError());

  }

 

  int[] configAttribs = {

 EGL14.EGL_BUFFER_SIZE,32,

 EGL14.EGL_ALPHA_SIZE,8,

 EGL14.EGL_BLUE_SIZE,8,

 EGL14.EGL_GREEN_SIZE,8,

 EGL14.EGL_RED_SIZE,8,

 EGL14.EGL_RENDERABLE_TYPE,EGL14.EGL_OPENGL_ES2_BIT,

 EGL14.EGL_SURFACE_TYPE,EGL14.EGL_WINDOW_BIT,

 EGL14.EGL_NONE

  };//獲取framebuffer格式和能力

 

  int[] numConfigs = new int[1];

  EGLConfig[] configs = new EGLConfig[1];

 

  if (!EGL14.eglChooseConfig(eglDisplay, configAttribs,0, configs,

 0,configs.length, numConfigs,0)) {

throw new RuntimeException("EGL error "+EGL14.eglGetError());

  }

 

  eglConfig = configs[0];

 

 

  //創建OpenGL上下文

  int[] contextAttribs = {

 EGL14.EGL_CONTEXT_CLIENT_VERSION,2,

 EGL14.EGL_NONE

  };

  eglContext = EGL14.eglCreateContext(eglDisplay,eglConfig,EGL14.EGL_NO_CONTEXT,contextAttribs,0);

 

  if(eglContext== EGL14.EGL_NO_CONTEXT) {

throw new RuntimeException("EGL error "+EGL14.eglGetError());

  }

 }

 

 private void destoryGL(){

  EGL14.eglDestroyContext(eglDisplay,eglContext);

  eglContext = EGL14.EGL_NO_CONTEXT;

  eglDisplay = EGL14.EGL_NO_DISPLAY;

 }

 

 @Override

 public synchronized void start() {

  super.start();

 

  new Handler(getLooper()).post(new Runnable() {

@Override

public void run() {

 createGL();

}

  });

 }

 

 public void release(){

  new Handler(getLooper()).post(new Runnable() {

@Override

public void run() {

 destoryGL();

 quit();

}

  });

 }

 

 /**

  * 加載制定shader的方法

  * @param shaderType shader的類型  GLES20.GL_VERTEX_SHADERGLES20.GL_FRAGMENT_SHADER

  * @param sourceCode shader的腳本

  * @return shader索引

  */

 private int loadShader(int shaderType,String sourceCode) {

  // 創建一個新shader

  int shader = GLES20.glCreateShader(shaderType);

  // 若創建成功則加載shader

  if (shader != 0) {

// 加載shader的源代碼

GLES20.glShaderSource(shader, sourceCode);

// 編譯shader

GLES20.glCompileShader(shader);

// 存放編譯成功shader數量的數組

int[] compiled = new int[1];

// 獲取Shader的編譯情況

GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

if (compiled[0] == 0) {//若編譯失敗則顯示錯誤日誌並刪除此shader

 Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");

 Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));

 GLES20.glDeleteShader(shader);

 shader = 0;

}

  }

  return shader;

 }

 

 

 /**

  * 創建shader程序的方法

  */

 private int createProgram(String vertexSource, String fragmentSource) {

  //加載頂點着色器

  int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);

  if (vertexShader == 0) {

return 0;

  }

 

  // 加載片元着色器

  int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);

  if (pixelShader == 0) {

return 0;

  }

 

  // 創建程序

  int program = GLES20.glCreateProgram();

  // 若程序創建成功則向程序中加入頂點着色器與片元着色器

  if (program != 0) {

// 向程序中加入頂點着色器

GLES20.glAttachShader(program, vertexShader);

// 向程序中加入片元着色器

GLES20.glAttachShader(program, pixelShader);

// 鏈接程序

GLES20.glLinkProgram(program);

// 存放鏈接成功program數量的數組

int[] linkStatus = new int[1];

// 獲取program的鏈接情況

GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

// 若鏈接失敗則報錯並刪除程序

if (linkStatus[0] != GLES20.GL_TRUE) {

 Log.e("ES20_ERROR", "Could not link program: ");

 Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));

 GLES20.glDeleteProgram(program);

 program = 0;

}

  }

  return program;

 }

 

 /**

  * 獲取圖形的頂點

  * 特別提示:由於不同平臺字節順序不同數據單元不是字節的一定要經過ByteBuffer

  * 轉換,關鍵是要通過ByteOrder設置nativeOrder(),否則有可能會出問題

  *

  * @return 頂點Buffer

  */

 private FloatBuffer getVertices() {

  float vertices[] = {

 0.0f,0.5f,

 -0.5f, -0.5f,

 0.5f,  -0.5f,

  };

 

  // 創建頂點座標數據緩衝

  // vertices.length*4是因爲一個float佔四個字節

  ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);

  vbb.order(ByteOrder.nativeOrder()); //設置字節順序

  FloatBuffer vertexBuf = vbb.asFloatBuffer(); //轉換爲Float型緩衝

  vertexBuf.put(vertices);//向緩衝區中放入頂點座標數據

  vertexBuf.position(0);  //設置緩衝區起始位置

 

  return vertexBuf;

 }

 

 public void render(Surface surface, int width, int height){

  final int[] surfaceAttribs = { EGL14.EGL_NONE };

  EGLSurface eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs, 0);

  EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

 

  // 初始化着色器

  // 基於頂點着色器與片元着色器創建程序

  program = createProgram(verticesShader, fragmentShader);

  // 獲取着色器中的屬性引用id(傳入的字符串就是我們着色器腳本中的屬性名)

  vPosition = GLES20.glGetAttribLocation(program, "vPosition");

  uColor = GLES20.glGetUniformLocation(program, "uColor");

 

  // 設置clear color顏色RGBA(這裏僅僅是設置清屏時GLES20.glClear()用的顏色值而不是執行清屏)

  GLES20.glClearColor(1.0f, 0, 0, 1.0f);

  // 設置繪圖的窗口(可以理解成在畫布上劃出一塊區域來畫圖)

  GLES20.glViewport(0,0,width,height);

  // 獲取圖形的頂點座標

  FloatBuffer vertices = getVertices();

 

  // 清屏

  GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

  // 使用某套shader程序

  GLES20.glUseProgram(program);

  // 爲畫筆指定頂點位置數據(vPosition)

  GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, vertices);

  // 允許頂點位置數據數組

  GLES20.glEnableVertexAttribArray(vPosition);

  // 設置屬性uColor(顏色 索引,R,G,B,A)

  GLES20.glUniform4f(uColor, 0.0f, 1.0f, 0.0f, 1.0f);

  // 繪製

  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 3);

 

  // 交換顯存(將surface顯存和顯示器的顯存交換)

  EGL14.eglSwapBuffers(eglDisplay, eglSurface);

 

  EGL14.eglDestroySurface(eglDisplay, eglSurface);

 }

 

 // 頂點着色器的腳本

 private static final String verticesShader

= "attribute vec2 vPosition;\n" // 頂點位置屬性vPosition

+ "void main(){ \n"

+ "gl_Position = vec4(vPosition,0,1);\n" // 確定頂點位置

+ "}";

 

 // 片元着色器的腳本

 private static final String fragmentShader

= "precision mediump float;\n" // 聲明float類型的精度爲中等(精度越高越耗資源)

+ "uniform vec4 uColor; \n" // uniform的屬性uColor

+ "void main(){\n"

+ "gl_FragColor = uColor;  \n" // 給此片元的填充色

+ "}";

 

 

}

createGL()方法獲取了一個默認的顯示設備(也就是手機屏幕),初始化並返回當前系統使用的OpenGL版本(主版本+子版本),然後通過配置(主要以鍵值對的方式配置,最後由EGL_NONE結尾)得到一個EGLConfig,最後創建一個EGLContext。

destroyGL()方法則是釋放掉OpenGL的資源(主要就是EGLContext)。

render()方法中主要是渲染,這裏爲了方便把渲染的環境和渲染寫在一起並只渲染一次(我們只畫了一個三角形),前三行代碼我們創建了一個EGLSurface並設置爲當前的渲染對象,後面eglSwapBuffers()交換了顯示器和EGLSurface的顯存,也就是將我們渲染的東西放到顯示器去顯示,這樣我們就看到我們繪製的三角形了,最後就是銷燬我們創建的EGLSurface。

然後在Activity中使用它:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public class MainActivity extends Activity {

 private GLRenderer glRenderer;

 

 @Override

 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_main);

 

  SurfaceView sv = (SurfaceView)findViewById(R.id.sv_main_demo);

  glRenderer = new GLRenderer();

  glRenderer.start();

 

  sv.getHolder().addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder surfaceHolder) {

 

}

 

@Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {

 glRenderer.render(surfaceHolder.getSurface(),width,height);

}

 

@Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

 

}

  });

 }

 

 @Override

 protected void onDestroy() {

  glRenderer.release();

  glRenderer = null;

  super.onDestroy();

 }

}

然後我們就可以得到一個:
這裏寫圖片描述

然後我們繼續學習:

EGL創建EGLSurface有三個方法:glCreateWindowSurface()、eglCreatePbufferSurface()和eglCreatePixmapSurface()。

WindowSurface:是和窗口相關的,也就是在屏幕閃給的一塊顯示區的封裝,渲染後即顯示在界面上。 PbufferSurface:在顯存中開闢一個控件,將渲染後的數據(幀)存放在這裏 pixmapSurface:以位圖的形式存放在內存中(支持性不好)

做離屏渲染我們可以選擇PbufferSurface或者PixmapSurface(其實WindowSurface也是可以的)

(幀緩存對象FBO是對幀緩存的封裝,性能要優於Pbuffer但並非可以完全替代Pbuffer)。

OpenGL操作的最終目標實際上是幀緩存(Frame Buffer)後面的各種表現形式則是EGL對Frame Buffer的封裝
這裏寫圖片描述

對之前的代碼進行整理:

新建GLSurface類,對EGLSurface進行封裝 將GLRenderer類改爲抽象類,繼承於Thread GLRenderer添加一個阻塞隊列(消息隊列),用於交互和解耦 GLRenderer添加一個Event內部類 GLRenderer中添加生命週期,將渲染之類的具體工作放到實現類中 添加ShaderUtil類,將着色器創建的代碼封裝到這個類中

代碼:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package com.example.asus1.camerawithopengl;

 

import android.opengl.EGL14;

import android.opengl.EGLSurface;

import android.view.Surface;

 

public class GLSurface {

 

 public static final int TYPE_WINDOW_SURFACE  = 0;

 public static final int TYPE_PBUFFER_SURFACE = 1;

 public static final int TYPE_PIXMAP_SURFACE  = 2;

 

 

 protected final int type;

 protected Object surface;

 protected EGLSurface eglSurface = EGL14.EGL_NO_SURFACE;

 protected Viewport viewport = new Viewport();

 

 public GLSurface(int width,int height){

  setViewport(0,0,width,height);

  surface = null;

  type = TYPE_PBUFFER_SURFACE;

 }

 public GLSurface(Surface surface, int width, int height) {

 

  this(surface,0,0,width,height);

 }

 

 public GLSurface(Surface surface, int x, int y, int width, int height) {

  setViewport(x, y, width, height);

  this.surface = surface;

  type = TYPE_WINDOW_SURFACE;

 }

 

 

 public void setViewport(int x,int y,int width,int height){

  viewport.x = x;

  viewport.y = y;

  viewport.width = width;

  viewport.height = height;

 }

 

 public Viewport getViewport(){

  return viewport;

 }

 

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

public abstract class GLRenderer1 extends Thread{

 

 private static final String TAG = "GLThread";

 private EGLConfig eglConfig = null;

 private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY;

 private EGLContext eglContext = EGL14.EGL_NO_CONTEXT;

 

 private ArrayBlockingQueue<event> eventQueue;

 private final List<glsurface> outputSurfaces;

 private boolean rendering;

 private boolean isRelease;

 

 public GLRenderer1() {

  setName("GLRenderer-"+getId());

  outputSurfaces = new ArrayList<>();

  rendering = false;

  isRelease = false;

 

  eventQueue = new ArrayBlockingQueue<>(100);

 }

 

 private boolean makeOutputSurface(GLSurface surface) {

  // 創建Surface緩存

  try {

switch (surface.type) {

 case GLSurface.TYPE_WINDOW_SURFACE: {

  final int[] attributes = {EGL14.EGL_NONE};

  // 創建失敗時返回EGL14.EGL_NO_SURFACE

  surface.eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface.surface, attributes, 0);

  break;

 }

 case GLSurface.TYPE_PBUFFER_SURFACE: {

  final int[] attributes = {

 EGL14.EGL_WIDTH, surface.viewport.width,

 EGL14.EGL_HEIGHT, surface.viewport.height,

 EGL14.EGL_NONE};

  // 創建失敗時返回EGL14.EGL_NO_SURFACE

  surface.eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, attributes, 0);

  break;

 }

 case GLSurface.TYPE_PIXMAP_SURFACE: {

  Log.w(TAG, "nonsupport pixmap surface");

  return false;

 }

 default:

  Log.w(TAG, "surface type error " + surface.type);

  return false;

}

  } catch (Exception e) {

Log.w(TAG, "can't create eglSurface");

surface.eglSurface = EGL14.EGL_NO_SURFACE;

return false;

  }

 

  return true;

 }

 

 public void addSurface(@NonNull final GLSurface surface){

  Event event = new Event(Event.ADD_SURFACE);

  event.param = surface;

  if(!eventQueue.offer(event))

Log.e(TAG,"queue full");

 }

 

 public void removeSurface(@NonNull final GLSurface surface){

  Event event = new Event(Event.REMOVE_SURFACE);

  event.param = surface;

  if(!eventQueue.offer(event))

Log.e(TAG,"queue full");

 }

 

 /**

  * 開始渲染

  * 啓動線程並等待初始化完畢

  */

 public void startRender(){

  if(!eventQueue.offer(new Event(Event.START_RENDER)))

Log.e(TAG,"queue full");

  if(getState()==State.NEW) {

super.start(); // 啓動渲染線程

  }

 }

 

 public void stopRender(){

  if(!eventQueue.offer(new Event(Event.STOP_RENDER)))

Log.e(TAG,"queue full");

 }

 

 public boolean postRunnable(@NonNull Runnable runnable){

  Event event = new Event(Event.RUNNABLE);

  event.param = runnable;

  if(!eventQueue.offer(event)) {

Log.e(TAG, "queue full");

return false;

  }

 

  return true;

 }

 

 @Override

 public void start() {

  Log.w(TAG,"Don't call this function");

 }

 

 public void requestRender(){

  eventQueue.offer(new Event(Event.REQ_RENDER));

 }

 

 /**

  * 創建OpenGL環境

  */

 private void createGL() {

  // 獲取顯示設備(默認的顯示設備)

  eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

  // 初始化

  int[] version = new int[2];

  if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) {

throw new RuntimeException("EGL error " + EGL14.eglGetError());

  }

  // 獲取FrameBuffer格式和能力

  int[] configAttribs = {

 EGL14.EGL_BUFFER_SIZE, 32,

 EGL14.EGL_ALPHA_SIZE, 8,

 EGL14.EGL_BLUE_SIZE, 8,

 EGL14.EGL_GREEN_SIZE, 8,

 EGL14.EGL_RED_SIZE, 8,

 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,

 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,

 EGL14.EGL_NONE

  };

  int[] numConfigs = new int[1];

  EGLConfig[] configs = new EGLConfig[1];

  if (!EGL14.eglChooseConfig(eglDisplay, configAttribs, 0, configs, 0, configs.length, numConfigs, 0)) {

throw new RuntimeException("EGL error " + EGL14.eglGetError());

  }

  eglConfig = configs[0];

  // 創建OpenGL上下文(可以先不設置EGLSurface,但EGLContext必須創建,

  // 因爲後面調用GLES方法基本都要依賴於EGLContext)

  int[] contextAttribs = {

 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,

 EGL14.EGL_NONE

  };

  eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT, contextAttribs, 0);

  if (eglContext == EGL14.EGL_NO_CONTEXT) {

throw new RuntimeException("EGL error " + EGL14.eglGetError());

  }

  // 設置默認的上下文環境和輸出緩衝區(小米4上如果不設置有效的eglSurface後面創建着色器會失敗,可以先創建一個默認的eglSurface)

  //EGL14.eglMakeCurrent(eglDisplay, surface.eglSurface, surface.eglSurface, eglContext);

  EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, eglContext);

 }

 

 /**

  * 銷燬OpenGL環境

  */

 private void destroyGL() {

  EGL14.eglDestroyContext(eglDisplay, eglContext);

  eglContext = EGL14.EGL_NO_CONTEXT;

  eglDisplay = EGL14.EGL_NO_DISPLAY;

 }

 

 /**

  * 渲染到各個eglSurface

  */

 private void render(){

  // 渲染(繪製)

  for(GLSurface output:outputSurfaces){

if(output.eglSurface== EGL14.EGL_NO_SURFACE) {

 if(!makeOutputSurface(output))

  continue;

}

// 設置當前的上下文環境和輸出緩衝區

EGL14.eglMakeCurrent(eglDisplay, output.eglSurface, output.eglSurface, eglContext);

// 設置視窗大小及位置

GLES20.glViewport(output.viewport.x, output.viewport.y, output.viewport.width, output.viewport.height);

// 繪製

onDrawFrame(output);

// 交換顯存(將surface顯存和顯示器的顯存交換)

EGL14.eglSwapBuffers(eglDisplay, output.eglSurface);

  }

 }

 

 @Override

 public void run() {

  Event event;

 

  Log.d(TAG,getName()+": render create");

  createGL();//創建display,config,context,現在是沒有surface的,只是將contxt聯繫起來

  onCreated();//得到program,postion,color和頂點的buffer

  // 渲染

  while(!isRelease){

try {

 event = eventQueue.take();

 switch(event.event){

  case Event.ADD_SURFACE: {

// 創建eglSurface

GLSurface surface = (GLSurface)event.param;

Log.d(TAG,"add:"+surface);

makeOutputSurface(surface);

outputSurfaces.add(surface);

break;

  }

  case Event.REMOVE_SURFACE: {

GLSurface surface = (GLSurface)event.param;

Log.d(TAG,"remove:"+surface);

EGL14.eglDestroySurface(eglDisplay, surface.eglSurface);

outputSurfaces.remove(surface);

 

break;

  }

  case Event.START_RENDER:

rendering = true;

break;

  case Event.REQ_RENDER: // 渲染

if(rendering) {

 Log.d(TAG, "run: rendering");

 onUpdate();

 render(); // 如果surface緩存沒有釋放(被消費)那麼這裏將卡住

}

break;

  case Event.STOP_RENDER:

rendering = false;

break;

  case Event.RUNNABLE:

((Runnable)event.param).run();

break;

  case Event.RELEASE:

isRelease = true;

break;

  default:

Log.e(TAG,"event error: "+event);

break;

 }

} catch (InterruptedException e) {

 e.printStackTrace();

}

  }

  // 回調

  onDestroy();

  // 銷燬eglSurface

  for(GLSurface outputSurface:outputSurfaces){

EGL14.eglDestroySurface(eglDisplay, outputSurface.eglSurface);

outputSurface.eglSurface = EGL14.EGL_NO_SURFACE;

  }

  destroyGL();

  eventQueue.clear();

  Log.d(TAG,getName()+": render release");

 }

 

 /**

  * 退出OpenGL渲染並釋放資源

  * 這裏先將渲染器釋放(renderer)再退出looper,因爲renderer裏面可能持有這個looper的handler,

  * 先退出looper再釋放renderer可能會報一些警告信息(sending message to a Handler on a dead thread)

  */

 public void release(){

  if(eventQueue.offer(new Event(Event.RELEASE))){

// 等待線程結束,如果不等待,在快速開關的時候可能會導致資源競爭(如競爭攝像頭)

// 但這樣操作可能會引起界面卡頓,擇優取捨

while (isAlive()){

 try {

  this.join(1000);

 } catch (InterruptedException e) {

  e.printStackTrace();

 }

}

  }

 }

 

 /**

  * 當創建完基本的OpenGL環境後調用此方法,可以在這裏初始化紋理之類的東西

  */

 public abstract void onCreated();

 

 /**

  * 在渲染之前調用,用於更新紋理數據。渲染一幀調用一次

  */

 public abstract void onUpdate();

 

 /**

  * 繪製渲染,每次繪製都會調用,一幀數據可能調用多次(不同是輸出緩存)

  * @param outputSurface 輸出緩存位置surface

  */

 public abstract void onDrawFrame(GLSurface outputSurface);

 

 /**

  * 當渲染器銷燬前調用,用戶回收釋放資源

  */

 public abstract void onDestroy();

 

 

 private static String getEGLErrorString() {

  return GLUtils.getEGLErrorString(EGL14.eglGetError());

 }

 

 private static class Event {

  static final int ADD_SURFACE = 1; // 添加輸出的surface

  static final int REMOVE_SURFACE = 2; // 移除輸出的surface

  static final int START_RENDER = 3; // 開始渲染

  static final int REQ_RENDER = 4; // 請求渲染

  static final int STOP_RENDER = 5; // 結束渲染

  static final int RUNNABLE = 6; //

  static final int RELEASE = 7; // 釋放渲染器

 

  final int event;

  Object param;

 

  Event(int event) {

this.event = event;

  }

 }

 

 

 

}

</glsurface></event>

離屏渲染關鍵部分就是在makeOutputSurface()方法中的GLSurface.TYPE_PBUFFER_SURFACE這個case裏面

其實// 交換顯存(將surface顯存和顯示器的顯存交換)
EGL14.eglSwapBuffers(eglDisplay, output.eglSurface);對於離屏渲染是沒有用的,因爲本來就沒有顯示屏幕,於是我們可以這樣:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

package com.example.asus1.camerawithopengl;

 

import android.opengl.GLES20;

 

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

 

public class TestRenderer extends GLRenderer1 {

 

 private static final String TAG = "TestRenderer";

 private int program;

 private int vPosition;

 private int uColor;

 

 private FloatBuffer vertices;

 

 

 /**

  * 獲取圖形的頂點

  * 特別提示:由於不同平臺字節順序不同數據單元不是字節的一定要經過ByteBuffer

  * 轉換,關鍵是要通過ByteOrder設置nativeOrder(),否則有可能會出問題

  *

  * @return 頂點Buffer

  */

 private FloatBuffer getVertices() {

  float vertices[] = {

 0.0f,0.5f,

 -0.5f, -0.5f,

 0.5f,  -0.5f,

  };

 

  // 創建頂點座標數據緩衝

  // vertices.length*4是因爲一個float佔四個字節

  ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);

  vbb.order(ByteOrder.nativeOrder()); //設置字節順序

  FloatBuffer vertexBuf = vbb.asFloatBuffer(); //轉換爲Float型緩衝

  vertexBuf.put(vertices);//向緩衝區中放入頂點座標數據

  vertexBuf.position(0);  //設置緩衝區起始位置

 

  return vertexBuf;

 }

 

 

 

 @Override

 public void onCreated() {

 

  program = ShaderUtil.createProgram(verticesShader,fragmentShader);

  vPosition = GLES20.glGetAttribLocation(program,"vPosition");

  uColor = GLES20.glGetUniformLocation(program,"uColor");

 

  vertices = getVertices();

 

 }

 

 @Override

 public void onUpdate() {

 

 }

 

 @Override

 public void onDrawFrame(GLSurface outputSurface) {

// 設置clear color顏色RGBA(這裏僅僅是設置清屏時GLES20.glClear()用的顏色值而不是執行清屏)

  GLES20.glClearColor(1.0f, 0, 0, 1.0f);

 

  // 清除深度緩衝與顏色緩衝(清屏,否則會出現繪製之外的區域花屏)

  GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

  // 使用某套shader程序

  GLES20.glUseProgram(program);

  // 爲畫筆指定頂點位置數據(vPosition)

  GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, vertices);

  // 允許頂點位置數據數組

  GLES20.glEnableVertexAttribArray(vPosition);

  // 設置屬性uColor(顏色 索引,R,G,B,A)

  GLES20.glUniform4f(uColor, 0.0f, 1.0f, 0.0f, 1.0f);

  // 繪製

  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 3);

 

 }

 

 @Override

 public void onDestroy() {

 

 }

 

 // 頂點着色器的腳本

 private static final String verticesShader

= "attribute vec2 vPosition;\n" // 頂點位置屬性vPosition

+ "void main(){ \n"

+ "gl_Position = vec4(vPosition,0,1);\n" // 確定頂點位置

+ "}";

 

 // 片元着色器的腳本

 private static final String fragmentShader

= "precision mediump float;\n" // 聲明float類型的精度爲中等(精度越高越耗資源)

+ "uniform vec4 uColor; \n" // uniform的屬性uColor

+ "void main(){\n"

+ "gl_FragColor = uColor;  \n" // 給此片元的填充色

+ "}";

 

}

在activity中

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

  @Override

 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

 

  setContentView(R.layout.activity_main);

  final SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface);

  mImageView = (ImageView)findViewById(R.id.image);

  glRenderer = new TestRenderer();

  GLSurface glPbufferSurface = new GLSurface(512,512);

  glRenderer.addSurface(glPbufferSurface);

  glRenderer.startRender();

  glRenderer.requestRender();

 

  glRenderer.postRunnable(new Runnable() {

@Override

public void run() {

 IntBuffer i = IntBuffer.allocate(512*512);

 GLES20.glReadPixels(0, 0, 512, 512, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, i);

 

 final Bitmap bitmap = frameToBitmap(512,512,i);

 new Handler(Looper.getMainLooper()).post(new Runnable() {

  @Override

  public void run() {

mImageView.setImageBitmap(bitmap);

  }

 });

}

  });

  surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

 

}

 

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

 // glRenderer.render(holder.getSurface(),width,height);

 GLSurface glWindowSurface = new GLSurface(holder.getSurface(),width,height);

 glRenderer.addSurface(glWindowSurface);

 glRenderer.requestRender();

 

 

}

 

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

 

}

  });

 

 

 

 }

 

 @Override

 protected void onDestroy() {

 // mPreview.destoryCamera();

  glRenderer.release();

  glRenderer = null;

  super.onDestroy();

 

 }

 

 /**

  * 將數據轉換成bitmap(OpenGL和Android的Bitmap色彩空間不一致,這裏需要做轉換)

  *

  * @param width 圖像寬度

  * @param height 圖像高度

  * @param ib 圖像數據

  * @return bitmap

  */

 private static Bitmap frameToBitmap(int width, int height, IntBuffer ib) {

  int pixs[] = ib.array();

  // 掃描轉置(OpenGl:左上->右下 Bitmap:左下->右上)

  for (int y = 0; y < height / 2; y++) {

for (int x = 0; x < width; x++) {

 int pos1 = y * width + x;

 int pos2 = (height - 1 - y) * width + x;

 

 int tmp = pixs[pos1];

 pixs[pos1] = (pixs[pos2] & 0xFF00FF00) | ((pixs[pos2] >> 16) & 0xff) | ((pixs[pos2] << 16) & 0x00ff0000); // ABGR->ARGB

 pixs[pos2] = (tmp & 0xFF00FF00) | ((tmp >> 16) & 0xff) | ((tmp << 16) & 0x00ff0000);

}

  }

  if (height % 2 == 1) { // 中間一行

for (int x = 0; x < width; x++) {

 int pos = (height / 2 + 1) * width + x;

 pixs[pos] = (pixs[pos] & 0xFF00FF00) | ((pixs[pos] >> 16) & 0xff) | ((pixs[pos] << 16) & 0x00ff0000);

}

  }

 

  return Bitmap.createBitmap(pixs, width, height, Bitmap.Config.ARGB_8888);

 }

我們可以看到我們其實進行了兩次渲染,第一次是離屏,第二次使用的是window_surface。爲了看到離屏的效果,我們必須從OpenGL的線程中取出像素數據,然後轉換成Bitmap設置給ImageView。
然後就能看到兩個三角形了,頂部的小三角形就是在後臺渲染的圖像了

這裏寫圖片描述

知識補充

Display

EGL提供了圖形API如OpenGL ES和原生窗口系統如Linux 的X Window之間的一個結合層次,在EGL能夠確定可用的Surface類型之前,它必須打開和窗口系統的通信渠道,因爲是跨平臺的,每個窗口系統都有不同的遺言,所以EGL提供基本的不透明類型的EGLDisplai,該類型封裝了所有系統的相關屬性,用於原生窗口的系統接口,不同的窗口系統定義了不同的DIsplay屬性,最終都會Native化。任何使用EGL的應用程序必須執行的第一個操作是創建與初始化與本地EGL Display的連接:

?

1

2

eglGetDisplay(EGLNativeDisplayType display_id);

eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)

eglGetDisplay用於獲取EGL DIsplay連接,display_id指定需要連接的Display,一般取默認值EGL_DEFAULT_DISPLAY
eglInitialize用於對Display進行初始化,初始化已經初始化了Display對這個Display沒有影響,major和minor獲取當前的EGL版本號

#配置Surface

EGL初始化Display完成後,就可以對Surface進行配置了,一種是查詢每個Surface配置,找出最好的選擇,另一種是指定一組需求,讓EGL推薦最佳匹配,兩者都返回一個EGLConfig,包括Surface相關的所有屬性。

?

1

2

3

4

5

6

7

eglGetConfigs(EGLDisplay dpy, EGLConfig *configs,

 EGLint config_size, EGLint *num_config);

 eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,

EGLint attribute, EGLint *value);

 eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list,

EGLConfig *configs, EGLint config_size,

EGLint *num_config);

eglGetConfigs用於獲取Display的frame buffer配置列表,dpy爲對應的Display,configs返回配置列表(可以爲NULL而只是通過num_config獲取配置列表可用的配置條目),size指定配置列表的大小(size大於1時configs需要有足夠的存儲空間),num_config返回配置列表獲取的配置條目。

eglGetConfigAttrib用於獲取EGL的frame buffer配置列表中某個具體屬性的值,dpy爲對應的Display,config爲待查詢的配置列表,attribute爲待查詢的具體屬性,value返回查詢的屬性值,

eglChooseConfig用於獲取Display的frame buffer配置列表,不過這個配置列表要與指定的屬性列表attrib_list匹配,dpy爲對應的Display,attrib_list爲config需要匹配的屬性列表,configs返回配置列表(非NULL時,size爲最大值,num_configs爲實際值,爲NULL時,忽略size),size指定配置列表的大小(size大於1時configs需要有足夠的存儲空間),num_config返回配置列表獲取的配置條目

創建Surface

Window Surface:可以理解爲EGL窗口,是屏幕上的渲染區域,有eglCreateWindowSurface創建

?

1

2

3

eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,

EGLNativeWindowType win,

const EGLint *attrib_list);

eglCreateWindowSurface用於創建Window Surface,dpy爲對應的EGL Display連接,config爲EGL frame buffer配置,定義了可用於Surface的frame buffer資源,win爲Native Window,是個平臺相關的類型,attrib_list爲Window Surface屬性列表,可以爲NULL。

?

1

eglDestroySurface(EGLDisplay dpy, EGLSurface surface

用於銷燬surface,dpy爲對應的Display,surface爲將要銷燬的Surface,如果任何其它線程都沒有使用這個Surface時,Surface將被立即銷燬,否則要等到這個Surface不被任何線程使用時才銷燬,另外,對於一個PBuffer Surface來說,其資源要等到綁定到紋理對象的顏色緩衝區釋放後才被銷燬。

?

1

2

glQuerySurface(EGLDisplay dpy, EGLSurface surface,

EGLint attribute, EGLint *value);

於獲取Surface信息,dpy爲對應的Display,surface待查詢的Surface,attribute爲待查詢的Surface屬性,value用於返回Surface屬性值

PBuffer Surface:稱作PBuffer(像素緩衝區Pixel Buffer的縮寫)的不可見屏幕外表面,和窗口一樣,PBuffer可以利用OpenGL ES 3.0中的任何硬件加速,PBuffer最常用於生成紋理貼圖,如果想要做的是渲染到一個紋理,那麼建議使用幀緩衝區對象(FBO)代替PBuffer,因爲幀緩衝區更高效,不過在某些FBO無法使用的情況下,PBuffer仍然有用,

?

1

2

glCreatePbufferSurface(EGLDisplay dpy, EGLConfig config,

 const EGLint *attrib_list);

eglCreatePbufferSurface用於創建off-screen的pixel buffer Surface,dpy爲對應的Display,config爲frame buffer配置,attrib_list爲PBuffer屬性列表,可以爲NULL

Pixmap Surface:

?

1

2

3

eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config,

EGLNativePixmapType pixmap,

const EGLint *attrib_list);

用於創建off-screen的Pixmap Surface,dpy爲對應的Display,config爲frame buffer配置,pixmap爲Native Pixmap,attrib_list爲Pixmap屬性列表,可以爲NULL

三種Surface的區別

window是on-screen的,
pbuffer和pixmap是off-screen的, window綁定到了NativeWindow
,pixmap綁定到了NativePixmap,
pbuffer沒有任何本地綁定 window是雙緩衝區的
pbuffer和pixmap是單緩衝區的, window默認在back buffer渲染,通過eglSwapBuffers交換到屏幕上顯示,
pbuffer在顯存中分配,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作紋理數據,
pixmap綁定到本地的像素緩衝區,這個緩衝區可以被其它API使用。 創建不同的EGLSurface時,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分別對應於EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。


?

1

eglSwapBuffers——

對於Window Surface或back buffer來說,還需要通過eglSwapBuffers把off-sreen的back buffer交換到screen buffer,也就是說把EGL Surface的顏色緩衝區post到Native Window,內部調用了渲染API的Flush命令,返回EGL_FALSE時可能的原因爲EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_CONTEXT_LOST。
eglSwapBuffers對PBuffer Surface和Pixmap Surface無效。

創建Context

?

1

2

3

eglCreateContext(EGLDisplay dpy, EGLConfig config,

 EGLContext share_context,

 const EGLint *attrib_list);

eglCreateContext用於創建EGL渲染Context,dpy爲對應的Display,config爲frame buffer配置,share_context爲其它的共享Context,可以設置爲EGL_NO_CONTEXT,attrib_list爲Context屬性列表,成功時返回新創建的EGLContext,失敗時返回EGL_NO_CONTEXT

ttrib_list屬性目前只有EGL_CONTEXT_CLIENT_VERSION,整數值,指定OpenGL ES版本,默認值爲1,還可以是2、3等其它版本值,創建OpenGL ES Context時設置這個屬性,也就是說渲染API爲EGL_OPENGL_ES_API時才設置這個屬性

?

1

2

eglMakeCurrent(EGLDisplay dpy, EGLSurface draw,

  EGLSurface read, EGLContext ctx);

創建了Surface和Context之後,因爲可能有多個Surface和Context,所以需要通過eglMakeCurrent綁定Context到Surface,dpy爲對應的Display,draw用於繪製,read用於讀,ctx爲要綁定的Context

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章