Android使用NDK  OpenGL ES3.0绘制一个三角形

尊重原创,转载请注明出处 https://blog.csdn.net/guyuealian/article/details/82021607

网上已经有很多OpenCV的教程,不过大都是基于Java层调用openGL接口,若使用Java层openGL接口绘制三角形,还是比较简单的,但要是使用NDK C++ 实现,还是有点复杂。

本文将使用Android NDK开发,利用C++的 OpenGL ES3.0绘制一个三角形。绘制三角形的C/C++源码大部分是参考:《 OPENGL ES 3.0编程指南 》第二章的代码,但该书只有源码,没有工程项目,是用Android.mk配置,要做成Android Studio Demo还是要花点力气的。本博客的OpenGL的开发,其配置文件使用CMakeLists.txt,Android Studio 2.3以上NDK开发很容易啦,其配置方法使用CMakeLists.txt会比使用Android.mk更容易。

这里不具体分析绘制三角形的代码实现过程了,毕竟《 OPENGL ES 3.0编程指南 》这本书已经很详细啦。

本项目源码下载地址: https://github.com/PanJinquan/openGL-Demo ,要是觉得不错,给个” Star “哈

开发环境:

  • (1)Android Studio 2.3.3 以上
  • (2)android-ndk-r10d 以上,下载地址: https://developer.android.google.cn/ndk/downloads/index.html

1、新建项目:

新建Android工程一定要勾选“Include C++ support”,这样新建的Android工程会直接支持NDK开发,避免各种配置问题,如果提示没有NDK,请下载NDK,并在工程“Project Structure”中导入即可。

我的工程目录是这样的:

2、新建RendererJNI类

