Kivy3点击3D对象触发函数实现:正俯视相机下的坐标转换问题
问题核心偏差定位
你之前手写坐标转换逻辑存在三个关键偏差,导致投影位置和实际渲染位置无法对齐:
- 轴映射错位:当前为正俯视相机,视轴沿世界Y轴方向,此时屏幕水平方向对应世界X轴、屏幕垂直方向对应世界Z轴,之前代码中用世界Y轴差值做三角函数分母的计算逻辑,混淆了相机视轴的参考方向
- 缩放系数不匹配:手写的fov换算逻辑没有对齐Kivy3透视投影的实际视锥范围,导致线性偏移存在固定比例误差
- 边界计算粗糙:手动给对象坐标加10偏移来模拟边界的方式,没有对齐对象实际的包围盒尺寸,无法准确匹配投影范围
正俯视场景下的实时坐标转换实现
因为当前相机为完全正俯视视角(无倾斜、无旋转),坐标映射为严格的线性关系,不需要复杂的矩阵连乘即可实现精准转换:
基准参数对齐
先明确固定俯视的相机坐标系约定:
- 相机沿世界Y轴正方向放置,视线朝向世界Y轴负方向,完全垂直向下
- 相机视锥中心线上的空间点,投影后固定落在控件的几何中心位置(
(width/2, height/2)) - 视场角fov参数和Kivy3 Renderer初始化时传入的参数完全一致
投影计算逻辑
按照透视投影的基本原理推导映射关系:
- 计算对象到相机的垂直深度(正俯视下深度值恒为Y轴方向的差值):
depth = camera.pos.y - obj.pos.y - 将fov值转换为弧度单位,计算对应深度处的可视范围:
- 垂直可视总高度:
visible_height = 2 * depth * tan(fov/2 * π/180) - 水平可视总宽度:
visible_height * (控件宽度 / 控件高度)(匹配透视投影的宽高比)
- 垂直可视总高度:
- 计算对象的屏幕投影坐标:
# 导入需要的数学计算模块 import math cam_pos = self.camera.pos obj_pos = obj.pos # 深度计算 depth = cam_pos.y - obj_pos.y fov_rad = math.radians(self.camera.fov) # 对应深度下的可视范围 visible_height = 2 * depth * math.tan(fov_rad / 2) visible_width = visible_height * (self.width / self.height) # 转换为屏幕坐标 screen_x = self.width / 2 + ( (obj_pos.x - cam_pos.x) / visible_width ) * self.width screen_y = self.height / 2 - ( (obj_pos.z - cam_pos.z) / visible_height ) * self.height
注意:Kivy坐标系的Y轴原点在控件左下角,向上为正;如果你的世界Z轴正方向是指向屏幕上方的,需要对计算值做符号对齐,否则投影位置会上下颠倒
点击检测适配
你所有对象为固定10×10×10的立方体,正俯视下的屏幕投影为正方形,可直接通过像素距离做判定:
- 计算对象投影对应的屏幕半尺寸像素值:
obj_half_size_pix = (5 / visible_height) * self.height(对象半长为5单位,换算为深度下的像素占比) - 当点击坐标与对象投影中心的差值同时满足以下两个条件时,判定为点击命中:
abs(touch_x - screen_x) <= obj_half_size_pixabs(touch_y - screen_y) <= obj_half_size_pix
- 命中后直接触发绑定的对应函数即可
通用适配方案(不局限正俯视场景)
如果后续需要调整相机角度、不再保持纯正俯视,可直接复用Kivy3 Renderer内部缓存的视图、投影矩阵完成转换,避免自行推导公式带来的偏差:
- 取每帧渲染时生成的视图矩阵V、投影矩阵P,相乘得到VP矩阵
- 将3D对象的齐次坐标
[x, y, z, 1]左乘VP矩阵,得到裁剪空间坐标 - 对裁剪空间坐标做透视除法,将x、y、z分量除以w分量,得到范围在[-1, 1]的标准化设备坐标(NDC)
- 将NDC坐标线性映射为Kivy屏幕坐标:
screen_x = (ndc_x + 1) / 2 * self.width screen_y = (ndc_y + 1) / 2 * self.height
这种计算逻辑和Kivy3实际渲染的坐标管线完全对齐,无论相机位置、角度如何调整,都能输出精准的投影坐标。
内容的提问来源于stack exchange,提问作者GreenJon902
相关产品推荐
相关产品推荐

