这是我为运行我的商业映射和路由库的各种平台实现 OpenGL ES 2.0 支持的经验结果。
渲染类被设计为在单独的线程中运行。它具有对包含地图数据和当前视图信息的对象的引用,并使用互斥锁来避免在绘图时读取该信息时发生冲突。它在图形内存中维护 OpenGL ES 矢量数据的缓存。
所有的渲染逻辑都是用C++编写的,在以下所有平台上都可以使用。
Windows (MFC)
使用 ANGLE 库:链接到 libEGL.lib 和 libGLESv2.lib 并确保可执行文件可以访问 DLL libEGL.dll 和 libGLESv2.dll。 C++ 代码创建了一个线程,该线程以适当的速率(例如,每秒 25 次)重绘图形。
Windows(.NET 和 WPF)
使用 C++/CLI 包装器创建 EGL 上下文并调用直接在 MFC 实现中使用的 C++ 呈现代码。 C++ 代码创建了一个线程,该线程以适当的速率(例如,每秒 25 次)重绘图形。
Windows (UWP)
在 UWP 应用代码中创建 EGL 上下文,并通过 C++/CXX 包装器调用 C++ 呈现代码。您将需要使用 SwapChainPanel 并创建在不同线程中运行的自己的渲染循环。示例代码参见GLUWP 项目。
Windows、Linux 和 Mac OS 上的 Qt
使用 QOpenGLWidget 作为您的窗口。使用 Qt OpenGL ES 包装器创建 EGL 上下文,然后在您的 paintGL() 函数中调用 C++ 渲染代码。
安卓
创建一个实现 android.opengl.GLSurfaceView.Renderer 的渲染器类。为 C++ 呈现对象创建 JNI 包装器。在 onSurfaceCreated() 函数中创建 C++ 渲染对象。在 onDrawFrame() 函数中调用 C++ 渲染对象的绘图函数。您需要为您的渲染器类导入以下库:
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;
创建一个从 GLSurfaceView 派生的视图类。在您的视图类的构造函数中,首先设置您的 EGL 配置:
setEGLContextClientVersion(2); // use OpenGL ES 2.0
setEGLConfigChooser(8,8,8,8,24,0);
然后创建一个渲染器类的实例并调用 setRenderer 来安装它。
iOS
使用 METALAngle 库,而不是 GLKit,Apple 已弃用它,最终将不再支持。
创建一个 Objective C++ 渲染器类来调用您的 C++ OpenGL ES 绘图逻辑。
创建一个从 MGLKView 派生的视图类。在视图类的 drawRect() 函数中,创建一个渲染器对象(如果它尚不存在),然后调用它的绘图函数。也就是说,你的 drawRect 函数应该是这样的:
-(void)drawRect:(CGRect)rect
{
if (m_renderer == nil && m_my_other_data != nil)
m_renderer = [[MyRenderer alloc] init:m_my_other_data];
if (m_renderer)
[m_renderer draw];
}
在您的应用中,您需要一个视图控制器类来创建 OpenGL 上下文并对其进行设置,使用如下代码:
MGLContext* opengl_context = [[MGLContext alloc] initWithAPI:kMGLRenderingAPIOpenGLES2];
m_view = [[MyView alloc] initWithFrame:aBounds context:opengl_context];
m_view.drawableDepthFormat = MGLDrawableDepthFormat24;
self.view = m_view;
self.preferredFramesPerSecond = 30;
Linux
在 Linux 上使用 Qt 是最简单的(见上文),但也可以使用 GLFW 框架。在您的应用程序类的构造函数中,调用 glfwCreateWindow 创建一个窗口并将其存储为数据成员。调用 glfwMakeContextCurrent 使 EGL 上下文当前,然后创建一个数据成员来保存渲染器类的实例;像这样:
m_window = glfwCreateWindow(1024,1024,"My Window Title",nullptr,nullptr);
glfwMakeContextCurrent(m_window);
m_renderer = std::make_unique<CMyRenderer>();
在你的应用类中添加一个 Draw 函数:
bool MapWindow::Draw()
{
if (glfwWindowShouldClose(m_window))
return false;
m_renderer->Draw();
/* Swap front and back buffers */
glfwSwapBuffers(m_window);
return true;
}
你的 main() 函数将是:
int main(void)
{
/* Initialize the library */
if (!glfwInit())
return -1;
// Create the app.
MyApp app;
/* Draw continuously until the user closes the window */
while (app.Draw())
{
/* Poll for and process events */
glfwPollEvents();
}
glfwTerminate();
return 0;
}
着色器不兼容
各种 OpenGL ES 2.0 实现所接受的着色器语言存在不兼容性。我在我的 CompileShader 函数中使用以下条件编译代码在 C++ 代码中克服了这些问题:
const char* preamble = "";
#if defined(_POSIX_VERSION) && !defined(ANDROID) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
// for Ubuntu using Qt or GLFW
preamble = "#version 100\n";
#elif defined(USING_QT) && defined(__APPLE__)
// On the Mac #version doesn't work so the precision qualifiers are suppressed.
preamble = "#define lowp\n#define mediump\n#define highp\n";
#endif
preamble 然后作为着色器代码的前缀。