126 lines
3.5 KiB
Markdown
126 lines
3.5 KiB
Markdown
# 射线显示坐标系统修复说明
|
||
|
||
## 🔍 问题描述
|
||
|
||
用户发现射线显示时是从场景中心点射出,而不是从相机位置射出。这违反了鼠标点击射线的基本原理。
|
||
|
||
## ⚡ 问题原因
|
||
|
||
### 1. **坐标系混淆**
|
||
```python
|
||
# 原始错误代码
|
||
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. **正确的坐标变换**
|
||
```python
|
||
# 获取相机坐标系中的射线点
|
||
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. **碰撞检测保持不变**
|
||
```python
|
||
# 碰撞检测仍使用相机坐标系(正确!)
|
||
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. **启动射线测试**:
|
||
```bash
|
||
python demo/ray_display_test.py
|
||
```
|
||
|
||
2. **按R键开启射线显示**
|
||
|
||
3. **移动相机到不同位置**
|
||
|
||
4. **点击鼠标观察射线**:
|
||
- 射线应该从当前相机位置开始
|
||
- 射线应该指向鼠标点击的方向
|
||
- 相机移动后射线起点应该跟随变化
|
||
|
||
## 📋 技术要点
|
||
|
||
### **关键API使用**
|
||
```python
|
||
# 获取相机坐标系中的射线
|
||
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)
|
||
```
|
||
|
||
这样您就可以清楚地看到射线现在是从真实的相机位置发射,而不是从场景中心发射了!🎯 |