如何在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
相关产品推荐
相关产品推荐

