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