android opengles绘制三角形(四边形)
一、概述
案例:编写一个opengles程序绘制一个三角形、四边形。其中opengles上下文环境及窗口管理由我们自己来维护。即通过SurfaceView+EGL+OpenGL ES来实现绘制一个三角形(四边形)
制作步骤:
1.创建Activity并为这个Activity创建一个布局,在布局中写一个SurfaceView用于显示绘制好的三角形
2.在Activity中实例化SurfaceView并通过SurfaceView获取SurfaceHolder,通过获取到的SurfaceHolder设置其CallBack
3.新建一个Java和C++通讯的类(DrawTriangle),并定义一个交互方法createGraphical(width,height,surface)
4.在jni层中创建一个triangle.cpp用于实现DrawTriangle中的方法,通过调用ANativeWindow_fromSurface(env,surface)来包装出一个ANativeWindow(这个类会辅助生成EGLSurface)
5.创建实现的渲染类(C++)triangle_render.cpp。通过其初始化方法init(width,height,_window)创建并初始化EGLCore,通过EGLCore创建EGLSurface、EGLSurface创建成功后通过glMakeCurrent方法给当前显示设备绑定EGLSurface及上下文环境Context
7.通过glViewPort指定窗口大小通过glClear来清除颜色缓冲和深度缓冲
8.创建通过glCreateShader创建顶点着色器和片元着色器
9.通过glCreateProgram创建显卡可执行程序
10.给显卡可执行程序的顶点填充数据
11.调用glDrawArrays方法绘制三角形
12.通过glSwapBuffers将framebuffer给front frame buffer并显示出来。
下面把两个shader和顶点坐标贴出来
1.顶点着色器代码:
/** *三角形顶点着色器 */ static const char *vertexShaderSource = "attribute vec4 vPosition;" "void main(){" "gl_Position = vPosition;" "}";
2.片元着色器代码:
/** * 三角形片元着色器 */ static const char *fragmentShaderSource = "precision mediump float;" "uniform vec4 vColor;" "void main(){" "gl_FragColor = vColor;" "}";
3.顶点坐标
//三维顶点坐标 static const float triangleCoords[] = { 0.25, -0.25, 0.0, // bottom right -0.25, -0.25, 0.0, // bottom left 0, 0.25, 0.0, // top };
二、代码实例
1.DrawTriangleActivity.java,测试类
/** * @ProjectName: YwMediaPlayer * @Package: com.yw.ywmediaplayer.activity.ui * @ClassName: DrawTriangleActivity * @Description: 使用C/C++绘制原生的三角形 * @Author: wei.yang * @CreateDate: 2021/11/16 10:58 * @UpdateUser: 更新者:wei.yang * @UpdateDate: 2021/11/16 10:58 * @UpdateRemark: 更新说明: * @Version: 1.0 * * 三角形绘制步骤总结: * 1.新建一个Activity并在布局中初始化SurfaceView * 2.初始化SurfaceView并获取SurfaceHolder * 3.给SurfaceView添加Callback——>onSurfaceCreated 、onSurfaceChanged、onSurfaceDestroy * 4.在onSurfaceCreated中初始化Java调用C/C++的native类 * 5.调用DrawTriangle.java就相当于调用了triangle.cpp * 6.根据传入init(width,height,surface)方法传递的参数创建ANativeWindow * 7.实例化Triangle并调用init(width,height,surface)方法对EGLCore进行初始化,初始化的过程中会创建EGL的上下文环境(EGLContext)和显示设备(EGLDisplay) * 8.通过调用createWindowSurface创建一个EGLSurface用于将画面呈现到屏幕上 * 9.通过eglCore->makeCurrent给当前线程绑定上下文环境 * 10.创建opengle程序通过glCreateProgram ..... glCreateShader ....glUseProgram * 11.从从glsl 的gpu程序中取出变量句柄,并给其赋值 * 12.开始绘制三角形可调用glDrawArrays或者glDrawElements * 13.调用eglSwapBuffers交换front frame buffer和back frame buffer,使真正的结果可以正确的显示出来 * * * * * * */ public class DrawTriangleActivity extends BaseActivity { private SurfaceView triangleSurfaceView; private SurfaceHolder holder = null; private DrawTriangle drawTriangle; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_draw_triangle); triangleSurfaceView = findViewById(R.id.triangleSurfaceView); holder = triangleSurfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { /** * 当surface创建的时候调用 * @param holder */ @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { drawTriangle = new DrawTriangle(); } /** * 当surface改变的时候调用 * @param holder * @param format * @param width * @param height */ @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { drawTriangle.createGraphical(width,height,holder.getSurface()); } /** * 当surface销毁的时候调用 * @param holder */ @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { } }); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); triangleSurfaceView.getLayoutParams().width = getWindowManager().getDefaultDisplay().getWidth(); triangleSurfaceView.getLayoutParams().height = getWindowManager().getDefaultDisplay().getHeight(); } @Override public void videoPathCallback(String videoPath) { } }
2.DrawTriangle.java,java和jni交互的类
public class DrawTriangle { static { System.loadLibrary("native-lib"); } /** * 初始化 */ public native void createGraphical(int width,int height,Surface surface); }
3.triangle.cpp,与DrawTriangle.java对应的类
static ANativeWindow *window = 0; Triangle *triangle; /** * 初始化 */ extern "C" JNIEXPORT void JNICALL Java_com_yw_ywmediaplayer_activity_nativeinterface_DrawTriangle_createGraphical(JNIEnv *env, jobject thiz, jint width, jint height, jobject surface) { window = ANativeWindow_fromSurface(env, surface); triangle = new Triangle(); triangle->init(width, height, window); //渲染 triangle->render(); }
4.triangle_render.cpp,最终会执行的渲染类
#include "triangle_render.h" Triangle::Triangle() { } Triangle::~Triangle() { } void Triangle::init(int width, int height, ANativeWindow *_window) { this->width = width; this->height = height; this->_window = _window; LOGI("EglCore init start"); //初始化eglCore eglCore = new EGLCore(); eglCore->init(); LOGI("EglCore init end"); LOGI("EGLSurface create start"); //创建EGLSurface eglSurface = eglCore->createWindowSurface(_window); LOGI("EGLSurface create end"); eglCore->makeCurrent(eglSurface); } void Triangle::render() { LOGI("glViewport:width= %d height=%d",width,height); glViewport(0, 0, width, height); glClearColor(0.0f, 0.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); createProgram(); } void Triangle::destroy() { //销毁EGLCore以及EGLSurface if (eglCore) { eglCore->releaseSurface(eglSurface); eglCore->release(); eglCore = NULL; } } /** * 加载shader * @param shaderType shader类型 * @return shader */ int Triangle::loadShader(GLenum shaderType,const char* pSource) { GLint status; //创建shader GLuint shader = glCreateShader(shaderType); glShaderSource(shader, 1, &pSource, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (!status) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); LOGI("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } } else { LOGI( "Guessing at GL_INFO_LOG_LENGTH size\n"); char* buf = (char*) malloc(0x1000); if (buf) { glGetShaderInfoLog(shader, 0x1000, NULL, buf); LOGI("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } } glDeleteShader(shader); shader = 0; } return shader; } /** * 创建显卡可执行程序 */ void Triangle::createProgram() { LOGI("glCreateProgram"); //创建显卡可执行程序 GLuint programId = glCreateProgram(); LOGI("glAttachShader start"); //绑定shader GLuint vertexShader = loadShader(GL_VERTEX_SHADER,vertexShaderSource); glAttachShader(programId, vertexShader); GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER,fragmentShaderSource); glAttachShader(programId,fragmentShader); LOGI("glAttachShader end"); LOGI("glLinkProgram start"); //链接程序 glLinkProgram(programId); LOGI("glUseProgram start"); GLint status; glGetProgramiv(programId, GL_LINK_STATUS, &status); checkGlError("glGetProgramiv"); if (status == GL_FALSE) { GLint bufLength = 0; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(programId, bufLength, NULL, buf); LOGI("Could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(programId); programId = 0; return; } //使用programId glUseProgram(programId); //填充数据 //获取顶点着色器vPosition成员句柄 GLint mPositionHandle = glGetAttribLocation(programId, "vPosition"); //启用三角形顶点句柄 glEnableVertexAttribArray(mPositionHandle); //准备三角形坐标数据 glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 12, triangleCoords); GLint mColorHandle = glGetUniformLocation(programId, "vColor"); //设置绘制三角形的颜色 glUniform4fv(mColorHandle, 1, colors); LOGI("glDrawElements start"); //绘制三角形,使用glDrawElements或者glDrawArrays都可以绘制 // glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_SHORT, indices); glDrawArrays(GL_TRIANGLE_STRIP,0,3); LOGI("glDrawElements end"); eglCore->swapBuffers(eglSurface); } bool Triangle::checkGlError(const char* op) { for (GLint error = glGetError(); error; error = glGetError()) { LOGE("after %s() glError (0x%x)\n", op, error); } return true; }
三、运行效果
ps:如果要绘制多边形可以把glDrawArrays方法注释掉,并解开glDrawElemetns方法注释即可。