NDK OpenGL ES 3.0 開發(二十):3D 模型

該原創文章首發於微信公衆號:字節流動

OpenGLES 3D 模型

在這裏插入圖片描述
OpenGLES 3D 模型本質上是由一系列三角形在 3D 空間(OpenGL 座標系)中構建而成,另外還包含了用於描述三角形表面的紋理、光照、材質等信息。

構建一些規則的 3D 物體,如立方體、球體、椎體等,我們自己可以手動輕易實現,但是在實際開發中往往會用到複雜的 3D 物體,如人體、汽車等,這就需要設計師和專業的建模工具軟件(像 3DS Max、Maya )來生成。

利用 3D 建模軟件,設計師可以構建一些複雜的形狀,並將貼圖應用到形狀上去,不需要去關注圖像技術細節。最後在導出模型文件時,建模工具會自己生成所有的頂點座標、頂點法線和紋理座標。

常用的模型文件格式有 .obj、.max、.fbx .3ds 等,其中 .obj 是 Wavefront 科技開發的一種幾何體圖形文件格式,包含每個頂點的位置、紋理座標、法線,以及組成面(多邊形)的頂點列表等數據,應用較爲廣泛。

OBJ 文件的結構

本文主要介紹 obj 3D 模型文件及其附屬文件 mtl ,下面是 obj 模型文件的數據結構(爲了方便展示部分數據被略過)。

# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2019 guruware
# File Created: 25.01.2019 02:22:51

mtllib earth.mtl

v  -4.4189 340.8998 -4.2436
v  -4.4189 339.2582 -37.6576
v  -7.6941 339.2582 -37.4967
v  -10.9377 339.2582 -37.0156
v  -14.1185 339.2582 -36.2188
v  -17.2059 339.2582 -35.1141
...
# 1986 vertices

vn -0.0000 1.0000 0.0000
vn 0.0000 0.9950 -0.0995
vn -0.0098 0.9950 -0.0991
vn -0.0194 0.9950 -0.0976
vn -0.0289 0.9950 -0.0952
vn -0.0381 0.9950 -0.0920
vn -0.0469 0.9950 -0.0878
...
# 1986 vertex normals

vt 0.0000 1.0000 0.0000
vt 0.0000 0.9688 0.0000
vt 0.0156 0.9688 0.0000
vt 0.0156 1.0000 0.0000
vt 0.0313 0.9688 0.0000
vt 0.0313 1.0000 0.0000
...
# 2143 texture coords

usemtl 01___Default 
s 1
f 1/1/1 2/2/2 3/3/3 
f 1/4/1 3/3/3 4/5/4 
f 1/6/1 4/5/4 5/7/5 
f 1/8/1 5/7/5 6/9/6 
f 1/10/1 6/9/6 7/11/7 
...

OBJ 文件數據結構的簡單說明:

  1. # 開頭的行表示註釋行;
  2. mtllib 表示指定該 OBJ 文件所使用的 mtl 文件(材質文件);
  3. v 開頭的行表示存放的是頂點座標,後面三個數分別表示一個頂點的(x,y,z)座標值;
  4. vn 開頭的行表示存放的是頂點法向量,後面三個數分別表示一個頂點法向量的三維(x,y,z)分量值;
  5. vt 開頭的行表示存放的是紋理座標,後面三個數分別表示一個紋理座標的(s,t,p)分量值,其中 p 分量一般用於 3D 紋理;
  6. usemtl 01___Default 表示使用指定 mtl 文件中名爲 01___Default的材質;
  7. s 1 表示開啓平滑渲染;
  8. f 開頭的行表示存放的是一個三角面的信息,後面有三組數據分別表示組成三角面的三個頂點的信息,每個頂點信息的格式爲:頂點位置索引/紋理座標索引/法向量索引。

mtl 文件的結構:

# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2019 guruware
# File Created: 25.01.2019 02:22:51

newmtl 01___Default
	Ns 10.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.0000 0.0000 0.0000
	Kd 0.0000 0.0000 0.0000
	Ks 0.0000 0.0000 0.0000
	Ke 0.0000 0.0000 0.0000
	map_Ka 4096_earth.jpg
	map_Kd 4096_earth.jpg
	map_Ke 4096_night_lights.jpg
	map_bump 4096_bump.jpg
	bump 4096_bump.jpg

newmtl 02___Default
	Ns 10.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.5882 0.5882 0.5882
	Kd 0.5882 0.5882 0.5882
	Ks 0.0000 0.0000 0.0000
	Ke 0.0000 0.0000 0.0000
	map_Ka 4096_clouds.jpg
	map_Kd 4096_clouds.jpg
	map_d 4096_clouds.jpg

mtl 文件結構說明:

  1. newmtl 01___Default表示定義一個名爲 01___Default 的材質;
  2. Ns 表示材質的反射指數,反射指數越高則高光越密集,取值範圍在一般爲 [0,1000];
  3. Ni 表示材質的折射值(折射率),一般取值範圍是 [0.001,10] ,取值爲 1.0,表示光在通過物體的時候不發生彎曲,玻璃的折射率爲 1.5 ;
  4. d 表示材質的漸隱指數(通透指數),取值爲 1.0 表示完全不透明,取值爲 0.0 時表示完全透明;
  5. Tr 表示材質的透明度(與 d 的取值相反),默認值爲0.0(完全不透明);
  6. Tf 表示材質的濾光折射率,三維向量表示;
  7. illum 表示材質的光照模型;
  8. Ka 表示材質的環境光(Ambient Color)(r,g,b);
  9. Kd 表示材質的散射光(Diffuse Color)(r,g,b);
  10. Ks 表示材質的鏡面光(Apecular Color)(r,g,b);
  11. Ke 表示材質的發射光,它與環境光,散射光和鏡面光並存,代表材質發出的光量;
  12. map_Ka 表示爲材質的環境反射指定紋理文件(紋理採樣值與環境光相乘作爲輸出顏色的一部分加權);
  13. map_Kd 表示爲材質的漫反射指定紋理文件;
  14. map_Ke 表示爲材質的發射光指定紋理文件;
  15. map_d 表示爲材質的透明度指定紋理文件;
  16. bump 表示指定材質的凹凸紋理文件,凹凸紋理修改表面法線,用於凹凸紋理的圖像表示相對於平均表面的表面拓撲或高度(沒用過)。

