Android使用NDK OpenGL ES3.0绘制一个三角形
Android使用NDK ?OpenGL ES3.0繪制一個三角形
【尊重原創(chuàng),轉載請注明出處】https://blog.csdn.net/guyuealian/article/details/82021607
? ? ?網上已經有很多OpenCV的教程,不過大都是基于Java層調用openGL接口,若使用Java層openGL接口繪制三角形,還是比較簡單的,但要是使用NDK C++ 實現(xiàn),還是有點復雜。
? ? 本文將使用Android NDK開發(fā),利用C++的 OpenGL ES3.0繪制一個三角形。繪制三角形的C/C++源碼大部分是參考:《OPENGL ES 3.0編程指南?》第二章的代碼,但該書只有源碼,沒有工程項目,是用Android.mk配置,要做成Android Studio Demo還是要花點力氣的。本博客的OpenGL的開發(fā),其配置文件使用CMakeLists.txt,Android Studio 2.3以上NDK開發(fā)很容易啦,其配置方法使用CMakeLists.txt會比使用Android.mk更容易。
? ? 這里不具體分析繪制三角形的代碼實現(xiàn)過程了,畢竟《OPENGL ES 3.0編程指南?》這本書已經很詳細啦。
? ? 本項目源碼下載地址:https://github.com/PanJinquan/openGL-Demo?,要是覺得不錯,給個”Star“哈
開發(fā)環(huán)境:
- (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開發(fā),避免各種配置問題,如果提示沒有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 !");}}/*** 當創(chuàng)建 GLSurfaceView時,系統(tǒng)調用這個方法.使用這個方法去執(zhí)行只需要發(fā)生一次的動作,* 例如設置OpenGL環(huán)境參數或者初始化OpenGL graphic 對象.* @param gl* @param config*/@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {readShaderFile(mAssetMgr);glesInit();}/*** 當GLSurfaceView 幾何學發(fā)生改變時系統(tǒng)調用這個方法.包括 GLSurfaceView 的大小發(fā)生改變或者橫豎屏發(fā)生改變.* 使用這個方法去響應GLSurfaceView 容器的改變* @param gl* @param width* @param height*/@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {glesResize(width, height);}/*** 執(zhí)行渲染工作:當系統(tǒng)每一次重畫 GLSurfaceView 時調用.使用這個方法去作為主要的繪制和重新繪制graphic 對象的執(zhí)行點.* @param gl*/@Overridepublic 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;@Overrideprotected 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綁定的RenderermGLSurfaceView.setRenderer(mRenderer);// 設置渲染的模式mGLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);} else {Log.e("opengles30", "OpenGL ES 3.0 not supported on device. Exiting...");finish();}setContentView(mGLSurfaceView);}@Overrideprotected void onResume() {super.onResume();mGLSurfaceView.onResume();}@Overrideprotected 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 #endifRendererJNI.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 objectshader = glCreateShader ( type );if ( shader == 0 ){return 0;}// Load the shader sourceglShaderSource ( shader, 1, &shaderSrc, NULL );// Compile the shaderglCompileShader ( shader );// Check the compile statusglGetShaderiv ( 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 objectprogramObject = glCreateProgram ( );if ( programObject == 0 ){return;}glAttachShader ( programObject, vertexShader );glAttachShader ( programObject, fragmentShader );// Link the programglLinkProgram ( programObject );// Check the link statusglGetProgramiv ( 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 objectg_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 viewportglViewport ( 0, 0, g_width, g_height );// Clear the color bufferglClear ( GL_COLOR_BUFFER_BIT );// Use the program objectglUseProgram ( g_programObject );// Load the vertex dataglVertexAttribPointer ( 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開發(fā)需要把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-libandroid # 錯誤: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,效果圖:
?
總結
以上是生活随笔為你收集整理的Android使用NDK OpenGL ES3.0绘制一个三角形的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: C++实现线程安全的单例模式
 - 下一篇: bilateral filter双边滤波