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 | tvOS | 기계적 인조 인간 | 편물 | |
---|---|---|---|---|
OpenGL ES 2, OpenGL ES 3 | ✔️ | ✔️ | ✔️ | ✔️ |
금속 | ✔️ | ✔️ | 해당 없음 | 해당 없음 |
레티나/고DPI | ✔️ | ✔️ | ✔️ | ✔️ |
장치 방향 | ✔️ | 해당 없음 | ✔️ | |
터치 이벤트 | ✔️ | ✔️ | ✔️ | ✔️ |
마우스 호버 이벤트 | ✔️ 1 | ✔️ | ||
마우스 휠 이벤트 | ✔️ | |||
마우스 커서 스타일 | ✔️ 1 | ✔️ | ||
키코드 이벤트 | ✔️ 2 | ✔️ | ✔️ | ✔️ |
주요 반복 이벤트 | ✔️ | ✔️ | ||
문자 입력 이벤트 | ✔️ | ✔️ 3 | ✔️ | ✔️ |
가상 키보드 | ✔️ | ✔️ | ||
가상 키보드 공개 이벤트 | ✔️ | ✔️ | ||
가속도계, 자력계, 자이로스코프, 장치 회전 | ✔️ | 해당 없음 | ✔️ | |
햅틱 피드백 | ✔️ 4 | 해당 없음 | ✔️ | |
클립보드 | ✔️ | 해당 없음 | ✔️ | ✔️ |
Chrome 삽입('안전 영역') | ✔️ 5 | ✔️ | ✔️ | |
Chrome 삽입 변경 이벤트 | ✔️ 5 | ✔️ | ✔️ | |
포커스 이벤트 | ✔️ | ✔️ | ✔️ | ✔️ |
이벤트 크기 조정 | ✔️ | ✔️ | ✔️ | ✔️ |
메모리 경고 이벤트 | ✔️ | ✔️ | ✔️ | |
OpenGL 컨텍스트 손실 이벤트(표면 파괴) | ✔️ | ✔️ | ✔️ | ✔️ |
1. 아이패드 전용입니다. 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은 앱에 필요한 것을 제공하는 다른 크로스 플랫폼 라이브러리와 함께 사용할 수 있습니다.
CMake가 필수는 아니지만 편의를 위해 CMakeLists.txt
파일이 제공됩니다.
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를 사용하세요. 데스크탑의 경우 원하는 IDE와 함께 GLFW를 사용하세요.
일상적인 개발에 모바일 시뮬레이터를 사용하지 않으려는 경우 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에서 작동하지 않습니다.