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方法注釋即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM