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

版权声明:本文为博主原创文章,未经博主允许不得转载(pan_jinquan) https://blog.csdn.net/guyuealian/article/details/82021607

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头文件

扫描二维码关注公众号,回复: 3052823 查看本文章
/* 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 !");
        }
    }
    else
    {
        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,效果图:

猜你喜欢

转载自blog.csdn.net/guyuealian/article/details/82021607