(二)你好,三角形
上一篇:(一)OpenGL ES 3.0简介:https://www.jianshu.com/p/d5956b60f6ef
下一篇:(三)EGL简介:https://www.jianshu.com/p/bb6a13c6066f
代码框架
我们将构建一个实用的Open GL ES 3.0工具函数库:
1、它应该简单、短小、容易理解;
2、框架应该可移植。
示例下载位置
示例代码位于http://opengles-book.com/。可以在Windows、Linux、Android、iOS上运行。
Android上可以支持原生开发波NDK(C++)和软件开发包SDK(Java)编写。我们主要关注Android NDK(C++)的实现,因为:
1、代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
2、在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C++代码编写的。
3、 便于移植,用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
“Hello Triangle” 示例
注意,Android Studio环境下的具体配置请参考简书文章https://www.jianshu.com/p/3792486deca1
#include "esUtil.h"
typedef struct {
// 程序对象句柄
GLuint programObject;
} UserData;
// 创建着色器对象,加载着色器源码,然后编译着色器
GLuint LoadShader ( GLenum type, const char *shaderSrc ) {
GLuint shader;
GLint compiled;
// 创建着色器对象
shader = glCreateShader ( type );
if ( shader == 0 ){
return 0;
}
// 加载着色器源码
glShaderSource ( shader, 1, &shaderSrc, NULL );
// 编译着色器
glCompileShader ( shader );
// 检查编译状态
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 ) {
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
esLogMessage ( "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
}
// 初始化着色器和程序对象
int Init ( ESContext *esContext ) {
UserData *userData = esContext->userData;
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 vPosition; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"out vec4 fragColor; \n"
"void main() \n"
"{ \n"
" fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
"} \n";
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
// 加载顶点/片段着色器
vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );
// 创建程序对象
programObject = glCreateProgram ( );
if ( programObject == 0 ) {
return 0;
}
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
// 链接程序
glLinkProgram ( programObject );
// 检查链接状态
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked ) {
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 ) {
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
esLogMessage ( "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return FALSE;
}
// 存储程序对象
userData->programObject = programObject;
glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
return TRUE;
}
// 使用着色器绘制三角形
void Draw ( ESContext *esContext ) {
UserData *userData = esContext->userData;
GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// 设置视口
glViewport ( 0, 0, esContext->width, esContext->height );
// 清空颜色缓冲区
glClear ( GL_COLOR_BUFFER_BIT );
// 使用程序对象
glUseProgram ( userData->programObject );
// 加载顶点数据
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
glDrawArrays ( GL_TRIANGLES, 0, 3 );
}
void Shutdown ( ESContext *esContext ) {
UserData *userData = esContext->userData;
glDeleteProgram ( userData->programObject );
}
int esMain ( ESContext *esContext ) {
esContext->userData = malloc ( sizeof ( UserData ) );
esCreateWindow ( esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB );
if ( !Init ( esContext ) ) {
return GL_FALSE;
}
esRegisterShutdownFunc ( esContext, Shutdown );
esRegisterDrawFunc ( esContext, Draw );
return GL_TRUE;
}
下面我们会逐片段分析上述代码。
使用OpenGL ES 3.0框架
esMain是程序的主入口。
ESContext结构体内有一个名为userData的成员变量,保存应用程序所有的数据。ESContext结构体还包含窗口宽度和高度、EGL上下文和回调函数指针等信息。
esContext->userData = malloc ( sizeof( UserData ) );
// 创建窗口和渲染表面
esCreateWindow( esContext, "Hello Triangle", 320, 240,
ES_WINDOW_RGB );
// 初始化运行程序
if ( !Init( esContext ) )
return GL_FALSE;
// 回调Draw以渲染帧
esRegisterDrawFunc(esContext, Draw);
创建简单的顶点和片段着色器
// 顶点着色器
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 vPosition; \n"
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n"
"} \n";
// 片段着色器
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"out vec4 fragColor; \n"
"void main() \n"
"{ \n"
" fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
"} \n";
编译和加载着色器
LoadShader()函数负责创建着色器对象,加载着色器源码,然后编译着色器:
GLuint LoadShader ( GLenum type, const char *shaderSrc ) {
GLuint shader;
GLint compiled;
// 创建着色器对象
shader = glCreateShader ( type );
if ( shader == 0 ){
return 0;
}
// 加载着色器源码
glShaderSource ( shader, 1, &shaderSrc, NULL );
// 编译着色器
glCompileShader ( shader );
// 检查编译状态
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 ) {
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
esLogMessage ( "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
}
创建一个程序对象并链接着色器
将不同的着色器编译成的着色器对象连接到一个程序对象并连接起来,用以绘制图形:
// 加载顶点/片段着色器
vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );
// 创建程序对象
programObject = glCreateProgram ( );
if ( programObject == 0 ) {
return 0;
}
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
// 链接程序
glLinkProgram ( programObject );
// 检查链接状态
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked ) {
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 ) {
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
esLogMessage ( "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return FALSE;
}
// 存储程序对象
userData->programObject = programObject;
设置视口和清楚颜色缓冲区
Draw回调函数用于绘制帧,其内部先设置视口glViewport,再清楚屏幕glClear:
// 设置视口
glViewport ( 0, 0, esContext->width, esContext->height );
// 清空屏幕
glClear ( GL_COLOR_BUFFER_BIT );
加载几何形状和绘制图元
指定图元,加载图元数据并且绘制:
// 三角形的三个顶点坐标
GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// 加载顶点数据
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
glDrawArrays ( GL_TRIANGLES, 0, 3 );
显示后台缓冲区
双缓冲区(Double Buffering):
所有的渲染都发生再后台缓冲区,它位于不可见的屏幕的内存区域。当渲染完成后,这个的缓冲区被“交换”到前台缓冲区(可见缓冲区)。然后前台缓冲区编程下一帧的后台缓冲区。
eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
小结
OpenGL ES 3.0几个关键组件:
1、用EGL创建屏幕渲染表面
2、使用着色器和相关对象
3、设置视口
4、清除颜色缓冲区
5、渲染图元
上一篇:(一)OpenGL ES 3.0简介:https://www.jianshu.com/p/d5956b60f6ef
下一篇:(三)EGL简介:https://www.jianshu.com/p/bb6a13c6066f