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

如何在GTK GLArea中集成AIS_INTERACTIVE_CONTEXT渲染OCCT模型

在Glade加载的GtkGLArea中集成OCCT AIS_InteractiveContext实现3D渲染

问题背景

需要将AIS_InteractiveContext嵌入到Gtk窗口内已有的、从Glade文件加载的GLArea控件中实现3D模型显示,参考独立窗口渲染的OCCT GTK样例编写的基础代码存在逻辑错误,无法正常运行,需要补全正确实现逻辑。
现有初始代码如下:

static gboolean render(GtkGLArea *area, GdkGLContext *context)
{
  glClearColor(1, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);

  return TRUE;
}

static void realize(GtkGLArea *area, GdkGLContext *context)
{
  gtk_gl_area_make_current(GTK_GL_AREA(area));
}

GdkGLContext *create_context(GtkGLArea *area, GdkGLContext *context)
{
  GdkGLContext *new_context;
  Handle(OpenGl_Context) aGlCtx = new OpenGl_Context();

  new_context = Handle(GdkGLContext *) myContext; // myContext的类型为AIS_INTERACTIVE_CONTEXT

  std::cout << "kjdnkcl" << std::endl;

  return new_context;
}

int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);

OSD::SetSignal(false);
OSD::SetSignalStackTraceLength(10);

OSD_Environment aBackend("GDK_BACKEND");
aBackend.SetValue("x11");
aBackend.Build();

Handle(Aspect_DisplayConnection) aDisp = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false);
// 交由Gtk::GLArea管理缓冲区交换
aDriver->ChangeOptions().buffersNoSwap = true;
// 不写入Alpha通道
aDriver->ChangeOptions().buffersOpaqueAlpha = true;
// 始终使用离屏FBO
aDriver->ChangeOptions().useSystemBuffer = false;

// 创建查看器
myViewer = new V3d_Viewer(aDriver);
myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);
myViewer->SetDefaultLights();
myViewer->SetLightOn();
myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines);

// 创建AIS交互上下文
myContext = new AIS_InteractiveContext(myViewer);

myViewCube = new AIS_ViewCube();
myViewCube->SetFixedAnimationLoop(false);
myViewCube->SetAutoStartAnimation(true);

// 注意:窗口将在后续onGlAreaRealized()回调中创建
myView = myViewer->CreateView();
myView->SetImmediateUpdate(false);
myView->ChangeRenderingParams().ToShowStats = true;
myView->ChangeRenderingParams().CollectedStats = 
(Graphic3d_RenderingParams::PerfCounters) 
(Graphic3d_RenderingParams::PerfCounters_FrameRate | 
Graphic3d_RenderingParams::PerfCounters_Triangles);

myContext->Display(myViewCube, 0, 0, false);

{
 // 测试用示例模型
 TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape();
 Handle(AIS_Shape) aShape = new AIS_Shape(aBox);
 myContext->Display(aShape, AIS_Shaded, 0, false);
}

GError *err = NULL;
ui_builder = gtk_builder_new();

gtk_builder_add_from_file(ui_builder, "./GLAreaWindow.glade", NULL);

window = GTK_WIDGET(gtk_builder_get_object(ui_builder, "Window"));
gl_area = GTK_WIDGET(gtk_builder_get_object(ui_builder, "GLArea"));

g_signal_connect(gl_area, "render", G_CALLBACK(render), NULL);
g_signal_connect(gl_area, "realize", G_CALLBACK(realize), NULL);
g_signal_connect(gl_area, "create-context", G_CALLBACK(create_context), NULL);

gtk_builder_connect_signals(ui_builder, window);

gtk_widget_show_all(window);

gtk_main();

return 0;
}

原有代码核心错误

  • create_context 回调逻辑完全错误:AIS_InteractiveContext和GdkGLContext是完全不相关的类型,强转返回会直接导致程序崩溃,该回调的作用是自定义GLArea使用的OpenGL上下文,而非传入OCCT的交互上下文
  • 未给OCCT的V3d_View绑定GLArea对应的原生渲染窗口,OCCT没有渲染输出目标
  • render回调仅做了缓冲区清空操作,没有调用OCCT的渲染接口,无法绘制加载的3D模型
  • 未处理GDK GL上下文和OCCT OpenGL上下文的资源共享逻辑,会导致shader、纹理等渲染资源不互通,渲染结果异常

