You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:35:05