模型加載庫 Assimp

Assimp 全稱爲 Open Asset Import Library,可以支持幾十種不同格式的模型文件的解析(同樣也可以導出部分模型格式),Assimp 本身是 C++ 庫,可以跨平臺使用。

Assimp 可以將幾十種模型文件都轉換爲一個統一的數據結構,所有無論我們導入何種格式的模型文件,都可以用同一個方式去訪問我們需要的模型數據。

當導入一個模型文件時,Assimp 將加載該模型文件所包含的所有模型和場景數據到一個 scene 對象,爲這個模型文件中的所有場景節點、模型節點都生成一個具有對應關係的數據結構,如下圖所示:

Assimp生成的模型文件數據結構

一個模型往往是由很多小模型組成,這些小模型在 Assimp 中稱之爲 Mesh ,Mesh 進行獨立渲染,Mesh 對象本身包含渲染所需的所有相關數據,比如頂點位置、法向量、紋理座標以及物體的材質。

編譯模型加載庫 Assimp

Assimp 源代碼地址:https://github.com/assimp/assimp

環境準備:

Windows 7 
Android Studio 版本> 2.3.0 (帶有 NDK 和 CMake)
Python 3.5 
  1. 從 Github 下載 Assimp(本文使用的是 assimp-v.5.0.0) 源碼並解壓到一個新建文件夾 BuildAssimp 中;
  2. 在 BuildAssimp 文件夾新建一個 make_standalone_toolchain.bat 文件用於創建編譯所需的工具鏈,該文件內容如下:
python D:/AndroidSDK/Sdk/ndk-bundle/build/tools/make_standalone_toolchain.py --arch=arm --stl=libc++ --api=24 --install-dir=android-toolchain-24-llvm-arm

D:/AndroidSDK/Sdk/ndk-bundle/build/tools/make_standalone_toolchain.py 需要換成你本地的 Android SDK 中的工具鏈構建腳本。

雙擊腳本,執行完成後,會在當前目錄下(BuildAssimp 文件夾中)生成一個新的文件夾 android-toolchain-24-llvm-arm ,它是我們用於編譯 32 位的 assimp 庫用到的工具鏈。

  1. 在 BuildAssimp 文件夾中新建一個 build_assimp.bat 文件,用於編譯生成 assimp.so 文件,其內容如下:
@echo off
cls

REM *NOTE* Change these based on 
SET ASSIMP_DIR=assimp-v.5.0.0
SET OUTPUT_DIR=assimp-build-arm
SET ANDROID_PATH=D:\AndroidSDK\Sdk
SET NDK_PATH=D:\AndroidSDK\Sdk\ndk-bundle
SET NDK_TOOLCHAIN=%~dp0android-toolchain-24-llvm-arm
SET CMAKE_TOOLCHAIN=%NDK_PATH%\build\cmake\android.toolchain.cmake
SET CMAKE_PATH=%ANDROID_PATH%\cmake\3.6.4111459

REM *NOTE* Careful if you don't want rm -rf, I use it for testing purposes.
del /F /S /Q %OUTPUT_DIR%
mkdir %OUTPUT_DIR%

REM pushd doesn't seem to work ):<
cd %OUTPUT_DIR%

if not defined ORIGPATH set ORIGPATH=%PATH%
SET PATH=%CMAKE_PATH%\bin;%ANDROID_PATH%\tools;%ANDROID_PATH%\platform-tools;%ORIGPATH%

cmake ^
      -GNinja ^
      -DCMAKE_TOOLCHAIN_FILE=%CMAKE_TOOLCHAIN% ^
      -DASSIMP_ANDROID_JNIIOSYSTEM=ON ^
      -DANDROID_NDK=%NDK_PATH% ^
      -DCMAKE_MAKE_PROGRAM=%CMAKE_PATH%\bin\ninja.exe ^
      -DCMAKE_BUILD_TYPE=Release ^
      -DANDROID_ABI="armeabi-v7a" ^
      -DANDROID_NATIVE_API_LEVEL=24 ^
      -DANDROID_FORCE_ARM_BUILD=TRUE ^
      -DCMAKE_INSTALL_PREFIX=install ^
      -DANDROID_STL=c++_static ^
      -DCMAKE_CXX_FLAGS=-Wno-c++11-narrowing ^
      -DANDROID_TOOLCHAIN=clang ^

      -DASSIMP_BUILD_TESTS=OFF ^

      ../%ASSIMP_DIR%

cmake --build .

cd ..

pause

這裏需要配置你的 Android SDK 和 NDK path 。
在這裏插入圖片描述

雙擊腳本,執行完成後,會在 BuildAssimp 文件夾下生成一個新的文件夾 android-toolchain-24-llvm-arm ,編譯通過後會在 android-toolchain-24-llvm-arm/code/ 下生成 32 位的 assimp.so 文件。

下一節中將會使用 assimp.so 去加載 obj 模型文件,然後利用 OpenGL ES 渲染模型。

聯繫與交流

我的公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章