OpenGL API

学习前瞻:

学习视频:

参考网址:

0. OpenGL相关介绍

OpenGL ES 是一套专为嵌入式设备设计的三维图形开发接口标准, 脱胎于 OpenGL.

OpenGL 家族以及他的主要竞争对手 Direct3D 都只是一套 API(说白了就是N个函数)标准, 显卡厂商根据标准开发出驱动,有了驱动 (当然还有SDK) 我们程序员就可以开始对显卡编程制作出绚丽的3D游戏.

程序员在开发游戏过程中发现不同游戏都要实现相同功能, 比如资源管理, 声音播放, 渲染. 于是很自然程序员就开始制作自己的工具完成这些重复工作, 游戏引擎就诞生了.

其中游戏引擎的一个工作就是抽象 OpenGL , D3D 这些底层API, 你再也不需要分别为OpenGL, D3D 编码, 只要调用引擎的抽象接口, 剩下的就交给引擎了.

用 Unity 发布 Android, iOS 的游戏, 那他就调用和绑定 OpenGL ES,

发布 PC 游戏, 就是D3D 或者 OpenGL.

首先,GPU要处理的事情,有以下几种:

3D,处理OpenGL ES;

2D的composition,包括BitBLT,Rotate等等,可以当是UI的合成(想象一层壁纸,一层图标,一层状态栏等),主要操作还是基于像素的;

2D的Vector Graphics,OpenVG,SVG,Flash之类的用用,绘制矢量图形;

所以,我们简称如下:3D, 2D-Composition, 2D-VG.

参考网址:

1. 如何在glutMainLoop()循环中正常返回,not exit

#include "GL/glew.h"
#include "GL/freeglut.h"

int main(int argc, char** argv) {

	glutInit(&argc, argv);

	if (GLUT_API_VERSION >= 4) {
		glutInitDisplayString( "rgb double depth>=24 samples" );
	} else {
		glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
	}

	glutInitWindowPosition(0,0);
	glutInitWindowPosition(500, 500);
	glutInitWindowSize(640, 480);
	glutCreateWindow("opengl");

	if (glewInit() != GLEW_OK) {
		return -1;
	}

  glutKeyboardFunc(Keyboard);
  glutReshapeFunc(reshape_func_ptr);
  glutDisplayFunc(display_func_ptr);
  glutIdleFunc(glutPostRedisplay);  //opengl free time callback
  
  glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); // **
  glutMainLoop(); // **
  
  return 0;
}

void Keyboard(unsigned char key, int x, int y) {
	switch( key ) {
	case 27: // esc
		glutLeaveMainLoop(); // **
		break;
	default:
	  break;
	}
}

参考网址: How to close GLUT window without terminating of application

2.1 OpenGL通用矩阵变换

矩阵变换:平移glTranslatef(),旋转glRotatef() 和缩放glScalef(),其中旋转遵循右手法则

矩阵变换组合:例如平移和旋转,先后顺序不同,导致结果不同。

参考网址: Android OpenGL ES 开发教程(15):通用的矩阵变换指令

参考代码: NeHe lesson 4

2.2 OpenGL模型视图(ModelView)矩阵变换

ModelView

使用逆序来指定坐标变换。

左图实现:

gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();

gl.glTranslatef(10.0f, 0.0f, 0.0f); //translation

gl.glRotatef(45.0f, 0.0f, 0.0f, 1.0f); //rotation
 
draw_the_object();

右图实现:

gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();

gl.glRotatef(45.0f, 0.0f, 0.0f, 1.0f); //rotation

gl.glTranslatef(10.0f, 0.0f, 0.0f); //translation
 
draw_the_object();

参考网址: Android OpenGL ES 开发教程(16):Viewing和Modeling(MODELVIEW) 变换

参考代码:

2.3 OpenGL模型视图(ModelView)矩阵之gluLookAt函数

gluLookAt

void gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ)

如果ModelView矩阵没有设置gluLookAt,相当于gluLookAt(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);

参考网址:

2.4 OpenGL投影(projection)矩阵变换

projection

void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)

参考网址: Android OpenGL ES 开发教程(17):投影变换Projection

