EG/demo/射线坐标系统修复说明.md
2025-07-10 09:19:51 +08:00

3.5 KiB
Raw Blame History

射线显示坐标系统修复说明

🔍 问题描述

用户发现射线显示时是从场景中心点射出,而不是从相机位置射出。这违反了鼠标点击射线的基本原理。

问题原因

1. 坐标系混淆

# 原始错误代码
nearPoint = Point3()
farPoint = Point3()
self.world.cam.node().getLens().extrude(Point2(mx, my), nearPoint, farPoint)

# 直接使用相机坐标系的点显示射线(错误!)
self.showClickRay(nearPoint, farPoint, hitPos)

2. 坐标系不匹配

  • lens.extrude() 返回的是相机坐标系中的点
  • 射线显示节点挂在 render 下,使用世界坐标系
  • 直接使用相机坐标系的点会导致射线从错误位置显示

🛠 修复方案

1. 正确的坐标变换

# 获取相机坐标系中的射线点
nearPoint = Point3()
farPoint = Point3()
self.world.cam.node().getLens().extrude(Point2(mx, my), nearPoint, farPoint)

# 转换到世界坐标系用于显示
worldNearPoint = self.world.render.getRelativePoint(self.world.cam, nearPoint)
worldFarPoint = self.world.render.getRelativePoint(self.world.cam, farPoint)

# 使用世界坐标系的点显示射线
self.showClickRay(worldNearPoint, worldFarPoint, hitPos)

2. 碰撞检测保持不变

# 碰撞检测仍使用相机坐标系(正确!)
pickerNode = CollisionNode('mouseRay')
pickerNP = self.world.cam.attachNewNode(pickerNode)  # 相机子节点
direction = farPoint - nearPoint
direction.normalize()
pickerNode.addSolid(CollisionRay(nearPoint, direction))  # 相机坐标系

📐 坐标系统详解

相机坐标系 vs 世界坐标系

坐标系 用途 特点
相机坐标系 碰撞检测 以相机为原点Z轴向前
世界坐标系 射线显示 以场景为原点,固定坐标

为什么需要不同坐标系?

  1. 碰撞检测

    • 碰撞节点是相机的子节点
    • 使用相机坐标系可以跟随相机移动
    • 射线方向相对于相机计算
  2. 射线显示

    • 射线节点是render的子节点
    • 需要在世界坐标系中显示固定位置
    • 从相机真实位置到点击点

🎯 修复效果

修复前

射线起点: (相机坐标系原点) = 场景中心附近
射线方向: 正确,但起点错误
显示效果: 射线从场景中心发射 ❌

修复后

射线起点: (相机世界位置) = 真实相机位置  
射线方向: 正确,指向鼠标点击方向
显示效果: 射线从相机位置发射 ✅

🧪 验证方法

  1. 启动射线测试

    python demo/ray_display_test.py
    
  2. 按R键开启射线显示

  3. 移动相机到不同位置

  4. 点击鼠标观察射线

    • 射线应该从当前相机位置开始
    • 射线应该指向鼠标点击的方向
    • 相机移动后射线起点应该跟随变化

📋 技术要点

关键API使用

# 获取相机坐标系中的射线
lens.extrude(screen_point, near_point, far_point)

# 坐标系转换
world_point = render.getRelativePoint(camera, camera_point)

# 反向转换
camera_point = camera.getRelativePoint(render, world_point)

调试输出

现在会显示两套坐标:

相机坐标系射线起点: (0, 1, 0)
世界坐标系射线起点: (-15.2, -42.3, 18.7)

这样您就可以清楚地看到射线现在是从真实的相机位置发射,而不是从场景中心发射了!🎯