Android Studio NDK-OpenGL ES/EGL渲染

Android Studio NDK-OpenGL ES/EGL渲染
Android Studio NDK-OpenGL ES/EGL渲染

OpenGL EGL作為平臺冇関 API, 今程序冇視『Linux X Window』『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 布局OpenGL ES
  4. 先加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL實用架餐庫
#include <EGL/egl.h> EGL庫
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫ANativeWindow
  1. 冚辦爛EGL程式首先調用『eglGetDisplay()』, 得到『EGLDisplay』等同『原生視窗』, 『EGLDisplay』係整數索引.
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1. 索引大於零即成功, 因為等於零失敗.
EGL_NO_DISPLAY 0失敗
  1. 初始EGL程式, 幷獲得『major』主版號 , 『minor』副版號
EGLint major,minor;

eglInitialize(display,&major,&minor)  ;

  1. 失敗返回EGL_FALSE, 查錯誤碼eglGetError()
EGL_BAD_DISPLAY 非有效EGLDisplay
EGL_NOT_INITALIZED 未能初始
  1. 穩『surface』渲染配置
EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
  1. EglChooseConfig()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLint *attrib_list 指定屬性列表, 以EGL_NONE結屘
EGLConfig *configs 返回配置列表
EGLint config_size 指定配置列表長度
EGLint *num_config 返回配置長度
  1. 『EGLConfig』屬性
屬性 描述
EGL_SURFACE_TYPE EGL渲染表面類型 EGL_WINDOW_BIT
EGL_RENDERABLE_TYPE OpenGL版本 EGL_OPENGL_ES_BIT
EGL_RED_SIZE 紅色量位 8Bit
EGL_GREEN_SIZE 綠色量位 8Bit
EGL_BLUE_SIZE 藍色量位 8Bit
EGL_ALPHA_SIZE 透明量位 8Bit
EGL_DEPTH_SIZE 深度量位 16Bit
EGL_NONE 屬性列表以EGL_NONE結屘 0

屬性列表

EGLint    attrib_array[32];
  1. 設定『EGLConfig』屬性
attrib_array[0] = EGL_SURFACE_TYPE; attrib_array[1] = EGL_WINDOW_BIT;
attrib_array[2] = EGL_RENDERABLE_TYPE; attrib_array[3] = EGL_OPENGL_ES_BIT;
attrib_array[4] = EGL_RED_SIZE; attrib_array[5] = 8;
attrib_array[6] = EGL_GREEN_SIZE; attrib_array[7] = 8,
attrib_array[8] = EGL_BLUE_SIZE; attrib_array[9] = 8;
attrib_array[10] = EGL_ALPHA_SIZE; attrib_array[11] = 8;
attrib_array[12] = EGL_DEPTH_SIZE; attrib_array[13] = 16;
attrib_array[14] = EGL_NONE;  
  1. 據 attrib_array 返回最隹配置config
EGLint    config_number = 32;

EGLConfig config_array[32];

EGLConfig  config;// 配置

eglChooseConfig(display,attrib_array,config_array,config_number,&config_number);

  1. 匹配最佳配置
for(int i=0;i<config_number;++i)    {

eglGetConfigAttrib(openGL->display, config_array[i],EGL_RED_SIZE, &red);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_GREEN_SIZE, &green);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_BLUE_SIZE, &blue);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_ALPHA_SIZE, &alpha);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_DEPTH_SIZE, &depth);

if( red == 8 && green == 8 && blue == 8 && alpha == 8 && depth == 16){

openGL->config = config_array[i];// 最佳配置

break;

}

}

  1. 生成渲染視窗
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
  1. eglCreateWindowSurface()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLConfig config 最佳配置
EGLNativeWindowType win SurfaceView
const EGLint *attrib_list 屬性列表
  1. 渲染視窗屬性
視窗屬性 描述
EGL_RENDER_BUFFER 渲染緩存 EGL_SINGLE_BUFFER『單緩存』EGL_BACK_BUFFER『雙緩存』
EGL_NONE 屬性列表以EGL_NONE結屘 0
  1. 屬性
attrib_array[0] = EGL_RENDER_BUFFER; attrib_array[1] = EGL_BACK_BUFFER;
attrib_array[2] = EGL_NONE;  
  1. 生成渲染視窗
surface_draw = eglCreateWindowSurface(display,config,nativeWindow,attrib_array);
  1. 獲得錯誤碼
錯誤碼EGLint error = eglGetError(); 簡介
EGL_BAD_MATCH 視窗唔匹配EGLConfig唔支持渲染
EGL_BAD_CONFIG 視窗唔匹配EGLConfig系統晤支持
EGL_BAD_NATIVE_WINDOW 原生視窗句柄冇效
EGL_BAD_ALLOC 冇法分配資源
  1. 關聯描述表, 将『EGLContext』『EGLSurface』與當前線程關聯, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回
