OpenGLES-triangle
总体流程
- 创建EGL 渲染表面
- 创建着色器和链接程序
- 输入顶点信息
- 开始渲染
- 开始绘制
源码解析
Android层操作
static void HandleCommand(struct android_app *pApp, int32_t cmd)
{
MYESContext *myesContext = (MYESContext *)pApp->userData;
switch (cmd) {
case APP_CMD_SAVE_STATE:
break;
case APP_CMD_INIT_WINDOW:
myesContext->eglNativeDisplayType = EGL_DEFAULT_DISPLAY;
myesContext->eglNativeWindowType = pApp->window;
// 1. 进行init操作
if (myesMain(myesContext) != GL_TRUE) {
exit(0);
}
break;
case APP_CMD_TERM_WINDOW:
// 3. shutdown操作
if (myesContext->shutdownFunc != NULL) {
myesContext->shutdownFunc(myesContext);
}
if (myesContext->userData != NULL) {
free(myesContext->userData);
}
memset(myesContext, 0, sizeof(MYESContext));
break;
case APP_CMD_LOST_FOCUS:
break;
case APP_CMD_GAINED_FOCUS:
break;
}
}
void android_main(struct android_app *pApp)
{
MYESContext myesContext;
float lastTime;
app_dummy();
memset(&myesContext, 0, sizeof(myesContext));
myesContext.platformData = (void *)pApp->activity->assetManager;
pApp->onAppCmd = HandleCommand;
pApp->userData = &myesContext;
lastTime = GetCurrentTime();
while (1) {
int ident;
int events;
struct android_poll_source *pSource;
while ((ident = ALooper_pollAll(0, NULL, &events, (void **)&pSource)) >= 0) {
if (pSource != NULL) {
pSource->process(pApp, pSource);
}
if (pApp->destroyRequested != 0) {
return;
}
}
if (myesContext.eglNativeWindowType == NULL) {
continue;
}
if (myesContext.updateFunc != NULL) {
float curTime = GetCurrentTime();
float deltaTime = (curTime - lastTime);
lastTime = curTime;
myesContext.updateFunc(&myesContext, deltaTime);
}
if (myesContext.drawFunc != NULL) {
// 2. 画出三角形
myesContext.drawFunc(&myesContext);
eglSwapBuffers(myesContext.eglDisplay, myesContext.eglSurface);
}
}
}
1. 进行init操作
int myesMain(MYESContext *myesContext)
{
// myUserData为GLuint programObject;
myesContext->userData = malloc(sizeof(myUserData));
// 1. 使用EGL创建一个屏幕上的渲染表面,surface
myesCreateWindow(myesContext, "Hello Triangle", 320, 240, MY_ES_WINDOW_RGB);
// 2. 创建顶点和片段着色器,并编译和加载着色器,创建程序对象并链接着色器
if (!Init(myesContext)) {
return GL_FALSE;
}
// 3. 和shutdown和draw函数绑定在一起
esRegisterShutdownFunc(myesContext, Shutdown);
esRegisterDrawFunc(myesContext, Draw);
return GL_TRUE;
}
1. 创建渲染表面
GLboolean myesCreateWindow(MYESContext *myesContext, const char* title, GLint width, GLint height, GLuint flags)
{
EGLConfig config;
EGLint majorVersion;
EGLint minorVersion;
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
if (myesContext == NULL) {
return GL_FALSE;
}
myesContext->width = ANativeWindow_getWidth(myesContext->eglNativeWindowType);
myesContext->height = ANativeWindow_getHeight(myesContext->eglNativeWindowType);
// EGLDisplay EGLAPIENTRY eglGetDisplay (EGLNativeDisplayType display_id)
// display_id:指定显示连接,默认为EGL_DEFAULT_DISPLAY,默认原生显示的连接
// 1. 打开与EGL显示服务器的连接
myesContext->eglDisplay = eglGetDisplay(myesContext->eglNativeDisplayType);
if (myesContext->eglDisplay == EGL_NO_DISPLAY) {
return GL_FALSE;
}
// EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor)
// dpy:EGL显示连接,major:EGL实现返回的主版本号,minor:次版本号
// 2. 初始化EGL
if (!eglInitialize(myesContext->eglDisplay, &majorVersion, &minorVersion)) {
return GL_FALSE;
}
{
EGLint numConfigs = 0;
// 配置为RGB565形式:红色为5 bits
EGLint attribList[] = {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, (flags & MY_ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE,
EGL_DEPTH_SIZE, (flags & MY_ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE,
EGL_STENCIL_SIZE, (flags & MY_ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE,
EGL_SAMPLE_BUFFERS, (flags & MY_ES_WINDOW_MULTISAMPLE) ? 1 : 0,
EGL_RENDERABLE_TYPE, GetContextRenderableType(myesContext->eglDisplay),
EGL_NONE
};
// EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
// 3. 指定一组需求,让EGL推荐最佳匹配
if (!eglChooseConfig(myesContext->eglDisplay, attribList, &config, 1, &numConfigs)) {
return GL_FALSE;
}
if (numConfigs < 1) {
return GL_FALSE;
}
}
{
EGLint format = 0;
// 4. 获取format,为WINDOW_FORMAT_RGB_565格式,为2
eglGetConfigAttrib(myesContext->eglDisplay, config, EGL_NATIVE_VISUAL_ID, &format);
// int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format);
// width和height必须同时为0或者同时不为0,设为0表示使用window的默认值,不进行指定操作
// 5. 设置原生window的format和buffer的大小
ANativeWindow_setBuffersGeometry(myesContext->eglNativeWindowType, 0, 0, format);
}
// EGLSurface eglCreateWindowSurface (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
// config为前面的配置;attrib_list为窗口属性列表
// 6. 创建EGL窗口
myesContext->eglSurface = eglCreateWindowSurface(myesContext->eglDisplay, config,
myesContext->eglNativeWindowType, NULL);
if (myesContext->eglSurface == EGL_NO_SURFACE) {
return GL_FALSE;
}
// EGLContext eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
// share_context允许多个EGLContext共享特定类型的数据;attrib_list:只接受EGL_CONTEXT_CLIENT_VERSION,为版本号
// 7. 创建渲染上下文
myesContext->eglContext = eglCreateContext(myesContext->eglDisplay, config,
EGL_NO_CONTEXT, contextAttribs);
if (myesContext->eglContext == EGL_NO_CONTEXT) {
return GL_FALSE;
}
// EGLBoolean eglMakeCurrent (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
// draw为绘图表面;read为读取表面
// 8. 指定某个EGLContext为当前上下文
if (!eglMakeCurrent(myesContext->eglDisplay, myesContext->eglSurface,
myesContext->eglSurface, myesContext->eglContext)) {
return GL_FALSE;
}
return GL_TRUE;
}
(1)EGL的作用:
- 与设备的原生窗口系统通信
- 查询绘图表面的可用类型和配置
- 创建绘图表面
- 在OpenGL ES 3.0和其他图形渲染API如OpenVG等之间同步渲染
- 管理纹理贴图等渲染资源
(2)ANativeWindow是C/C++中定义的一个结构体,等同于Java中的Surface
2. 创建着色器和链接程序
int Init(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->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;
// 1. 创建着色器
vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
// 程序对象是一个容器对象,可以将着色器与之连接,并链接一个最终的可执行程序
// 2. 创建一个程序对象
programObject = glCreateProgram();
if (programObject == 0) {
return 0;
}
// 3. 连接着色器
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
// 4. 链接程序
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 = (char *)malloc(sizeof(char) *infoLen);
glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
esLogMessage("Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(programObject);
return 0;
}
userData->programObject = programObject;
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
return 1;
}
1. 创建着色器
GLuint LoadShader(GLenum type, const char *shaderSrc)
{
GLuint shader;
GLint compiled;
// GL_VERTEX_SHADER为顶点类型的,GL_FRAGMENT_SHADER为片段类型的
// 1. 创建着色器
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
// void glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
// count为着色器字符串数量;length为多个字符串的时候,指定各个字符串长度的一个数组,当count为1时其为NULL
// 2. 提供着色器源代码
glShaderSource(shader, 1, &shaderSrc, NULL);
// 3. 编译着色器
glCompileShader(shader);
// 4. 查询信息,这里查询编译信息
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
// 查询信息日志长度
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = (char *)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;
}
2. OpenGL ES 着色器语言代码解析
(1)顶点着色器
char vShaderStr[] =
"#version 300 es \n" // 着色器版本规范
"layout(location = 0) in vec4 vPosition; \n" // layout用来指定顶点属性的索引,这里为0,表示位置;in表示顶点着色器中,每个顶点的输入;
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n" // gl_Position为顶点着色器的输出向量(内建变量)
"} \n";
0层属性,也就是位置输入(vPosition) -> gl_Position
顶点着色器输出 out vec3 v_color 颜色 -> 片段着色器输入 in vec3 v_color,就可以使用顶点着色器输出的颜色作为片段着色器的输入了
(2)片段着色器
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n" // 设置float精度为mediump
"out vec4 fragColor; \n" // out表示片段着色器的输出变量
"void main() \n"
"{ \n"
" fragColor = vec4(1.0, 0.0, 0.0, 1.0); \n" // 设置为红色
"} \n";
3. 进行draw操作
void Draw(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
// 三个顶点信息
GLfloat vVertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// 1. 通知OpenGL ES用于绘制的2D渲染表面的原点(x,y)坐标,宽度和高度
glViewport(0, 0, myesContext->width, myesContext->height);
// 2. 清除颜色缓冲区;有颜色、深度和模板缓冲区
glClear(GL_COLOR_BUFFER_BIT);
// 3. 将程序设为活动程序
glUseProgram(userData->programObject);
// void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
// index为第几个属性,属性有顶点的位置为0/纹理为1/法线为3;size为一个顶点所有数据的个数,这里XYZ为3个;
// type为顶点描述数据的类型,这里为FLOAT;normalized为是否需要显卡把数据归一化到-1到+1区间,这里不需要为FALSE
// stride连续顶点属性之间的偏移量,0表示他们为紧密排列在一起的。pointer为顶点数组
// 4. 加载顶点位置到GL中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
// 5. 使能顶点数组顶点位置属性,也就是0属性;如果什么都不使能的话,就使用常量顶点属性0
glEnableVertexAttribArray(0);
// 6. 绘制三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
}
4. 进行显示
if (myesContext.drawFunc != NULL) {
myesContext.drawFunc(&myesContext);
// 显示在屏幕上
eglSwapBuffers(myesContext.eglDisplay, myesContext.eglSurface);
}
网友评论