1
0
forked from Rowland/EG

vr画面抖动修复

This commit is contained in:
Rowland 2025-09-16 14:11:46 +08:00
parent 11cbefc905
commit 86b38bcfb3
2 changed files with 124 additions and 16 deletions

View File

@ -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")
@ -1356,3 +1434,34 @@ class VRManager(DirectObject):
object_node: 要标记的对象节点
"""
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行为")

@ -1 +0,0 @@
Subproject commit 3f9567897552df6c10078bc124795101cf478f91