参考代码: NeHe lesson 1

3. OpenGL纹理

...
glEnable(GL_TEXTURE_2D);
...
glDisable(GL_TEXTURE_2D);
...
GLuint texID_ = 0;
...
if (0 == texID_) {
	// create texture
	glGenTextures(1, &texID_);
	glBindTexture(GL_TEXTURE_2D, texID_);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0,
		GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
} else {
	glBindTexture(GL_TEXTURE_2D, texID_);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
		GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
}
...
// release texture
glDeleteTextures(1, &texID_);
texID_ = 0;

4. OpenGL混合(Blend)

OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜 色。(也可以不是相加,新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨 论这个了) 下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs, As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db, Da)。则混合产生的新颜色可以表示为: (RsSr+RdDr, GsSg+GdDg, BsSb+BdDb, AsSa+AdDa) 当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0。

OpenGL混合函数: void glBlendFunc(GLenum sfactor, GLenum dfactor);

Description: Pixels can be drawn using glBlendFunc that blends the incoming (source) RGBA values with the RGBA values that are already in the frame buffer (the destination values).

例如:带有alpha通道的源图像叠加到目标图像:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

参考文档

5. OpenGL模型加载相关-网格

参考文档

mesh

6. OpenGL ES 2.0 shader

opengle render pipeline

参考文档

7. OpenGL ES 2.0 shader language(GLSL)

参考文档

8. OpenGL ES 2.0 sample(GLSL)

开发环境搭建参考:

全用例参考: opengles-book-samples(github)

8.1 OpenGL ES 2.0 绘制一个三角形(webset)

8.2 OpenGL ES 2.0 绘制一个立方体(github)

/*
**  esRotate旋转和右手定则方向相反
*/
void Update ( ESContext *esContext, float deltaTime ) {
  ...
  //esTranslate( &modelview, 0.0, 0.0, -2.0 );
  //esRotate( &modelview, userData->angle, 1.0, 0.0, 1.0 );
  
  /* 模拟地球的太阳系公转和自转 */
  esTranslate( &modelview, 0.0, -1.5, -7.0 );
  esRotate( &modelview, userData->angle, 0.0, 1.0, 0.0 );
  esTranslate( &modelview, 3.0, 0.0, 0.0 );
  esRotate( &modelview, userData->angle, 0.0, 1.0, 0.0 );
  ...
}

8.3 OpenGL ES 2.0 纹理混合(github)

   // fragment shader (texture blend)
   GLbyte fShaderStr[] =  
      "precision mediump float;                            \n"
      "varying vec2 v_texCoord;                            \n"
      "uniform sampler2D s_baseMap;                        \n"
      "uniform sampler2D s_lightMap;                       \n"
      "void main()                                         \n"
      "{                                                   \n"
      "  vec4 baseColor;                                   \n"
      "  vec4 lightColor;                                  \n"
      "                                                    \n"
      "  baseColor = texture2D( s_baseMap, v_texCoord );   \n"
      "  lightColor = texture2D( s_lightMap, v_texCoord ); \n"
      "  gl_FragColor = baseColor * (lightColor + 0.25);   \n"
      // gl_FragColor = lightColor*lightColor.a + baseColor*(1.0-lightColor.a);
      "}                                                   \n";


void Draw ( ESContext *esContext ) {

   UserData *userData = esContext->userData;
   ...

   /*
   ** base map texture
   */
   glActiveTexture ( GL_TEXTURE0 ); // Active the texture unit 0
   glBindTexture ( GL_TEXTURE_2D, userData->baseMapTexId ); // Bind the texture object to unit 0

   // Set the base map sampler to texture unit 0
   glUniform1i ( userData->baseMapLoc, 0 );


   /*
   ** light map texture
   */
   glActiveTexture ( GL_TEXTURE1 ); // Active the texture unit 1
   glBindTexture ( GL_TEXTURE_2D, userData->lightMapTexId ); // Bind the texture object to unit 1
   
   // Set the light map sampler to texture unit 1
   glUniform1i ( userData->lightMapLoc, 1 );
   
   ...
}

