GLFM 是用於使用 OpenGL ES 進行行動應用程式開發的 C API。它很大程度上受到 GLFW 的啟發。
GLFM 在 iOS 9、tvOS 9、Android 4.1 (API 16) 和 WebGL 1.0(透過 Emscripten)上運作。
此外,GLFM 在 iOS 和 tvOS 上提供 Metal 支援。
iOS系統 | 電視作業系統 | 安卓 | 網路 | |
---|---|---|---|---|
OpenGL ES 2、OpenGL ES 3 | ✔️ | ✔️ | ✔️ | ✔️ |
金屬 | ✔️ | ✔️ | 不適用 | 不適用 |
視網膜/高 DPI | ✔️ | ✔️ | ✔️ | ✔️ |
設備方向 | ✔️ | 不適用 | ✔️ | |
觸摸事件 | ✔️ | ✔️ | ✔️ | ✔️ |
滑鼠懸停事件 | ✔️1 | ✔️ | ||
滑鼠滾輪事件 | ✔️ | |||
滑鼠遊標樣式 | ✔️1 | ✔️ | ||
關鍵程式碼事件 | ✔️2 | ✔️ | ✔️ | ✔️ |
關鍵重複事件 | ✔️ | ✔️ | ||
字元輸入事件 | ✔️ | ✔️3 | ✔️ | ✔️ |
虛擬鍵盤 | ✔️ | ✔️ | ||
虛擬鍵盤可見性事件 | ✔️ | ✔️ | ||
加速計、磁力計、陀螺儀、設備旋轉 | ✔️ | 不適用 | ✔️ | |
觸覺回饋 | ✔️4 | 不適用 | ✔️ | |
剪貼簿 | ✔️ | 不適用 | ✔️ | ✔️ |
Chrome 插圖(「安全區域」) | ✔️5 | ✔️ | ✔️ | |
Chrome 插入更改事件 | ✔️5 | ✔️ | ✔️ | |
焦點事件 | ✔️ | ✔️ | ✔️ | ✔️ |
調整事件大小 | ✔️ | ✔️ | ✔️ | ✔️ |
記憶體警告事件 | ✔️ | ✔️ | ✔️ | |
OpenGL 上下文遺失事件(表面被破壞) | ✔️ | ✔️ | ✔️ | ✔️ |
1. 僅限 iPad。需要 iOS 13.4 或更高版本
2. 需要 iOS/tvOS 13.4 或更高版本
3. 需要 tvOS 13.4 或更高版本
4. 需要 iOS 13 或更高版本
5. 需要 iOS/tvOS 11 或更高版本
此外,也初步支援具有 OpenGL 3.2 的 macOS。 macOS 版本可用於開發目的,但不具備發布品質。例如,沒有設定視窗大小的功能。
GLFM 的範圍有限,並非旨在提供應用程式所需的一切。例如,GLFM 不提供(也永遠不會提供)以下內容:
相反,GLFM 可以與其他跨平台庫一起使用,提供應用程式所需的內容。
為了方便起見,提供了CMakeLists.txt
文件,儘管 CMake 不是必需的。
沒有 CMake:
include
和src
中)新增至您的專案。void glfmMain(GLFMDisplay *display)
函數。對於發布版本,定義NDEBUG
以刪除多餘的日誌記錄語句。 NDEBUG
是在 Android Studio 中為發布版本自動定義的,但在 Xcode 中則不然。
此範例在glfmMain()
中初始化顯示並在onDraw()
中繪製三角形。此處提供了更詳細的範例。
#include "glfm.h"
static GLint program = 0 ;
static GLuint vertexBuffer = 0 ;
static GLuint vertexArray = 0 ;
static void onDraw ( GLFMDisplay * display );
static void onSurfaceDestroyed ( GLFMDisplay * display );
void glfmMain ( GLFMDisplay * display ) {
glfmSetDisplayConfig ( display ,
GLFMRenderingAPIOpenGLES2 ,
GLFMColorFormatRGBA8888 ,
GLFMDepthFormatNone ,
GLFMStencilFormatNone ,
GLFMMultisampleNone );
glfmSetRenderFunc ( display , onDraw );
glfmSetSurfaceDestroyedFunc ( display , onSurfaceDestroyed );
}
static void onSurfaceDestroyed ( GLFMDisplay * display ) {
// When the surface is destroyed, all existing GL resources are no longer valid.
program = 0 ;
vertexBuffer = 0 ;
vertexArray = 0 ;
}
static GLuint compileShader ( const GLenum type , const GLchar * shaderString , GLint shaderLength ) {
GLuint shader = glCreateShader ( type );
glShaderSource ( shader , 1 , & shaderString , & shaderLength );
glCompileShader ( shader );
return shader ;
}
static void onDraw ( GLFMDisplay * display ) {
if ( program == 0 ) {
const GLchar vertexShader [] =
"#version 100n"
"attribute highp vec4 position;n"
"void main() {n"
" gl_Position = position;n"
"}" ;
const GLchar fragmentShader [] =
"#version 100n"
"void main() {n"
" gl_FragColor = vec4(0.85, 0.80, 0.75, 1.0);n"
"}" ;
program = glCreateProgram ();
GLuint vertShader = compileShader ( GL_VERTEX_SHADER , vertexShader , sizeof ( vertexShader ) - 1 );
GLuint fragShader = compileShader ( GL_FRAGMENT_SHADER , fragmentShader , sizeof ( fragmentShader ) - 1 );
glAttachShader ( program , vertShader );
glAttachShader ( program , fragShader );
glLinkProgram ( program );
glDeleteShader ( vertShader );
glDeleteShader ( fragShader );
}
if ( vertexBuffer == 0 ) {
const GLfloat vertices [] = {
0.0 , 0.5 , 0.0 ,
-0.5 , -0.5 , 0.0 ,
0.5 , -0.5 , 0.0 ,
};
glGenBuffers ( 1 , & vertexBuffer );
glBindBuffer ( GL_ARRAY_BUFFER , vertexBuffer );
glBufferData ( GL_ARRAY_BUFFER , sizeof ( vertices ), vertices , GL_STATIC_DRAW );
}
int width , height ;
glfmGetDisplaySize ( display , & width , & height );
glViewport ( 0 , 0 , width , height );
glClearColor ( 0.08f , 0.07f , 0.07f , 1.0f );
glClear ( GL_COLOR_BUFFER_BIT );
#if defined( GL_VERSION_3_0 ) && GL_VERSION_3_0
if ( vertexArray == 0 ) {
glGenVertexArrays ( 1 , & vertexArray );
}
glBindVertexArray ( vertexArray );
#endif
glUseProgram ( program );
glBindBuffer ( GL_ARRAY_BUFFER , vertexBuffer );
glEnableVertexAttribArray ( 0 );
glVertexAttribPointer ( 0 , 3 , GL_FLOAT , GL_FALSE , 0 , 0 );
glDrawArrays ( GL_TRIANGLES , 0 , 3 );
glfmSwapBuffers ( display );
}
參見glfm.h
使用cmake
生成Xcode專案:
cmake -D GLFM_BUILD_EXAMPLES=ON -B build/apple -G Xcode
open build/apple/GLFM.xcodeproj
在 Xcode 中,切換到glfm_touch
目標並在模擬器或裝置上運行。
使用emcmake
設定cmake
的環境變量,然後建構:
emcmake cmake -D GLFM_BUILD_EXAMPLES=ON -B build/emscripten && cmake --build build/emscripten
然後本地運行:
emrun build/emscripten/examples
或運行一個具體的例子:
emrun build/emscripten/examples/glfm_touch.html
Android Studio 專案沒有 CMake 產生器,但您可以在新專案或現有專案中包含CMakeLists.txt
。
[path/to/glfm]/build/android
然後按下「完成」。AndroidManifest.xml
中,加入主要
如下所示: xml version = " 1.0 " encoding = " utf-8 " ?>
< manifest xmlns : android = " http://schemas.android.com/apk/res/android " >
< uses-feature android : glEsVersion = " 0x00020000 " android : required = " true " />
< application
android : allowBackup = " true "
android : icon = " @mipmap/ic_launcher "
android : label = " @string/app_name "
android : supportsRtl = " true " >
< activity android : name = " android.app.NativeActivity "
android : exported = " true "
android : configChanges = " orientation|screenLayout|screenSize|keyboardHidden|keyboard " >
< meta-data
android : name = " android.app.lib_name "
android : value = " glfm_touch " />
< intent-filter >
< action android : name = " android.intent.action.MAIN " />
< category android : name = " android.intent.category.LAUNCHER " />
intent-filter >
activity >
application >
manifest >
app/build.gradle
中,新增externalNativeBuild
和sourceSets.main
部分,如下所示: apply plugin : ' com.android.application '
android {
compileSdkVersion 34
ndkVersion ' 23.2.8568313 ' // Last version to support API 16, 17, 18
defaultConfig {
applicationId " com.brackeen.glfmexample "
minSdkVersion 16
targetSdkVersion 34
versionCode 1
versionName " 1.0 "
// Add externalNativeBuild in defaultConfig (1/2)
externalNativeBuild {
cmake {
arguments " -DGLFM_BUILD_EXAMPLES=ON "
}
}
}
// Add sourceSets.main and externalNativeBuild (2/2)
sourceSets . main {
assets . srcDirs = [ " ../../../examples/assets " ]
}
externalNativeBuild {
cmake {
path " ../../../CMakeLists.txt "
}
}
namespace ' com.brackeen.glfmexample '
}
glfmMain
或從回呼函數)。 我應該使用什麼 IDE?為什麼沒有桌面實作?使用 Xcode 或 Android Studio。對於桌面,請將 GLFW 與您選擇的 IDE 結合使用。
如果您不喜歡使用行動模擬器進行日常開發,一個好的解決方案是使用 GLFW,然後將您的應用程式移植到 GLFM。並非所有 OpenGL 呼叫都能完美移植到 OpenGL ES,但為了獲得最大程度的 OpenGL 可移植性,請在桌面上使用 OpenGL 3.2 Core Profile,在行動裝置上使用 OpenGL ES 2.0。
為什麼入口點是glfmMain()
而不是main()
?
否則,它無法在 iOS 上運行。要初始化 Objective-C 環境, main()
函數必須建立一個自動釋放池並呼叫UIApplicationMain()
函數,該函數永遠不會回傳。在 iOS 上,GLFM 在UIApplicationDelegate
和UIViewController
初始化之前不會呼叫glfmMain()
。
為什麼 GLFM 是事件驅動的?為什麼 GLFM 接管主循環?
否則,它無法在 iOS(見上文)或事件驅動的 HTML5 上運行。