eglMakeCurrent(display,surface_draw,surface_draw,context);
  1. 『Native』層用『ANativeWindow 』蒞渲染.通過『Surface』得到『ANativeWindow』.
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface)
  1. Android 『java』層用『SurfaceView』蒞渲染, 提取『Surface』畀『Native』
SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface_view);
SurfaceHolder     surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
Surface             surface = surfaceHolder.getSurface();

Android Studio NDK-OpenGL ES布局

Android Studio NDK-OpenGL ES布局
Android Studio NDK-OpenGL ES布局

『Android Studio NDK』提供『EGL』連接『OpenGL』, 『EGL』被設計出來,作爲 OpenGL 和原生窗口系統之間的橋梁『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 載入共享庫”app.so”,『app』系你庫名.
static {

System.loadLibrary(“app”);

}

  1. 首先修改布局『xml』添加『SurfaceView』,而非『GLSurfaceView』.
 <android.view.SurfaceView

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:id=”@+id/surface_view” />

  1. 係『xml』添加『全屏樣式』
<style  name=”FullscreenTheme”  parent=”android:Theme.NoTitleBar.Fullscreen”  >
  1. 係『java』加『android.view.SurfaceHolder.Callback』用蒞行『SurfaceView』『創建』『改變』『銷毀』
        @Override

public void surfaceCreated(SurfaceHolder surfaceHolder) {

new Thread(this).start();//渲染,啟動線程  Runnable.run()

}

        @Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

}

        @Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

}

  1. 係『java』加『Runnable』用蒞行OpenGL 渲染線程, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回EGL_BAD_SURFACE.
    @Override

public void run() {

init( );

while (true) {

update(0);

}

}

  1. 係『xml』為Google Play加版本過濾
<uses-sdk        android:minSdkVersion=”9″       android:targetSdkVersion=”19″ />
  1. 係『txt』穩庫文檔
find_library( OpenGL-lib libGLESv1_CM.so  )
find_library( OpenEGL-lib libEGL.so )
find_library( Android-lib libandroid.so )
  1. 係『txt』連連接庫文檔
target_link_libraries(app

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib})

  1. 加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL架餐庫
#include <EGL/egl.h> EGL頭文檔
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫

 

Android Studio NDK-OpenGL ES-3D相機gluPerspective()

Android Studio NDK-OpenGL ES-3D相機gluPerspective()
Android Studio NDK-OpenGL ES-3D相機gluPerspective()

將『OpenGL』移稙過『Andord』. 但OpenGL ES偏偏冇gluPerspective()視錐投影.但有glFrustumf()設定視口.

通過glFrustumf()設置視口, 得到 gluPerspective()視錐投影.
void gluPerspective(double  fovy,double aspect,double zNear,double zFar){

double  ymax = zNear * tan(fovy * 3.141592654f / 360.0f);

double  ymin = -ymax;

double    xmin = ymin * aspect;

double    xmax = ymax * aspect;

glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);

}

3D相機投影代碼

glViewport(0,0,viewport_width,viewport_height); 重置視區尺寸
glMatrixMode(GL_PROJECTION); 設定投影矩陣
glLoadIdentity(); 載入單位矩陣
gluPerspective(fov,aspect_ratio,near_clip_z,far_clip_z); 設置視錐體投影
glMatrixMode(GL_MODELVIEW); 設定模型視圖矩陣
glLoadIdentity(); 載入單位矩陣

 

Android Studio NDK-OpenGL-交換緩存畫面eglSwapBuffers()返回EGL_BAD_SURFACE

Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE
Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE

將『風水羅盤』由『Windows』移稙過『Andord』. 係交換緩存畫面『eglSwapBuffers()』返回『EGL_FALSE』, 『eglGetError()』返回『EGL_BAD_SURFACE』.

EGLBoolean ret = eglSwapBuffers(display,surface);

if(ret == EGL_FALSE){

GLint error = eglGetError();

if(error == EGL_BAD_SURFACE){

}

}

係『Windows』有『Winmain()』入口. 可以控制冚個整游戲運作, 係 『Winmain()』 游戲『運行』『渲染』都係主線程行.

而 『Andord』冇『Winmain()』. 游戲『運行』『渲染』要係新線程『Thread()』行. 而『EGLContext』同『EGLSurface』需係同壹線程『Thread()』生成, 唔係『eglSwapBuffers()』投『EGL_BAD_SURFACE』錯誤碼.

知錯係邊就易整, 將egl()程式擺係新線程『Thread()』行,而非『 onSurfaceCreated()』.

新線程『Thread()』
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);

 

 

Android Studio NDK- assets訪問讀

