diff --git a/core/vr_manager.py b/core/vr_manager.py index f16cb340..7a28e5f8 100644 --- a/core/vr_manager.py +++ b/core/vr_manager.py @@ -60,7 +60,8 @@ class VRManager(DirectObject): self.hmd_pose = Mat4.identMat() self.controller_poses = {} self.tracked_device_poses = [] - self.poses = None # OpenVR姿态数组 + self.poses = None # OpenVR渲染姿态数组 + self.game_poses = None # OpenVR游戏逻辑姿态数组 # VR渲染参数 self.eye_width = 1080 @@ -95,6 +96,13 @@ class VRManager(DirectObject): # 帧同步标记 - 修复ATW闪烁 self._poses_updated_this_frame = False + # 姿态缓存 - 修复时序不匹配 + self._cached_render_poses = None # 用于渲染的缓存姿态 + self._first_frame = True # 首帧标记 + + # ATW控制选项 - 备选方案 + self.disable_async_reprojection = False # 是否禁用异步重投影 + # VR手柄控制器 self.left_controller = None self.right_controller = None @@ -168,10 +176,11 @@ class VRManager(DirectObject): self.eye_width, self.eye_height = self.vr_system.getRecommendedRenderTargetSize() print(f"✓ VR渲染目标尺寸: {self.eye_width}x{self.eye_height}") - # 创建OpenVR姿态数组 + # 创建OpenVR姿态数组 - 分离渲染和游戏姿态 poses_t = openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount - self.poses = poses_t() - print("✓ VR姿态数组已创建") + self.poses = poses_t() # 渲染姿态(预测的) + self.game_poses = poses_t() # 游戏逻辑姿态(当前的) + print("✓ VR渲染和游戏姿态数组已创建") # 创建VR渲染缓冲区 if not self._create_vr_buffers(): @@ -194,6 +203,10 @@ class VRManager(DirectObject): if not self.interaction_manager.initialize(): print("⚠️ VR交互系统初始化失败,但VR系统将继续运行") + # 可选:禁用异步重投影(备选方案) + if self.disable_async_reprojection: + self._disable_async_reprojection() + # 启动VR更新任务 self._start_vr_task() @@ -466,14 +479,14 @@ class VRManager(DirectObject): self.pose_failures += 1 def _wait_get_poses_immediate(self): - """立即获取VR姿态 - 修复ATW闪烁的关键方法""" + """立即获取VR姿态 - 修复ATW闪烁的关键方法(双姿态版本)""" try: - if not self.vr_compositor or not self.poses: + if not self.vr_compositor or not self.poses or not self.game_poses: return - # 使用0预测时间来避免双重预测,立即获取当前姿态 - # 这是修复ATW闪烁的关键:在渲染前立即获取,避免时序错误 - result = self.vr_compositor.waitGetPoses(self.poses, None) + # 关键修复:传递渲染姿态和游戏姿态数组 + # 渲染姿态用于绘制,游戏姿态用于逻辑,避免ATW过度补偿 + result = self.vr_compositor.waitGetPoses(self.poses, self.game_poses) # 检查姿态数据的有效性 valid_poses = 0 @@ -498,9 +511,10 @@ class VRManager(DirectObject): valid_poses += 1 # 调试信息 - 仅在第一次成功时输出 - if not hasattr(self, '_immediate_mode_logged') and valid_poses > 0: - print(f"✅ 立即姿态获取模式启用 - 有效姿态数: {valid_poses}") - self._immediate_mode_logged = True + if not hasattr(self, '_dual_pose_mode_logged') and valid_poses > 0: + print(f"✅ 双姿态立即获取模式启用 - 有效姿态数: {valid_poses}") + print(" 渲染姿态用于绘制,游戏姿态用于逻辑,防止ATW过度补偿") + self._dual_pose_mode_logged = True except Exception as e: # 限制错误输出频率 @@ -513,6 +527,33 @@ class VRManager(DirectObject): self.pose_failures += 1 + def _cache_poses_for_next_frame(self): + """缓存当前姿态供下一帧渲染使用 - 修复时序不匹配""" + try: + if not self.poses or len(self.poses) == 0: + return + + # 如果是第一帧,直接使用当前姿态 + if self._first_frame: + self._cached_render_poses = self.poses + self._first_frame = False + print("✓ 首帧姿态缓存已设置") + return + + # 复制当前渲染姿态到缓存 + # 下一帧将使用这些姿态进行渲染 + poses_t = openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount + cached_poses = poses_t() + + # 复制姿态数据 + for i in range(len(self.poses)): + cached_poses[i] = self.poses[i] + + self._cached_render_poses = cached_poses + + except Exception as e: + print(f"⚠️ 姿态缓存失败: {e}") + def _reset_frame_flag(self, task): """重置帧标记 - 确保下一帧可以更新姿态""" self._poses_updated_this_frame = False @@ -667,6 +708,37 @@ class VRManager(DirectObject): import traceback traceback.print_exc() + def _update_camera_poses_with_cache(self): + """使用缓存姿态更新相机 - 符合OpenVR时序假设""" + try: + # 使用缓存的姿态,符合OpenVR的时序假设 + # OpenVR假设你用上一次WaitGetPoses的姿态渲染当前提交的帧 + + if not self._cached_render_poses: + # 如果没有缓存姿态,回退到当前姿态(首帧情况) + print("⚠️ 没有缓存姿态,使用当前姿态") + return self._update_camera_poses() + + # 从缓存姿态数组中获取HMD姿态 + if len(self._cached_render_poses) > 0: + hmd_pose = self._cached_render_poses[openvr.k_unTrackedDeviceIndex_Hmd] + if hmd_pose.bPoseIsValid: + self.update_hmd(hmd_pose) + + # 调试信息 - 验证缓存姿态使用 + if not hasattr(self, '_cached_pose_logged'): + print("✅ 使用缓存姿态更新相机 - 符合OpenVR时序假设") + self._cached_pose_logged = True + else: + print("⚠️ 缓存的HMD姿态数据无效") + + except Exception as e: + print(f"使用缓存姿态更新相机失败: {e}") + # 回退到正常更新方式 + self._update_camera_poses() + import traceback + traceback.print_exc() + def _submit_frames_to_vr(self): """将渲染帧提交给VR系统""" try: @@ -897,7 +969,10 @@ class VRManager(DirectObject): # 在渲染开始前立即获取最新姿态,避免ATW双重预测 if not hasattr(self, '_poses_updated_this_frame') or not self._poses_updated_this_frame: self._wait_get_poses_immediate() - self._update_camera_poses() + # 缓存当前姿态供下一帧使用,符合OpenVR时序假设 + self._cache_poses_for_next_frame() + # 使用缓存的姿态更新相机(上一帧的姿态) + self._update_camera_poses_with_cache() self._poses_updated_this_frame = True # 在帧结束时重置标记 self.world.taskMgr.doMethodLater(0.001, self._reset_frame_flag, "reset_frame_flag") @@ -920,7 +995,10 @@ class VRManager(DirectObject): # 由于左右眼都会调用回调,确保每帧只调用一次WaitGetPoses if not hasattr(self, '_poses_updated_this_frame') or not self._poses_updated_this_frame: self._wait_get_poses_immediate() - self._update_camera_poses() + # 缓存当前姿态供下一帧使用,符合OpenVR时序假设 + self._cache_poses_for_next_frame() + # 使用缓存的姿态更新相机(上一帧的姿态) + self._update_camera_poses_with_cache() self._poses_updated_this_frame = True # 在帧结束时重置标记 self.world.taskMgr.doMethodLater(0.001, self._reset_frame_flag, "reset_frame_flag") @@ -1355,4 +1433,35 @@ class VRManager(DirectObject): Args: object_node: 要标记的对象节点 """ - self.interaction_manager._add_collision_to_object(object_node) \ No newline at end of file + self.interaction_manager._add_collision_to_object(object_node) + + def _disable_async_reprojection(self): + """禁用异步重投影 - 备选修复方案""" + try: + # 尝试通过OpenVR设置禁用异步重投影 + if hasattr(openvr, 'VRSettings'): + settings = openvr.VRSettings() + if settings: + # 禁用异步重投影 + error = settings.setBool("steamvr", "enableAsyncReprojection", False) + if error == openvr.VRSettingsError_None: + print("✅ 异步重投影已禁用") + else: + print(f"⚠️ 禁用异步重投影失败: 设置错误 {error}") + else: + print("⚠️ 无法获取VR设置接口") + else: + print("⚠️ OpenVR设置接口不可用") + + except Exception as e: + print(f"⚠️ 禁用异步重投影失败: {e}") + + def enable_async_reprojection_disable(self): + """启用异步重投影禁用选项 - 用户可调用的方法""" + self.disable_async_reprojection = True + print("📝 异步重投影禁用选项已启用,将在下次VR初始化时生效") + + def disable_async_reprojection_disable(self): + """禁用异步重投影禁用选项 - 恢复默认行为""" + self.disable_async_reprojection = False + print("📝 异步重投影禁用选项已关闭,将使用默认ATW行为") \ No newline at end of file diff --git a/参考文件/panda3d-openvr b/参考文件/panda3d-openvr deleted file mode 160000 index 3f956789..00000000 --- a/参考文件/panda3d-openvr +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f9567897552df6c10078bc124795101cf478f91