参考网址:

8.4 OpenGL ES 2.0 shader language(github)

   GLbyte vShaderStr[] =
      "uniform float u_time;		                    \n"
      "uniform vec3 u_centerPosition;                       \n"
      "attribute float a_lifetime;                          \n"
      "attribute vec3 a_startPosition;                      \n"
      "attribute vec3 a_endPosition;                        \n"
      "varying float v_lifetime;                            \n"
      "void main()                                          \n"
      "{                                                    \n"
      "  if ( u_time <= a_lifetime )                        \n"
      "  {                                                  \n"
      "    gl_Position.xyz = a_startPosition +              \n"
      "                      (u_time * a_endPosition);      \n"
      "    gl_Position.xyz += u_centerPosition;             \n"
      "    gl_Position.w = 1.0;                             \n"
      "  }                                                  \n"
      "  else                                               \n"
      "     gl_Position = vec4( -1000, -1000, 0, 0 );       \n"
      "  v_lifetime = 1.0 - ( u_time / a_lifetime );        \n"
      "  v_lifetime = clamp ( v_lifetime, 0.0, 1.0 );       \n"
      "  gl_PointSize = ( v_lifetime * v_lifetime ) * 40.0; \n"
      "}";
      
   GLbyte fShaderStr[] =  
      "precision mediump float;                             \n"
      "uniform vec4 u_color;		                    \n"
      "varying float v_lifetime;                            \n"
      "uniform sampler2D s_texture;                         \n"
      "void main()                                          \n"
      "{                                                    \n"
      "  vec4 texColor;                                     \n"
      "  texColor = texture2D( s_texture, gl_PointCoord );  \n"
      "  gl_FragColor = vec4( u_color ) * texColor;         \n"
      "  gl_FragColor.a *= v_lifetime;                      \n"
      "}                                                    \n";

9. OpenGL ES 2.0 vao & vbo(GLSL)

OpenGL ES 2.0 MultiTexture 添加如下代码:

GLuint vao, vbo[2]; /* Create handles for our Vertex Array Object and two Vertex Buffer Objects */

int Init ( ESContext *esContext ) {
	...

	GLfloat vVertices[] = { -0.5f,  0.5f, 0.0f,
				-0.5f, -0.5f, 0.0f,
				 0.5f, -0.5f, 0.0f,
				-0.5f,  0.5f, 0.0f,
				 0.5f, -0.5f, 0.0f,
				 0.5f,  0.5f, 0.0f
			      };

	GLfloat texCoord[] = { 0.0f,  0.0f,
			       0.0f,  1.0f,
			       1.0f,  1.0f,
			       0.0f,  0.0f,
			       1.0f,  1.0f,
			       1.0f,  0.0f
			     };
	...

	/* Allocate and assign a Vertex Array Object to our handle */
	//glGenVertexArrays(1, &vao);
	
	/* Bind our Vertex Array Object as the current used object */
	//glBindVertexArray(vao);
	
	/* Allocate and assign two Vertex Buffer Objects to our handle */
	glGenBuffers(2, vbo);
	
	/* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	
	/* Copy the vertex data from vVertices to our buffer */
	glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
	
	/* Bind our second VBO as being the active buffer and storing vertex attributes (texture) */
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	
	/* Copy the vertex data from texCoord to our buffer */
	glBufferData(GL_ARRAY_BUFFER, sizeof(texCoord), texCoord, GL_STATIC_DRAW);
	
	...
}

