OpenGL绘制一张图片的流程--以android-openGL-canvas为例
来源:互联网 发布:手机淘宝如何分享宝贝 编辑:程序博客网 时间:2024/06/05 02:43
样例代码来源: android-openGL-canvas
OpenGL文档参考:
OpenGL文档
本文只对流程做排序以及一些简单的说明,想了解原理的请查看OpenGL相关文档,有对相应函数有疑问的可查阅上述文档。
一、创建 eglContext。EGLContext 是一个比较重的对象,所以一般只创建一次。
例子代码代码主要在 EglHelper
- egl = EGLContext.getEGL()
- 获取 eglDisplay = eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
- 获取 EGLConfig eglChooseConfig(..)
- eglInitialize(mEgl, mEglDisplay, mEglConfig, eglContext)
创建 eglContext = eglCreateContext(display, config, eglContext, attrib_list))
大致的相关代码
public EGLContext start(EGLContext eglContext) { if (GLThread.LOG_EGL) { Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); } /* * Get an EGL instance */ mEgl = (EGL10) EGLContext.getEGL(); /* * Get to the default display. */ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { throw new RuntimeException("eglGetDisplay failed"); } /* * We can now initialize EGL for that display */ int[] version = new int[2]; if (!mEgl.eglInitialize(mEglDisplay, version)) { throw new RuntimeException("eglInitialize failed"); } mEglConfig = eglConfigChooser.chooseConfig(mEgl, mEglDisplay); /* * Create an EGL context. We want to do this as rarely as we can, because an * EGL context is a somewhat heavy object. */ mEglContext = eglContextFactory.createContext(mEgl, mEglDisplay, mEglConfig, eglContext); if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { mEglContext = null; throwEglException("createContext"); } if (GLThread.LOG_EGL) { Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); } mEglSurface = null; return mEglContext; }
二、创建 surface
eglCreateWindowSurface(display, config, nativeWindow, surfaceAttribs)
将 context 和 surface 联系起来。
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)如果需要更换surface,就先destroy现在的
eglDestroySurface大致的相关代码
public boolean createSurface(Object surface) { if (GLThread.LOG_EGL) { Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); } /* * Check preconditions. */ if (mEgl == null) { throw new RuntimeException("egl not initialized"); } if (mEglDisplay == null) { throw new RuntimeException("eglDisplay not initialized"); } if (mEglConfig == null) { throw new RuntimeException("mEglConfig not initialized"); } /* * The window size has changed, so we need to create a new * surface. */ destroySurfaceImp(); /* * Create an EGL surface we can render into. */ mEglSurface = eglWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, surface); if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { int error = mEgl.eglGetError(); if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); } return false; } /* * Before we can issue GL commands, we need to make sure * the context is current and bound to a surface. */ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { /* * Could not make the context current, probably because the underlying * SurfaceView surface has been destroyed. */ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); return false; } return true; }
三、接下来就是使用vertex数据,texture数据,vertex shader, texture shader进行绘制了,这一部分是可以随时替换数据绘制新的东西的,其中比较耗性能的部分是编译链接shader的部分,所以要对编译链接部分进行缓存以保证性能。
代码主要在GLES20Canvas
vertex 数据
glGenBuffers(…)
glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId)
glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, GLES20.GL_STATIC_DRAW)
- 大致的相关代码
private int uploadBuffer(Buffer buffer, int elementSize) { mGLId.glGenBuffers(1, mTempIntArray, 0); checkError(); int bufferId = mTempIntArray[0]; GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); checkError(); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, GLES20.GL_STATIC_DRAW); checkError(); return bufferId; }
编译,链接shader等
- 以两个 String 对象作为参数,分别是 vertex shader 和 fragment shader
- 编译
glCreateShader
glShaderSource
glCompileShader
private static int loadShader(int type, String shaderCode) { // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); checkError(); GLES20.glCompileShader(shader); checkError(); return shader; }
- 链接
glCreateProgram
glAttachShader vertex
glAttachShader fragment
glLinkProgram
glGetProgramiv
private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { int program = GLES20.glCreateProgram(); checkError(); if (program == 0) { throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); } GLES20.glAttachShader(program, vertexShader); checkError(); GLES20.glAttachShader(program, fragmentShader); checkError(); GLES20.glLinkProgram(program); checkError(); int[] mLinkStatus = mTempIntArray; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); if (mLinkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } loadHandles(params, program); return program; }
设定绘制区域
- glViewport(width, height)
此时就可以利用 width 和 height 来对 model view projection 矩阵进行一些初始设置了
@Override public void setSize(int width, int height) { mWidth = width; mHeight = height; GLES20.glViewport(0, 0, mWidth, mHeight); checkError(); Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); if (getTargetTexture() == null) { mScreenWidth = width; mScreenHeight = height; Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); } }
对model-view矩阵进行处理(也可以不处理,看需求)
可以是平移,旋转,放缩之类的。
传入 texture, 一个带有宽高和图像数据的对象。
- glUseProgram(programId) 使用之前编译链接好的的id
- 将texture数据传入到OpenGL里的gl操作
glActiveTexture(GLES20.GL_TEXTURE0)
private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { GLES20.glUseProgram(program); checkError(); enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); checkError(); texture.onBind(this); GLES20.glBindTexture(texture.getTarget(), texture.getId()); checkError(); GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); checkError(); GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha()); checkError(); }
glGenTextures
glBindTexture
glTexParameteri 设定相关的texture params
@Override public void setTextureParameters(BasicTexture texture) { int target = texture.getTarget(); GLES20.glBindTexture(target, texture.getId()); checkError(); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); }
- 如果bitmap的宽高是2的倍数
glBindTexture
glTexImage2D
@Override public void initializeTexture(BasicTexture texture, Bitmap bitmap) { int target = texture.getTarget(); GLES20.glBindTexture(target, texture.getId()); checkError(); GLUtils.texImage2D(target, 0, bitmap, 0); }
- 如果不是2的倍数
glBindTexture
GLUtils.texSubImage2D
@Override public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, int format, int type) { int target = texture.getTarget(); GLES20.glBindTexture(target, texture.getId()); checkError(); GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); }
glUniform1i(...) // 对shader里面的textureSampler传值,因为ActiveTexture的是Texture0,所以传0就可以了。
GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
将vertex的值传入OpenGL
glBindBuffer
glVertexAttribPointer
glBindBuffer 0 清理
private void setPosition(ShaderParameter[] params, int offset) { GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); checkError(); GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); checkError(); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); checkError(); }
texture Matrix
- 利用texture的宽高设定 textureMatrix。 textureMatrix 是shader 里的一个uniform对象,用来映射从texture对应位置获取的颜色到绘制区域。例如在不进行平移旋转等情况下,第一个像素点就绘制在第一个位置。
TextureMatrixTransformer.copyTextureCoordinates(...); TextureMatrixTransformer.convertCoordinate(...); TextureMatrixTransformer.setTextureMatrix(...);
model view projection matrix 将这个matrix传入到OpenGL里
translateM
scaleM
multiplyMM
glUniformMatrix4fv(…)
private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); Matrix.scaleM(mTempMatrix, 0, width, height, 1f); Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); checkError(); }
最后就是绘制了
glEnableVertexAttribArray positionHandle
glDrawArrays
glDisableVertexAttribArray positionHandle
private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, float height) { setMatrix(params, x, y, width, height); int positionHandle = params[INDEX_POSITION].handle; GLES20.glEnableVertexAttribArray(positionHandle); checkError(); GLES20.glDrawArrays(type, 0, count); checkError(); GLES20.glDisableVertexAttribArray(positionHandle); checkError(); }
四、总结
以上,说明了OpenGL绘制的三个流程。由于OpenGL是独立于语言的,这些流程不仅适用于Android,使用其它语言编写也可借鉴。从上面冗长的流程可以看到,每次要进行绘制都写那么多代码实在不经济,那么接下来的文章就会讨论如何封装以上的流程了。其实开头的github仓库里面的代码已经有封装好的类了,有需要的话可以先去看源代码。感谢阅读!
- OpenGL绘制一张图片的流程--以android-openGL-canvas为例
- 如何封装 opengl 流程 – 以为android-opengl-canvas例
- 3D 图形学 以 OpenGL 为例的学习
- Android OpenGL绘制球体
- OpenGL:将绘制场景保存为bmp图片
- OpenGL的数组绘制
- Opengl -- 五角星的绘制
- Opengl -- 五角星的绘制
- opengl点的绘制
- opengl圆环的绘制
- [OpenGL]用OpenGL图形的绘制--矩形
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.3 用OpenGL ES 2.0显示一张图片(上)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.4 用OpenGL ES 2.0显示一张图片(下)
- android opengl 绘制立方体【转】
- Android+NDK+OpenGL绘制球形
- openGL ES Android 三角形绘制
- openGL ES Android 绘制点
- 【OpenGL】GLSurfaceView绘制bitmap图片及glViewport调整的效果
- 【最短路】CODE[VS] 1557 热浪 (Dijkstra模板)
- 《软件开发者路线图》读书随笔
- 关于素数的问题
- C专家编程 九 什么时候数组与指针相同(一)
- SQL 结果集合操作
- OpenGL绘制一张图片的流程--以android-openGL-canvas为例
- Linux的特殊权限(suid、sgid、sticky)
- 并查集
- Ubuntu14.04下 ROS indigo使用kinect 亲测好用
- 1638: [Usaco2007 Mar]Cow Traffic 奶牛交通
- 尝尝nodeJs 的味道
- 用pip安装pymongo模块报错:Could not find a version that satisfies the requirement pymongo(from version:)
- 零散的东西
- 参数化登陆防止SQL注入攻击