正确实现步骤

1. 修正全局变量声明

先统一声明需要跨回调使用的OCCT对象句柄,避免类型不匹配问题:

Handle(V3d_Viewer) myViewer;
Handle(V3d_View) myView;
Handle(AIS_InteractiveContext) myContext;
Handle(OpenGl_GraphicDriver) aDriver;
GtkWidget* gl_area;
GtkWidget* window;
GtkBuilder* ui_builder;
Handle(AIS_ViewCube) myViewCube;

2. 重写create-context回调

该回调不需要返回自定义的OCCT对象,只需要先创建GLArea默认的OpenGL上下文,拿到上下文原生句柄后完成OCCT图形驱动的共享上下文绑定即可,最终返回GDK创建的GL上下文:

GdkGLContext* create_context(GtkGLArea* area, gpointer user_data)
{
    // 创建GLArea默认的3.3核心版GL上下文
    GdkGLContext* ctx = gtk_gl_area_create_context(area);
    gdk_gl_context_set_required_version(ctx, 3, 3);
    gdk_gl_context_set_use_es(ctx, FALSE);
    gdk_gl_context_make_current(ctx);

    // 拿到原生GL上下文句柄,初始化OCCT的GL上下文做资源共享
    GLContext* nativeCtx = (GLContext*)gdk_gl_context_get_cogl_context(ctx)->gl_context;
    Handle(OpenGl_Context) occtGlCtx = new OpenGl_Context();
    occtGlCtx->Init(NULL);
    occtGlCtx->SetSharedContext(nativeCtx);
    aDriver->SetSharedContext(occtGlCtx);

    return ctx;
}

3. 重写realize回调

GLArea完成初始化后,将GLArea的原生窗口包装成OCCT可识别的Aspect_Window,绑定到V3d_View,完成视口初始化:

static void realize(GtkGLArea* area, gpointer user_data)
{
    gtk_gl_area_make_current(area);
    GtkAllocation alloc;
    gtk_widget_get_allocation(GTK_WIDGET(area), &alloc);

    // 拿到X11窗口ID(适配代码中强制指定的x11后端)
    Window xwin = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(area)));
    Handle(Aspect_DisplayConnection) disp = new Aspect_DisplayConnection();
    Handle(Xw_Window) occtWin = new Xw_Window(disp, xwin, alloc.width, alloc.height);
    
    // 绑定窗口到视图
    if (!occtWin->IsMapped()) occtWin->Map();
    myView->SetWindow(occtWin);
    myView->SetViewport(0, 0, alloc.width, alloc.height);
    // 初始视图适配模型尺寸
    myView->FitAll();
    myView->Redraw();
}

4. 重写render回调

替换原有手动清屏逻辑,直接调用OCCT视图的重绘接口完成所有3D内容渲染:

static gboolean render(GtkGLArea* area, GdkGLContext* context, gpointer user_data)
{
    gtk_gl_area_make_current(area);
    // 触发OCCT渲染所有已加载的AIS对象
    myView->Redraw();
    // 交由GLArea处理缓冲区交换,不要手动调用glFlush/swapbuffers
    return TRUE;
}

5. 补充必要的事件回调

  • 监听resize事件:GLArea尺寸变化时,重新调用myView->SetViewport()更新视口尺寸,触发视图重绘
  • 监听鼠标、键盘事件:将事件坐标、按键参数转换后转发给myContext的对应交互接口(比如MoveTo、Select、StartRotation等方法),实现模型旋转、缩放、选中、ViewCube点击等交互能力
  • 监听unrealize事件:程序退出时正确释放OCCT的视图、上下文资源,避免内存泄漏

注意事项

  • 代码中指定的GL版本要和OCCT编译时指定的GL版本匹配,常规使用3.3核心版即可
  • 如果切换到Wayland后端,需要将Xw_Window替换为对应的Wayland窗口包装类,原有代码已经强制设置GDK_BACKEND=x11,可直接使用X11窗口接口
  • 开启buffersNoSwap选项后不要手动调用缓冲区交换接口,否则会出现画面撕裂、重复绘制问题

内容的提问来源于stack exchange,提问作者XoDefender

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 09:45:32