///
// Draw a triangle using the shader pair created in Init() 
// using VAO and VBO
//
void Draw2 ( ESContext *esContext ) {
	UserData *userData = esContext->userData;
	
	// Set the viewport
	glViewport ( 0, 0, esContext->width, esContext->height );
	
	// Clear the color buffer
	glClear ( GL_COLOR_BUFFER_BIT );
	
	// Use the program object
	glUseProgram ( userData->programObject );
	
	glEnableVertexAttribArray(userData->positionLoc);
	glEnableVertexAttribArray(userData->texCoordLoc);
	
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	
	// Load the vertex position
	glVertexAttribPointer(userData->positionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
	
	/* Bind our second VBO as being the active buffer and storing vertex attributes (texture) */
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	
	/* Load the texture coordinate */
	glVertexAttribPointer(userData->texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
	
	/* bind texture */
	glActiveTexture ( GL_TEXTURE0 ); // Active the texture unit 0
	glBindTexture ( GL_TEXTURE_2D, userData->baseMapTexId ); // Bind the texture object to unit 0
	
	// Set the base map sampler to texture unit 0
	glUniform1i ( userData->baseMapLoc, 0 );
	
	// Bind the light map
	glActiveTexture ( GL_TEXTURE1 ); // Active the texture unit 1
	glBindTexture ( GL_TEXTURE_2D, userData->lightMapTexId ); // Bind the texture object to unit 1
	
	// Set the light map sampler to texture unit 1
	glUniform1i ( userData->lightMapLoc, 1 );
	
	// draw array
	glDrawArrays(GL_TRIANGLES, 0, 6);
	
	eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
	
	glDisableVertexAttribArray(userData->positionLoc);
	glDisableVertexAttribArray(userData->texCoordLoc);
}

void ShutDown ( ESContext *esContext ) {
	...
	glDeleteBuffers(2, vbo);
	//glDeleteVertexArrays(1, &vao);
}

int main ( int argc, char *argv[] ) {
	...
	//esRegisterDrawFunc ( &esContext, Draw );
	esRegisterDrawFunc ( &esContext, Draw2 );
	...
}

参考网址:

10. glDrawArrays(GLSL)

glDrawArrays (int mode, int first, int count)

参数mode

glDrawArrays's mode

参考网址:

11. EGL

OpenGL是一个操作GPU的API,它通过驱动向GPU发送相关指令,控制图形渲染管线状态机的运行状态。
但OpenGL需要本地视窗系统进行交互,这就需要一个中间控制层,最好与平台无关。

EGL——因此被独立的设计出来,它作为OpenGL ES和本地窗口的桥梁。

EGL 是 OpenGL ES(嵌入式)和底层 Native 平台视窗系统之间的接口。

EGL API 是独立于OpenGL ES各版本标准的独立API, 其主要作用是为OpenGL指令创建 Context、绘制目标Surface、 配置Framebuffer属性、Swap提交绘制结果等。

一般来说,OpenGL ES 图形管线的状态被存储于 EGL 管理的一个Context中。
而Frame Buffers 和其他绘制 Surfaces 通过 EGL API进行创建、管理和销毁。
EGL 同时也控制和提供了对设备显示和可能的设备渲染配置的访问。

参考: OpenGL ES:EGL接口解析与理解

12. eglContext和eglSurface的绑定和解绑

Android系统对于window也就是surface的管理是很严格的,因为一片nativewindow申请的内存其实是很大的。1080*1920的一个显示RGBA32 的buffer就在8M左右。当应用在后台时,nativewindow申请的buffer一般都会回收掉。

我们经常发现有的游戏,或者应用的界面,在转到Android系统后台再返回前台时, 界面或者游戏内容还在, 这是如何做到的呢?

从EGL的角度来看,这个问题就变得很简单。

EglContext代表了OpenGL的状态机, 只要eglContext还在就表明OpenGL的管线状态还在保持,只是渲染流程停止了。所以程序在后台时,eglContext还在。

EglSurface代表了渲染目标后端的Buffer, 当应用和游戏进入后台时,当前的EglContext与EglSurface进行了解绑。eglSurface的Buffer将被回收。
当应用或游戏重新进入前台时, 新的EglSurface将从NativeWindow中申请出来(新的Buffer),此时只要利用eglMakeCurrent重新把之前的eglContext和新的EglSurface进行绑定就可以了,渲染线程继续运行,界面马上又绘制出来。

注:将eglDisplay、eglContext、eglSurface解绑的API为
eglMakeCurrent(display,0, 0,0)
调用后,虽然之前的eglContext还在, 但此时调用OpenGL API将不起作用。

13. OpenGL(3.3) load model(.obj)

用例介绍: OpenGL OBJ Model Loader

代码: Download

其他参考网址: