学习前瞻:
学习视频:
参考网址:
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.
参考网址:
#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
矩阵变换:平移glTranslatef(),旋转glRotatef() 和缩放glScalef(),其中旋转遵循右手法则。
矩阵变换组合:例如平移和旋转,先后顺序不同,导致结果不同。
参考网址: Android OpenGL ES 开发教程(15):通用的矩阵变换指令
参考代码: NeHe lesson 4
使用逆序来指定坐标变换。
左图实现:
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) 变换
参考代码:
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);
参考网址:
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
参考网址: Android OpenGL ES 开发教程(17):投影变换Projection
参考代码: NeHe lesson 1
...
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;
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);
参考文档:
参考文档:
参考文档
参考文档
开发环境搭建参考:
全用例参考: 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";
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 );
...
}
参考网址:
glDrawArrays (int mode, int first, int count)
参数mode:
参考网址:
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 同时也控制和提供了对设备显示和可能的设备渲染配置的访问。
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将不起作用。
用例介绍: OpenGL OBJ Model Loader
代码: Download
其他参考网址: