最近在鼓捣OpenGL渲染,这东西确实给显示带来很大便利,网上资料也比较多,不过因为这些接口都高度封装,出问题可不太好查。我在网上看了一些资料,有很多渲染图的例子,不过貌似渲染图并刷新的比较少见,所以把我的一些经验分享一下,毕竟像我一样临时现学现搞的菜鸟应该还是比较多的。期间遇到一些坑,还好有大佬的帮忙。。。
1. 着色器脚本
这算是opengl的基本概念,就不做详细解释了
char *vertex_shader_source = "attribute vec2 vertexCoord;" "attribute vec2 textureCoord;" "varying vec2 sampleCoord;" "void main()" "{" " sampleCoord = textureCoord;" " gl_Position = vec4(vertexCoord, 0.0, 1.0);" "}"; char *fragment_shader_source = "precision mediump float;" "varying vec2 sampleCoord;" "uniform sampler2D sampleTexture;" "void main()" "{" " gl_FragColor = texture2D(sampleTexture, sampleCoord);" "}";
2.构建着色器程序
这个也是基本 *** 作,八股文一样,也不再细说了
static void check_gl_error(char *gl_operation) { for (GLint error = glGetError(); error; error = glGetError()) { g_info("[GL] Operation %s() glError (0x%x)", gl_operation, error); } } static GLuint load_shader(GLenum shader_type, char *shader_source) { GLuint shader = glCreateShader(shader_type); if (shader) { glShaderSource(shader, 1, &shader_source, NULL); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*)malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); g_info("[GL] Could not compile shader %d:%s", shader_type, buf); free(buf); } glDeleteShader(shader); shader = 0; } } } return shader; } static GLuint gl_render_create_program(GlRender *render, char *vertex_shader_source, char *fragment_shader_source) { g_return_val_if_fail(IS_GL_RENDER(render), 0); GlRenderPrivate *p = render->priv; p->vertex_shader = load_shader(GL_VERTEX_SHADER, vertex_shader_source); if (!p->vertex_shader) { return 0; } p->fragment_shader = load_shader(GL_FRAGMENT_SHADER, fragment_shader_source); if (!p->fragment_shader) { return 0; } GLuint program = glCreateProgram(); if (program) { glAttachShader(program, p->vertex_shader); check_gl_error("glAttachShader"); glAttachShader(program, p->fragment_shader); check_gl_error("glAttachShader"); gllinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_link_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*)malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, NULL, buf); g_info("[GL] Could not link program:%s", buf); free(buf); } } glDeleteProgram(program); program = 0; } } return program; }
3.坐标系初始化
定点坐标及纹理坐标初始化后可以直接使用了,不需要再对每一幅图作坐标映射,因为图像是局部刷新,每次显示还是全图
static void gl_render_get_handles(GlRender *render) { g_return_if_fail(IS_GL_RENDER(render)); GlRenderPrivate *p = render->priv; glUseProgram(p->program); p->vertex_coord_handle = glGetAttribLocation(p->program, "vertexCoord"); p->texture_coord_handle = glGetAttribLocation(p->program, "textureCoord"); p->sample_texture_handle = glGetUniformLocation(p->program, "sampleTexture"); g_info("[GL] vertex_coord_handle = %d", p->vertex_coord_handle); g_info("[GL] texture_coord_handle = %d", p->texture_coord_handle); g_info("[GL] sample_texture_handle = %d", p->sample_texture_handle); glEnableVertexAttribArray(p->vertex_coord_handle); glEnableVertexAttribArray(p->texture_coord_handle); } void gl_render_open(GlRender *render) { g_return_if_fail(IS_GL_RENDER(render)); GlRenderPrivate *p = render->priv; p->texture_id = 0; p->texture_width = 0; p->texture_height = 0; p->surface_width = 0; p->surface_height = 0; p->vertex_coord[0] = -1.0f; p->vertex_coord[1] = -1.0f; p->vertex_coord[2] = 1.0f; p->vertex_coord[3] = -1.0f; p->vertex_coord[4] = -1.0f; p->vertex_coord[5] = 1.0f; p->vertex_coord[6] = 1.0f; p->vertex_coord[7] = 1.0f; p->texture_coord[0] = 0.0f; p->texture_coord[1] = 1.0f; p->texture_coord[2] = 1.0f; p->texture_coord[3] = 1.0f; p->texture_coord[4] = 0.0f; p->texture_coord[5] = 0.0f; p->texture_coord[6] = 1.0f; p->texture_coord[7] = 0.0f; p->program = gl_render_create_program(render, vertex_shader_source, fragment_shader_source); if (!p->program) { g_info("[GL] init render failed"); } gl_render_get_handles(render); }
4.加载纹理到GPU
考虑切换分辨率的情况,当view的尺寸或纹理的尺寸出现变化时,通过设置glTexImage2D最后一个参数为NULL分配一个全图的缓存,尺寸不变的时候通过glTexSubImage2D往缓冲区写入局部的图像数据,达到局部刷新的效果
GLuint gl_render_load_texture(GlRender *render, GlContext *context, void* data, int x, int y, int width, int height, int max_width, int max_height) { g_return_val_if_fail(IS_GL_RENDER(render), 0); GlRenderPrivate *p = render->priv; int surface_width = 0; int surface_height = 0; gl_context_get_width_height(context, &surface_width, &surface_height); if (p->texture_width != max_width || p->texture_height != max_height || p->surface_width != surface_width || p->surface_height != surface_height) { p->texture_width = max_width; p->texture_height = max_height; p->surface_width = surface_width; p->surface_height = surface_height; glViewport(0, 0, surface_width, surface_height); g_info("[GL] set opengl view width %d, height %d, texture width %d, texture height %d", surface_width, surface_height, max_width, max_height); if (p->texture_id) { glDeleteTextures(1, &p->texture_id); p->texture_id = 0; } glGenTextures(1, &p->texture_id); check_gl_error("glGenTextures"); glBindTexture(GL_TEXTURE_2D, p->texture_id); check_gl_error("glBindTexture"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_IMG, max_width, max_height, 0, GL_BGRA_IMG, GL_UNSIGNED_BYTE, NULL); check_gl_error("glTexImage2D"); } glBindTexture(GL_TEXTURE_2D, p->texture_id); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_BGRA_IMG, GL_UNSIGNED_BYTE, data); check_gl_error("glTexSubImage2D"); glBindTexture(GL_TEXTURE_2D, 0); return p->texture_id; }
5.绘制
void gl_render_draw(GlRender *render, GlContext *context) { g_return_if_fail(IS_GL_RENDER(render)); GlRenderPrivate *p = render->priv; glUseProgram(p->program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, p->texture_id); glUniform1i(p->sample_texture_handle, 0); glVertexAttribPointer(p->vertex_coord_handle, 2, GL_FLOAT, GL_FALSE, 0, p->vertex_coord); glVertexAttribPointer(p->texture_coord_handle, 2, GL_FLOAT, GL_FALSE, 0, p->texture_coord); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); check_gl_error("glDrawArrays"); gl_context_swap_buffers(context); }
6.用完后记得释放资源
if (p->texture_id) { glDeleteTextures(1, &p->texture_id); } if (p->fragment_shader) { glDeleteShader(p->fragment_shader); glDetachShader(p->program, p->fragment_shader); } if (p->vertex_shader) { glDeleteShader(p->vertex_shader); glDetachShader(p->program, p->vertex_shader); } if (p->program) { glDeleteProgram(p->program); }
7.小结
用法:先调用gl_render_load_texture加载纹理(第一次调用最好传一幅全图),然后调用gl_render_draw绘制,不断循环这个过程。关键点是顶点坐标及纹理坐标,以及图像纹理的加载方式。本人用的是gobiect,跟c++的class是类似的东西,这里的GlContext其实是另一个抽象类,里面有egl的实现,主要是创建surface还有egl的swapbuffers,这个实现网上很多,这里就不再细述了
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)