Android Studio NDK- assets讀係Android游戲唔將游戲資源擺係『res』而係『assets』, 事因『res』限制層次結構, 而『assets』允許任意檔案資料夾層次結構.位於『assets』檔案只讀唔寫. NDK需android 9版先支持.

係『CMakeLists.txt』添加『libandroid.so』庫

CMakeLists.txt
find_library( Android-lib libandroid.so )
target_link_libraries( geomanticcompass

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib}

${log-lib})

係c檔案加入頭檔

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

『assets』檔通過『AssetManager』訪問

AssetManager assetManager = this.getAssets();

係『NDK』『assets』檔通過『AAssetManager』訪問, 『AAssetManager』接口

AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);

將『AssetManager』傳過去獲得『AAssetManager』

extern “C”

JNIEXPORT void JNICALL Java_net_bookcard_geomanticcompass_MainActivity_init(JNIEnv *env, jobject thiz, jobject egl_config, jobject asset_manager) {

AAssetManager *assetManager= AAssetManager_fromJava(env, asset_manager);

}

public native void init(AssetManager asset_manager);
init (getAssets());

 

開啟『assets』檔, 冇需畀絕對路徑, 而係畀『assets』相對路俓

AAsset *asset = AAssetManager_open(nativeasset, “name.bmp”, AASSET_MODE_BUFFER);

檔长度

int size = AAsset_getLength(asset)

分配記憶體

PBYTE data = (PBYTE)malloc(size);

讀『assets』檔

AAsset_read(asset, data, size);

載入分析

Load(data, size);

释放记忆体

free(data);

閂『assets』檔

AAsset_close(asset);

 

將游戲資源擺係『assets』下,『assets』『res』同位於『app\src\main』之下.

檔案資料夾 位置
assets D:\Android\game\app\src\main\assets
cpp D:\Android\game\app\src\main\cpp
java D:\Android\game\app\src\main\java
res D:\Android\game\app\src\main\res

 

 

Android Studio NDK-OpenGL ES替換gluLookAt()

OpenGL ES替換gluLookAt()
OpenGL ES替換gluLookAt()

gluLookAt()  UVN相機模型,設定視點(相機)位置和視綫方向(矩陣運算).

void gluLookAt( GLdouble eyeX,

GLdouble eyeY,

GLdouble eyeZ,

GLdouble centerX,

GLdouble centerY,

GLdouble centerZ,

GLdouble upX,

GLdouble upY,

GLdouble upZ);

OpenGL ES冇gluLookAt() , 手工生成『旋轉矩陣』『相機位置』函式

void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,

GLfloat centerx, GLfloat centery, GLfloat centerz,

GLfloat upx, GLfloat upy, GLfloat upz)

{

GLfloat m[16];// 旋轉矩陣

GLfloat x[3], y[3], z[3];

GLfloat mag;

//生成旋轉矩陣

// Z矢量

z[0] = eyex – centerx;

z[1] = eyey – centery;

z[2] = eyez – centerz;

mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);

if (mag) {

z[0]/= mag;

z[1]/= mag;

z[2]/= mag;

}

 

// Y矢量

y[0] = upx;

y[1] = upy;

y[2] = upz;

 

// X 矢量 = Y 叉積 Z

x[0] = y[1] * z[2] – y[2] * z[1];

x[1] = -y[0] * z[2] + y[2] * z[0];

x[2] = y[0] * z[1] – y[1] * z[0];

 

// 重新計算 Y = Z 叉積 X

y[0] = z[1] * x[2] – z[2] * x[1];

y[1] = -z[0] * x[2] + z[2] * x[0];

y[2] = z[0] * x[1] – z[1] * x[0];

 

 

// 叉積給出了平行四邊形的面積,對于非垂直單位長度向量;所以在這裏標準化x,y

mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);

if (mag) {

x[0]/= mag;

x[1]/= mag;

x[2]/= mag;

}

 

mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);

if (mag) {

y[0]/= mag;

y[1]/= mag;

y[2]/= mag;

}

 

#define M(row,col)  m[col*4+row]

M(0, 0) = x[0];

M(0, 1) = x[1];

M(0, 2) = x[2];

M(0, 3) = 0.0;

M(1, 0) = y[0];

M(1, 1) = y[1];

M(1, 2) = y[2];

M(1, 3) = 0.0;

M(2, 0) = z[0];

M(2, 1) = z[1];

M(2, 2) = z[2];

M(2, 3) = 0.0;

M(3, 0) = 0.0;

M(3, 1) = 0.0;

M(3, 2) = 0.0;

M(3, 3) = 1.0;

#undef M

glMultMatrixf(m);

 

// 視點(相機)位置

glTranslatef(-eyex, -eyey, -eyez);

}

 

Android Studio NDK编译OpenGL ES

Android Studio NDK编译OpenGL ES
Android Studio NDK编译OpenGL ES

