一、概述
案例:編寫一個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方法注釋即可。