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

Kivy技术问题:如何在指定布局内正确渲染3D模型

解决Kivy 3D渲染器覆盖UI控件的问题

我刚帮你排查了这个问题,核心原因是原3Drendering示例里的Renderer类默认使用了根窗口的画布(Window.canvas)来渲染3D内容,而不是绑定到自己控件的画布上,所以才会直接覆盖所有UI控件。咱们一步步来修复:

1. 修改Renderer类的渲染目标

打开renderer.py,找到Renderer类的初始化方法,去掉对Window.canvas的绑定,改用控件自身的画布:

原示例中的问题代码片段:

from kivy.core.window import Window

class Renderer(Widget):
    def __init__(self, **kwargs):
        super(Renderer, self).__init__(**kwargs)
        self.canvas = Window.canvas  # 这行导致渲染内容脱离控件范围
        # 后续的画布指令、着色器初始化...

修改后的代码:

from kivy.core.window import Window

class Renderer(Widget):
    def __init__(self, **kwargs):
        super(Renderer, self).__init__(**kwargs)
        # 删掉 self.canvas = Window.canvas,使用控件默认的self.canvas
        # 重新绑定原示例中的画布渲染指令
        self.canvas.add(Color(1, 1, 1, 1))
        self.canvas.add(Callback(self.setup_gl_context))
        self.canvas.add(Callback(self.setup_scene))
        self._instructions = InstructionGroup()
        self.canvas.add(self._instructions)
        self.canvas.add(Callback(self.draw_scene))
        self.canvas.add(Callback(self.reset_gl_context))
        
        # 绑定控件尺寸/位置变化事件,用于动态更新投影和视口
        self.bind(size=self.update_viewport_projection, pos=self.update_viewport_projection)
        # 初始化投影矩阵
        self.update_viewport_projection()

2. 适配控件尺寸的视口与投影矩阵

原示例的视口和投影是基于整个窗口的,现在要改成基于Renderer控件的大小和位置:

新增update_viewport_projection方法

在Renderer类里添加这个方法,用于根据控件尺寸计算正确的投影矩阵:

def update_viewport_projection(self, *args):
    # 计算宽高比,避免除以0的情况
    aspect_ratio = self.width / float(self.height) if self.height != 0 else 1.0
    # 生成适配控件的投影矩阵(逻辑和原示例一致,仅替换宽高比来源)
    self.projection_mat = Matrix().view_clip(
        -aspect_ratio, aspect_ratio,
        -1, 1,
        1, 100,
        0
    )

修改视口设置

找到原示例中调用glViewport的地方(通常在setup_gl_context函数里),把窗口尺寸替换为控件的位置和大小:

原代码:

def setup_gl_context(self, *args):
    glViewport(0, 0, Window.width, Window.height)
    # 其他GL上下文设置...

修改后:

def setup_gl_context(self, *args):
    # Kivy坐标以左下角为原点,和GL视口坐标规则一致
    glViewport(
        int(self.x), int(self.y),
        int(self.width), int(self.height)
    )
    # 其他GL设置保持不变...

3. 更新着色器的投影矩阵引用

在draw_scene函数里,把原示例中硬编码的投影矩阵替换成我们动态计算的self.projection_mat:

原代码片段:

def draw_scene(self, *args):
    # 原示例直接基于窗口尺寸计算投影
    aspect = Window.width / float(Window.height)
    proj = Matrix().view_clip(-aspect, aspect, -1, 1, 1, 100, 0)
    self.shader['projection_mat'] = proj
    # 其他渲染逻辑...

修改后:

def draw_scene(self, *args):
    # 使用动态计算的适配控件的投影矩阵
    self.shader['projection_mat'] = self.projection_mat
    # 其他渲染逻辑保持不变...

验证效果

修改完成后重新运行test_renderer.py:

  • 3D模型会被限制在Splitter内部的Renderer控件区域里,不会覆盖左侧的Label和Button
  • 调整Splitter的宽度时,模型会自动适配控件尺寸,不会出现变形

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:42:30