如何在X-Plane 12窗口中嵌入Chromium Embedded Framework实例展示HTML页面?
在X-Plane 12中嵌入CEF显示HTML的解决方案
核心思路
不要尝试直接嵌入CEF顶层窗口,必须采用CEF离屏渲染模式,将CEF的渲染帧输出为像素数据,再在X-Plane插件窗口的绘制回调中将像素数据渲染为OpenGL纹理。之前的失败大概率是CEF初始化未适配X-Plane的消息循环,或纹理同步、格式转换环节出错。
分步实现
1. 环境准备
- 使用CEF官方预编译二进制包(匹配你的编译器架构,如x64 + MSVC 2022),避免自行编译CEF引发版本兼容问题。
- 确保X-Plane SDK的编译选项与CEF一致:启用C++17或更高标准,使用多线程DLL运行时(/MD),目标架构设为x64。
2. CEF初始化适配X-Plane
X-Plane插件为DLL,不能直接调用CefRunMessageLoop(),需将CEF消息处理集成到X-Plane主循环:
// 在XPluginStart中初始化CEF int XPluginStart(char *outName, char *outSig, char *outDesc) { CefSettings settings; settings.windowless_rendering_enabled = true; // 必须开启离屏渲染 settings.multi_threaded_message_loop = true; // 让CEF在后台线程处理消息 if (!CefInitialize(CefCommandLine::CreateEmpty(), settings, nullptr, nullptr)) { return 0; } // 创建CEF浏览器实例(离屏模式) CefWindowInfo window_info; window_info.SetAsWindowless(0); // 0表示无父窗口,离屏渲染模式 CefBrowserSettings browser_settings; CefBrowserHost::CreateBrowser(window_info, new MyRenderHandler(), "https://example.com", browser_settings, nullptr, nullptr); return 1; } // 在XPluginReceiveMessage中处理CEF消息循环 void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void *inParam) { if (inMsg == XPLM_MSG_DRAW) { CefDoMessageLoopWork(); // 每次绘制帧时处理CEF的消息任务 } }
3. 自定义RenderHandler处理渲染数据
实现CefRenderHandler子类,获取CEF的像素数据并同步到X-Plane的OpenGL纹理:
class MyRenderHandler : public CefRenderHandler { public: // 设置CEF的渲染区域大小(与X-Plane窗口客户区一致) void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override { int x, y, w, h; XPLMGetWindowGeometry(gMyWindowID, &x, &y, &w, &h); rect = CefRect(0, 0, w, h); } // CEF渲染完成时回调,获取像素数据更新纹理 void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height) override { // 确保在X-Plane的OpenGL上下文里更新纹理 if (!gTextureID) { glGenTextures(1, &gTextureID); glBindTexture(GL_TEXTURE_2D, gTextureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } glBindTexture(GL_TEXTURE_2D, gTextureID); // CEF输出的是BGRA格式,转换为OpenGL兼容的RGBA glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer); } IMPLEMENT_REFCOUNTING(MyRenderHandler); };
4. X-Plane窗口绘制回调中渲染纹理
创建X-Plane窗口时,在绘制回调中绑定纹理并绘制:
void MyWindowDrawCallback(XPLMWindowID inWindowID, void *inRefcon) { int x, y, w, h; XPLMGetWindowGeometry(inWindowID, &x, &y, &w, &h); // 启用2D渲染模式 XPLMSetGraphicsState(0, 0, 0, 1, 1, 0, 0); glBindTexture(GL_TEXTURE_2D, gTextureID); // 绘制纹理到窗口客户区 glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(x, y); glTexCoord2f(1, 0); glVertex2i(x + w, y); glTexCoord2f(1, 1); glVertex2i(x + w, y + h); glTexCoord2f(0, 1); glVertex2i(x, y + h); glEnd(); } // 创建X-Plane窗口 gMyWindowID = XPLMCreateWindow(100, 100, 400, 300, 1, MyWindowDrawCallback, nullptr, nullptr, nullptr);
5. 编译错误排查
- 链接错误(LNK2019/LNK1120):检查是否添加了CEF的链接库(
libcef.lib、cef_sandbox.lib),库的架构与项目是否一致(x64)。 - 编译错误(C2039/C2664):确保CEF头文件包含顺序正确(先包含CEF头,再包含X-Plane SDK头),启用C++17或更高标准。
- 纹理显示异常:检查CEF的像素格式(BGRA)与OpenGL纹理格式(GL_BGRA)是否匹配,窗口大小变化时需调用
browser->GetHost()->WasResized()触发CEF重绘。
插件退出清理
在XPluginStop中正确销毁CEF资源:
void XPluginStop() { // 销毁CEF浏览器实例 if (gBrowser) { gBrowser->GetHost()->CloseBrowser(true); gBrowser = nullptr; } CefShutdown(); // 销毁OpenGL纹理 if (gTextureID) { glDeleteTextures(1, &gTextureID); } }
内容的提问来源于stack exchange,提问作者mattip
相关产品推荐
相关产品推荐