package opengl.panjq.com.opengl_demo;
import android.opengl.GLSurfaceView.Renderer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.content.res.AssetManager;
import android.opengl.GLSurfaceView;
import android.util.Log;
public class RendererJNI implements GLSurfaceView.Renderer {
    static {
        System.loadLibrary("gltest-lib");
    private AssetManager mAssetMgr = null;
    private final String mLogTag = "ndk-build";
    public native void glesInit();
    public native void glesRender();
    public native void glesResize(int width, int height);
    public native void readShaderFile(AssetManager assetMgr);
    public RendererJNI(Context context) {
        mAssetMgr = context.getAssets();
        if (null == mAssetMgr) {
            Log.e(mLogTag, "getAssets() return null !");
     * 当创建 GLSurfaceView时,系统调用这个方法.使用这个方法去执行只需要发生一次的动作,
     * 例如设置OpenGL环境参数或者初始化OpenGL graphic 对象.
     * @param gl
     * @param config
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        readShaderFile(mAssetMgr);
        glesInit();
     *  当GLSurfaceView  几何学发生改变时系统调用这个方法.包括 GLSurfaceView  的大小发生改变或者横竖屏发生改变.
     *  使用这个方法去响应GLSurfaceView 容器的改变
     * @param gl
     * @param width
     * @param height
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        glesResize(width, height);
     *    执行渲染工作:当系统每一次重画 GLSurfaceView 时调用.使用这个方法去作为主要的绘制和重新绘制graphic  对象的执行点.
     * @param gl
    @Override
    public void onDrawFrame(GL10 gl) {
        glesRender();

3、修改MainActivity.java文件

package opengl.panjq.com.opengl_demo;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.app.ActivityManager;
import android.content.pm.ConfigurationInfo;
import android.util.Log;
import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
public class MainActivity extends AppCompatActivity {
    private final int CONTEXT_CLIENT_VERSION = 3;
    private GLSurfaceView mGLSurfaceView;
    RendererJNI mRenderer;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLSurfaceView = new GLSurfaceView(this);
        mRenderer=new RendererJNI(this);
        if (detectOpenGLES30()) {
            // 设置OpenGl ES的版本
            mGLSurfaceView.setEGLContextClientVersion(CONTEXT_CLIENT_VERSION);
            // 设置与当前GLSurfaceView绑定的Renderer
            mGLSurfaceView.setRenderer(mRenderer);
            // 设置渲染的模式
            mGLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
        } else {
            Log.e("opengles30", "OpenGL ES 3.0 not supported on device.  Exiting...");
            finish();
        setContentView(mGLSurfaceView);
    @Override
    protected void onResume() {
        super.onResume();
        mGLSurfaceView.onResume();
    @Override
    protected void onPause() {
        super.onPause();
        mGLSurfaceView.onPause();
    private boolean detectOpenGLES30() {
        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        ConfigurationInfo info = am.getDeviceConfigurationInfo();
        return (info.reqGlEsVersion >= 0x30000);

4.新建C++ JNI文件

RendererJNI.h头文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class opengl_panjq_com_opengl_demo_RendererJNI */
#ifndef _Included_opengl_panjq_com_opengl_demo_RendererJNI
#define _Included_opengl_panjq_com_opengl_demo_RendererJNI
#ifdef __cplusplus
extern "C" {
#endif
 * Class:     opengl_panjq_com_opengl_demo_RendererJNI
 * Method:    glesInit
 * Signature: ()V
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesInit
  (JNIEnv *, jobject);
 * Class:     opengl_panjq_com_opengl_demo_RendererJNI
 * Method:    glesRender
 * Signature: ()V
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesRender
  (JNIEnv *, jobject);
 * Class:     opengl_panjq_com_opengl_demo_RendererJNI
 * Method:    glesResize
 * Signature: (II)V
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesResize
  (JNIEnv *, jobject, jint, jint);
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_readShaderFile
        (JNIEnv *env, jobject self, jobject assetManager);
#ifdef __cplusplus
#endif
#endif

RendererJNI.cpp源文件

    这里提供一个方法char* readShaderSrcFile(char *shaderFile, AAssetManager *pAssetManager),用于获取assets资源文件*.glsl文件:

  char *pVertexShader = readShaderSrcFile("shader/vs.glsl", g_pAssetManager);
  char *pFragmentShader = readShaderSrcFile("shader/fs.glsl", g_pAssetManager);

    当然,也可以写死在文件中,如: 

    char vShaderStr[] =
            "#version 300 es                          \n"
                    "layout(location = 0) in vec4 vPosition;  \n"
                    "void main()                              \n"
                    "{                                        \n"
                    "   gl_Position = vPosition;              \n"
                    "}                                        \n";
    char fShaderStr[] =
            "#version 300 es                              \n"
                    "precision mediump float;                     \n"
                    "out vec4 fragColor;                          \n"
                    "void main()                                  \n"
                    "{                                            \n"
                    "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
                    "}                                            \n";

     再用LoadShader加载:

    vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
    fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );

    完整的代码如下 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "RendererJNI.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <GLES3/gl3.h>
#include <android/asset_manager_jni.h>
#include <android/log.h>
#define LOG_TAG "ndk-build"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
GLint	g_programObject;
jint	g_width;
jint	g_height;
AAssetManager* g_pAssetManager = NULL;
char* readShaderSrcFile(char *shaderFile, AAssetManager *pAssetManager)
    AAsset *pAsset = NULL;
    char *pBuffer = NULL;
    off_t size = -1;
    int numByte = -1;
    if (NULL == pAssetManager)
        LOGE("pAssetManager is null!");
        return NULL;
    pAsset = AAssetManager_open(pAssetManager, shaderFile, AASSET_MODE_UNKNOWN);
    //LOGI("after AAssetManager_open");
    size = AAsset_getLength(pAsset);
    LOGI("after AAssetManager_open");
    pBuffer = (char *)malloc(size+1);
    pBuffer[size] = '\0';
    numByte = AAsset_read(pAsset, pBuffer, size);
    LOGI("%s : [%s]", shaderFile, pBuffer);
    AAsset_close(pAsset);
    return pBuffer;
GLuint LoadShader ( GLenum type, const char *shaderSrc )
    GLuint shader;
    GLint compiled;
    // Create the shader object
    shader = glCreateShader ( type );
    if ( shader == 0 )
        return 0;
    // Load the shader source
    glShaderSource ( shader, 1, &shaderSrc, NULL );
    // Compile the shader
    glCompileShader ( shader );
    // Check the compile status
    glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
    if ( !compiled )
        GLint infoLen = 0;
        glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
        if ( infoLen > 1 )
            char *infoLog = (char *)malloc ( sizeof ( char ) * infoLen );
            glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
            LOGE("Error compiling shader:[%s]", infoLog );
            free ( infoLog );
        glDeleteShader ( shader );
        return 0;
    return shader;
//*********************************************************************************
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesInit
  (JNIEnv *pEnv, jobject obj){
    char vShaderStr[] =
            "#version 300 es                          \n"
                    "layout(location = 0) in vec4 vPosition;  \n"
                    "void main()                              \n"
                    "{                                        \n"
                    "   gl_Position = vPosition;              \n"
                    "}                                        \n";
    char fShaderStr[] =
            "#version 300 es                              \n"
                    "precision mediump float;                     \n"
                    "out vec4 fragColor;                          \n"
                    "void main()                                  \n"
                    "{                                            \n"
                    "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
                    "}                                            \n";
    char *pVertexShader = readShaderSrcFile("shader/vs.glsl", g_pAssetManager);
    char *pFragmentShader = readShaderSrcFile("shader/fs.glsl", g_pAssetManager);
    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint programObject;
    GLint linked;
    // Load the vertex/fragment shaders
    //vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
    //fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );
    vertexShader = LoadShader ( GL_VERTEX_SHADER, pVertexShader );
    fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, pFragmentShader );
    // Create the program object
    programObject = glCreateProgram ( );
    if ( programObject == 0 )
        return;
    glAttachShader ( programObject, vertexShader );
    glAttachShader ( programObject, fragmentShader );
    // Link the program
    glLinkProgram ( programObject );
    // Check the link status
    glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
    if ( !linked )
        GLint infoLen = 0;
        glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
        if ( infoLen > 1 )
            char *infoLog = (char *)malloc ( sizeof ( char ) * infoLen );
            glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
            LOGE("Error linking program:[%s]", infoLog );
            free ( infoLog );
        glDeleteProgram ( programObject );
        return;
    // Store the program object
    g_programObject = programObject;
    glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
 * Class:     opengl_panjq_com_opengl_demo_RendererJNI
 * Method:    glesRender
 * Signature: ()V
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesRender
  (JNIEnv *pEnv, jobject obj){
    GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f,
                             -0.5f, -0.5f, 0.0f,
                             0.5f, -0.5f, 0.0f
    // Set the viewport
    glViewport ( 0, 0, g_width, g_height );
    // Clear the color buffer
    glClear ( GL_COLOR_BUFFER_BIT );
    // Use the program object
    glUseProgram ( g_programObject );
    // Load the vertex data
    glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
    glEnableVertexAttribArray ( 0 );
    glDrawArrays ( GL_TRIANGLES, 0, 3 );
 * Class:     opengl_panjq_com_opengl_demo_RendererJNI
 * Method:    glesResize
 * Signature: (II)V
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_glesResize
  (JNIEnv *pEnv, jobject obj, jint width, jint height){
    g_width = width;
    g_height = height;
JNIEXPORT void JNICALL Java_opengl_panjq_com_opengl_1demo_RendererJNI_readShaderFile
        (JNIEnv *env, jobject self, jobject assetManager){
    if (assetManager && env)
        //LOGI("before AAssetManager_fromJava");
        g_pAssetManager = AAssetManager_fromJava(env, assetManager);
        //LOGI("after AAssetManager_fromJava");
        if (NULL == g_pAssetManager)
            LOGE("AAssetManager_fromJava() return null !");
        LOGE("assetManager is null !");

5、修改CMakeLists.txt文件

    OpenGL ES开发需要把OpenGL的相关依赖库导入进来,其方法很简单,直接在target_link_libraries中添加GLESv2 或者GLESv3都可以,由于需要在NDK使用AAssetManager操作asset资源,因此需要在target_link_libraries中,也把android添加进来,否则会出错:Error: undefined reference to 'AAssetManager_fromJava'。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
include_directories(${CMAKE_SOURCE_DIR} src/main/cpp)
add_library( # Sets the name of the library.
             gltest-lib
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s).
             src/main/cpp/RendererJNI.cpp )
add_library( # Sets the name of the library.
             native-lib
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
              log-lib
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
                       gltest-lib
                       android # 错误:Error: undefined reference to 'AAssetManager_fromJava'
                       GLESv3  # 把opengl库文件添加进来,GLESv3
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
target_link_libraries( # Specifies the target library.
                       native-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

Run,Run,Run,效果图:

My OpenGL Playground 使用 NDK 编写 OpenGL ES 3.0 正在找工作啊,你们有工作给我吗~~~~redknot@126.com 有的话联系我啊~~~~ 使用 DrawArrays 的方法,绘制三角形。并使用 Uniform 变量传递了一个 Float 变量,改变三角形大小。 使用 glDrawElements 绘制四面体,并着色 使用矩阵相乘的方法,将变换混合。 加入透视投影变换 纹理贴图,加入了 libpng 作为文件解析库。 调整环境光的亮度,让物体表面变得暗淡 调整环境光的颜色,让物体反射指定的光 Android OpenGLES 3.0 开发系统性学习教程 备注: 请使用 Android Studio 4.1+ ,NDK r21,其中一些 Case 的 3D 效果是通过手势触发(转动和缩放) OpenGL ES 3.0 开发(08):坐标系统 OpenGL ES 3.0 开发(09):光照基础 OpenGL ES 3.0 开发(10):深度测试 OpenGL ES 3.0 开发(11):模板测试 OpenGL ES 3.0 开发(12):混合 OpenGL ES 3.0 开发(13):实例化(Instancing) OpenGL ES 3.0 开发(14):粒子(ParticlesOpenGL ES 3.0 开发(15):立方体贴图(天空盒) OpenGL ES 3.0 开发(16):相机预览 OpenGL ES 3.0 开发(17):相机基础滤镜 OpenGL Android使用NDK  OpenGL ES3.0绘制一个三角形      网上已经有很多OpenCV的教程,不过大都是基于Java层调用openGL接口,若使用Java层openGL接口绘制三角形,还是比较简单的,但要是使用NDK C++ 实现,还是有点复杂。     本文将使用Android NDK开发,利用C++的 OpenGL ES3.0绘制一个三角形绘制三角形的C/C++源码大部分是参考:《OPENGL ES 3.0编程指南&nb 利用opengl进行yuv的渲染,主要原理利用显卡的加速运算,是把YUV转换成 RGBA ,然后交给gl渲染, 即opengl最终需要的数据还是 RGBA, 我们可以采用cpu软件jisua 编译Android平台上的freetype https://www.cnblogs.com/freedreamnight/p/14930341.html这篇文章写的十分详细,要注意的是,他这里用的是静态库,我自己使用的是动态库,只需要删除–enable-static这个参数就行了。 集成freetype到项目中 在项目app/src/main/cpp,把头文件放入对应的目录,其实不用在意,. 1.1 OpenGL ES 3.0 OpenGL ES 3.0实现了具有可编程着色功能的图形管线,由两个规范组成:OpenGL ES 3.0 API规范和OpenGL ES着色语言3.0规范。 图1-1展示了OpenGL ES 3.0 图形管线。图中带有阴影的方框表示OpenGL ES 3.0中管线的可编程阶段。 图1-1 OpenGL ES 3.0 图形管线 1.1.1 顶点着色器 顶点着色器实现了顶点操作的通用可编程方法。 顶点着色器的输入包括: 1 着色器程序代码 2 顶点输入 3 统一. 打算一边学习一边整理学习笔记,我的开发环境是Android studio 2.1.3,不过有个问题是Android studio自带的模拟器只能支持es2.0   无法使用es3.0  所以3.0只能在真机上调试,手机必须是4.3或者是以上系统,我试过4.3的三星s3和  6 早就听过大名鼎鼎的 OpenGL,却迟迟没有实践学习,有些惭愧。今天开始通过实践+博文方式学习掌握 OpenGL。此文对于 OpenGL 的学习分为以下部分: OpenGL 基础概念 OpenGL 坐标系理解 OpenGL 渲染管线 OpenGL 着色语言 OpenGL 基础概念 OpenGL OpenGL 即 Open Graphics Library,是一个功能强大、调用方便的底层图形库... OpenGL ES的基本流程和概念 这个系列我们一起对OpenGL ES进行重新学习实践,掌握OpenGL ES 3.0,编写迷人的OpenGL ES 3.0的程序。 下面开始今天的主题。 一、OpenGL ES的简介 OpenGL(Open Graphics Library)是一个跨平台图像程序接口,用于二维和三位图片的渲染。OpenGL ES(Open Graphic Library Embedded Systems)是OpenGL一个子集,用 在前面几张中你已经看看到,着色器是OpenGL ES 3.0 API 的一个基础核心概念,每个OpenGL ES 3.0 程序都需要一个顶点着色器和一个片段着色器,以渲染有意义的图片,考虑到着色器是API概念的核心,我们希望确保你在深入了解图形API的更多细节之前,掌握编写着色器的基础知识 本章的目标是确保你理解着色器语言中的如下概念 变量和变量类型 向量和矩阵的构造选择 结构和数组 运算符,控制流和函数 输入/输出变量,统一变量,统一变量块和布局限定符 预处理器和指令 统一变量和插