java openglcanvas_OpenGL绘制一张图片的流程--以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的部分,所以要对编译链接部分进行缓存以保证性能。

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 流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值