係Android Studio NDK编译OpenGL ES, 需鏈接『libGLESv1_CM.so』庫, 編輯『CMakeLists.txt』.

首先稳『libGLESv1_CM.so』庫

find_library( OpenGL-lib libGLESv1_CM.so )

鏈接『libGLESv1_CM.so』庫

target_link_libraries(  geomanticcompass

${OpenGL-lib}

${log-lib})

 

3ds Max導出MD2檔

3ds Max導出MD2檔啉住將3D模型導出『.OBJ』但寫『解碼』費時.更好方法導出『MD2』模型. 『MD2』仍名作『Quake2』專屬3D模型.有大量書籍講解『MD2』模型結構.

  1. 首先網絡下載『ms』與『md2Export.ms』.
  2. 『ms』與『md2Export.ms』屬於Script腳本.需複製到『C:\Program Files\Autodesk\3ds Max \scripts\startup』
  3. 重啟『3ds Max』
  4. 撳Q選擇3D模型.
  5. 撳『Edit mesh編輯網格
  6. 撳『Utilities』錘子圖標
  7. 撳『MAX Script』->『Utilities』選MD2 Exporter.激活『MD2 Exporter』Script腳本.
  8. 撳『Export』導出MD2模型檔.
 Save Animation 『保存動畫』勾選激活,導出動畫.
Frame Step 『幀步』.默認1.
Active Time Segment 導出『冚辦爛』活動時間段
Custom Time Segment 導出指定時間段
Generate Normals 『生成法線』勾選激活
Export 『導出』MD2模型檔.

 

3ds Max 2009 C:\Program Files\Autodesk\3ds Max 2009\scripts\startup
3ds Max 2018 C:\Program Files\Autodesk\3ds Max 2018\scripts\startup

 

OpenGL相機之神睇

OpenGL相機之神睇人睇世界為平視,神睇世界則高處俯視.OpenGL用UVN相機gluLookAt()用於指定相機視線. 神位於10米高,處垂直俯視地表即『視線』設(0.0f,0.0f,0.0f).將『頭頂指向』設(0.0f,0.0f,1.0f) .

void gluLookAt(0.0f, 10.0f, 0.0f,    0.0f, 0.0f, 0.0f,   0.0f,0.0f,1.0f);
void gluLookAt(GLdouble eyeX,GLdouble eyeY,GLdouble eyeZ,

GLdouble centerX,GLdouble centerY,GLdouble centerZ,

GLdouble upX,GLdouble upY,GLdouble upZ);

eyeX, eyeY, eyeZ 神目位置.即相機位置. 世界坐標位置
centerX, centerY, centerZ 『視點』指向位置.即『視線』. 世界坐標位置
upX, upY, upZ 神頭頂指向,設(0.0f,0.1f,0.0f)

用『歐拉』Euler相機模型則方便直接繞X軸轉90度.

glRotatef(90.0f, 1.0f, 0.0f, 0.0f); 傾斜/俯仰繞X軸旋轉的角度
glRotatef(0.0f, 0.0f, 0.0f, 1.0f); 橫搖/側滾繞Z軸旋轉的角度
glRotatef(0.0f, 0.0f, 1.0f, 0.0f); 偏航/航向繞Y軸旋轉的角度
glTranslatef(-x,-y,-z); 移動模型坐標

 

Android遊戲之跟隨相機

Android遊戲之跟隨相機

『跟隨相機』與『歐拉相機』喂一區別在於屬性設置不同.跟隨相機常將它固定在移動物體上.它需要以下屬性:

3D空間位置position

向上向量.相當於在相機上貼上一個向上箭頭up

視點向量即相機視口朝向目標lookAt

遠裁剪面far

近裁剪面near

『視場』即視口角度fieldOfView

視口縱橫比aspectRatio

再移動相機時你需要分被相機『位置』與『視點』.『跟隨相機』生成代碼:

設定相機視口,寬與高為屏幕分辨率

gl.glViewport(0,0,width,height);

設置相機矩陣,將當前堆棧設為投影矩陣

gl.glMatrixMode(GL10.GL_PROJECTION);

棧頂載入單位矩陣

gl.glLoadIdentity();

設置透視投影矩陣.定義視錐體參數.『視口角度』『視口縱橫比』『遠近裁剪面』

GLU.gluPerspective(gl, fieldOfView, aspectRation, near, far);

將當前堆棧設為模型視圖矩陣

gl.glMatrixMode(GL10.GL_MODELVIEW);

棧頂載入單位矩陣

gl.glLoadIdentity();

生成方位矩陣,好處在於能防止出現弄反位置或角度

GLU.gluLookAt(gl, position.x,position.y,position.z,

lookAt.x, lookAt.y, lookAt.z,

up.x, up.y, up.z);