VR #8

Merged
Rowland merged 8 commits from VR into main 2025-10-21 08:09:34 +00:00
6 changed files with 1656 additions and 56 deletions
Showing only changes of commit a8e27a56e9 - Show all commits

18
config/vr_settings.json Normal file
View File

@ -0,0 +1,18 @@
{
"render_mode": "normal",
"resolution_scale": 0.75,
"pipeline_resolution_scale": 0.75,
"quality_preset": "quality",
"pipeline_vr_config": {
"enable_shadows": true,
"enable_ao": true,
"enable_bloom": false,
"enable_motion_blur": false,
"enable_ssr": false,
"shadow_quality": "medium",
"ao_quality": "low"
},
"anti_aliasing": "4x",
"refresh_rate": "72Hz",
"async_reprojection": true
}

273
core/vr_config.py Normal file
View File

@ -0,0 +1,273 @@
"""
VR配置管理器模块
负责VR设置的保存加载和管理
"""
import os
import json
from pathlib import Path
class VRConfigManager:
"""VR配置管理器类"""
def __init__(self, config_dir=None):
"""初始化配置管理器
Args:
config_dir: 配置目录路径默认为项目目录/config
"""
if config_dir is None:
# 默认使用项目根目录下的config文件夹
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_dir = os.path.join(project_root, "config")
self.config_dir = Path(config_dir)
self.config_file = self.config_dir / "vr_settings.json"
# 确保配置目录存在
self.config_dir.mkdir(parents=True, exist_ok=True)
# 默认配置
self.default_config = {
"render_mode": "normal", # "normal" 或 "render_pipeline"
"resolution_scale": 0.75,
"pipeline_resolution_scale": 0.75,
"quality_preset": "balanced", # "performance", "balanced", "quality"
"anti_aliasing": "4x", # "无", "2x", "4x", "8x"
"refresh_rate": "90Hz", # "72Hz", "90Hz", "120Hz", "144Hz"
"async_reprojection": True, # 异步重投影开关
"pipeline_vr_config": {
"enable_shadows": True,
"enable_ao": True,
"enable_bloom": False,
"enable_motion_blur": False,
"enable_ssr": False,
"shadow_quality": "medium",
"ao_quality": "low"
}
}
def load_config(self):
"""加载VR配置
Returns:
dict: VR配置字典
"""
try:
if self.config_file.exists():
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
print(f"✓ VR配置已加载: {self.config_file}")
return config
else:
print(f"⚠️ 配置文件不存在,使用默认配置: {self.config_file}")
return self.default_config.copy()
except Exception as e:
print(f"❌ 加载VR配置失败: {e}")
print(" 使用默认配置")
return self.default_config.copy()
def save_config(self, config):
"""保存VR配置
Args:
config: VR配置字典
Returns:
bool: 保存是否成功
"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=4, ensure_ascii=False)
print(f"✓ VR配置已保存: {self.config_file}")
return True
except Exception as e:
print(f"❌ 保存VR配置失败: {e}")
import traceback
traceback.print_exc()
return False
def get_render_mode(self):
"""获取渲染模式配置
Returns:
str: 渲染模式 ("normal" "render_pipeline")
"""
config = self.load_config()
return config.get("render_mode", "normal")
def set_render_mode(self, mode):
"""设置渲染模式并保存
Args:
mode: 渲染模式字符串
Returns:
bool: 设置是否成功
"""
if mode not in ["normal", "render_pipeline"]:
print(f"❌ 无效的渲染模式: {mode}")
return False
config = self.load_config()
config["render_mode"] = mode
return self.save_config(config)
def get_resolution_scale(self):
"""获取分辨率缩放配置
Returns:
float: 分辨率缩放系数
"""
config = self.load_config()
return config.get("resolution_scale", 0.75)
def set_resolution_scale(self, scale):
"""设置分辨率缩放并保存
Args:
scale: 分辨率缩放系数 (0.5-1.0)
Returns:
bool: 设置是否成功
"""
if not 0.5 <= scale <= 1.0:
print(f"❌ 无效的分辨率缩放: {scale} (应在0.5-1.0之间)")
return False
config = self.load_config()
config["resolution_scale"] = scale
return self.save_config(config)
def get_quality_preset(self):
"""获取质量预设
Returns:
str: 质量预设名称
"""
config = self.load_config()
return config.get("quality_preset", "balanced")
def set_quality_preset(self, preset):
"""设置质量预设并保存
Args:
preset: 质量预设 ("performance", "balanced", "quality")
Returns:
bool: 设置是否成功
"""
if preset not in ["performance", "balanced", "quality"]:
print(f"❌ 无效的质量预设: {preset}")
return False
config = self.load_config()
config["quality_preset"] = preset
return self.save_config(config)
def get_pipeline_config(self):
"""获取RenderPipeline VR配置
Returns:
dict: Pipeline配置字典
"""
config = self.load_config()
return config.get("pipeline_vr_config", self.default_config["pipeline_vr_config"].copy())
def update_pipeline_config(self, pipeline_config):
"""更新RenderPipeline VR配置
Args:
pipeline_config: Pipeline配置字典
Returns:
bool: 更新是否成功
"""
config = self.load_config()
config["pipeline_vr_config"] = pipeline_config
return self.save_config(config)
def reset_to_defaults(self):
"""重置为默认配置
Returns:
bool: 重置是否成功
"""
return self.save_config(self.default_config.copy())
def apply_config_to_vr_manager(self, vr_manager):
"""将配置应用到VR管理器
Args:
vr_manager: VRManager实例
Returns:
bool: 应用是否成功
"""
try:
config = self.load_config()
# 应用渲染模式
render_mode = config.get("render_mode", "normal")
if render_mode == "render_pipeline":
from .vr_manager import VRRenderMode
vr_manager.vr_render_mode = VRRenderMode.RENDER_PIPELINE
else:
from .vr_manager import VRRenderMode
vr_manager.vr_render_mode = VRRenderMode.NORMAL
# 应用分辨率缩放
resolution_scale = config.get("resolution_scale", 0.75)
vr_manager.resolution_scale = resolution_scale
# 应用Pipeline分辨率缩放
pipeline_resolution_scale = config.get("pipeline_resolution_scale", 0.75)
vr_manager.pipeline_resolution_scale = pipeline_resolution_scale
# 应用质量预设
quality_preset = config.get("quality_preset", "balanced")
vr_manager.current_quality_preset = quality_preset
# 应用Pipeline配置
pipeline_config = config.get("pipeline_vr_config", {})
if pipeline_config:
vr_manager.pipeline_vr_config.update(pipeline_config)
print("✓ VR配置已应用到VR管理器")
return True
except Exception as e:
print(f"❌ 应用VR配置失败: {e}")
import traceback
traceback.print_exc()
return False
def save_from_vr_manager(self, vr_manager):
"""从VR管理器保存当前配置
Args:
vr_manager: VRManager实例
Returns:
bool: 保存是否成功
"""
try:
config = {
"render_mode": vr_manager.vr_render_mode.value,
"resolution_scale": vr_manager.resolution_scale,
"pipeline_resolution_scale": vr_manager.pipeline_resolution_scale,
"quality_preset": vr_manager.current_quality_preset,
"pipeline_vr_config": vr_manager.pipeline_vr_config.copy()
}
return self.save_config(config)
except Exception as e:
print(f"❌ 从VR管理器保存配置失败: {e}")
import traceback
traceback.print_exc()
return False

View File

@ -34,6 +34,13 @@ from .vr_actions import VRActionManager
from .vr_interaction import VRInteractionManager
from .vr_joystick import VRJoystickManager
from .vr_teleport import VRTeleportSystem
from enum import Enum
class VRRenderMode(Enum):
"""VR渲染模式枚举"""
NORMAL = "normal" # 普通渲染模式
RENDER_PIPELINE = "render_pipeline" # RenderPipeline高级渲染模式
class VRManager(DirectObject):
@ -113,6 +120,25 @@ class VRManager(DirectObject):
}
self.current_quality_preset = 'balanced' # 默认平衡模式
# 🎨 VR渲染模式配置 - RenderPipeline集成
self.vr_render_mode = VRRenderMode.NORMAL # 默认使用普通渲染模式
self.render_pipeline_enabled = False # RenderPipeline是否已启用
self.vr_pipeline_left_target = None # 左眼RenderPipeline渲染目标
self.vr_pipeline_right_target = None # 右眼RenderPipeline渲染目标
self.pipeline_resolution_scale = 0.75 # RenderPipeline模式下的分辨率缩放性能优化
self.vr_pipeline_controller = None # VR Pipeline控制器管理完整的VR渲染管线
# RenderPipeline VR优化配置
self.pipeline_vr_config = {
'enable_shadows': True, # 启用阴影
'enable_ao': True, # 启用环境光遮蔽
'enable_bloom': False, # 禁用泛光VR性能考虑
'enable_motion_blur': False, # 禁用运动模糊VR中会引起不适
'enable_ssr': False, # 禁用屏幕空间反射(性能密集)
'shadow_quality': 'medium', # 阴影质量low/medium/high
'ao_quality': 'low', # AO质量low/medium/high
}
# VR任务
self.vr_task = None
@ -215,6 +241,17 @@ class VRManager(DirectObject):
self.use_prediction_time = 0.011 # 11ms的预测时间 - OpenVR标准值平衡准确性和延迟
self.poses_updated_in_task = True # 始终在更新任务中获取姿态Running Start模式
# 🎨 初始化VR配置管理器
try:
from .vr_config import VRConfigManager
self.config_manager = VRConfigManager()
# 加载配置并应用
self.config_manager.apply_config_to_vr_manager(self)
print("✓ VR配置管理器初始化完成并已加载配置")
except Exception as e:
print(f"⚠️ VR配置管理器初始化失败: {e}")
self.config_manager = None
# 尝试导入性能监控库
self._init_performance_monitoring()
@ -530,10 +567,19 @@ class VRManager(DirectObject):
self.game_poses = poses_t() # 游戏逻辑姿态(当前的)
print("✓ VR渲染和游戏姿态数组已创建")
# 创建VR渲染缓冲区
if not self._create_vr_buffers():
print("❌ 创建VR渲染缓冲区失败")
return False
# 创建VR渲染缓冲区 - 根据渲染模式选择
print(f"🎨 VR渲染模式: {self.vr_render_mode.value}")
if self.vr_render_mode == VRRenderMode.RENDER_PIPELINE:
if not self._create_vr_buffers_with_pipeline():
print("⚠️ RenderPipeline模式创建失败回退到普通渲染模式")
self.vr_render_mode = VRRenderMode.NORMAL
if not self._create_vr_buffers():
print("❌ 创建VR渲染缓冲区失败")
return False
else:
if not self._create_vr_buffers():
print("❌ 创建VR渲染缓冲区失败")
return False
# 设置VR相机
if not self._setup_vr_cameras():
@ -692,12 +738,11 @@ class VRManager(DirectObject):
# 准备左眼纹理并缓存ID
if self.vr_left_texture:
print(f" 准备左眼纹理: {self.vr_left_texture.getName()}")
texture_context = self.vr_left_texture.prepareNow(0, prepared_objects, gsg)
if texture_context and hasattr(texture_context, 'getNativeId'):
self.left_texture_id = texture_context.getNativeId()
if self.left_texture_id > 0:
print(f" ✅ 左眼纹理ID缓存: {self.left_texture_id}")
print(f" ✅ 左眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.left_texture_id}")
else:
print(" ❌ 左眼纹理ID无效")
return False
@ -707,12 +752,11 @@ class VRManager(DirectObject):
# 准备右眼纹理并缓存ID
if self.vr_right_texture:
print(f" 准备右眼纹理: {self.vr_right_texture.getName()}")
texture_context = self.vr_right_texture.prepareNow(0, prepared_objects, gsg)
if texture_context and hasattr(texture_context, 'getNativeId'):
self.right_texture_id = texture_context.getNativeId()
if self.right_texture_id > 0:
print(f" ✅ 右眼纹理ID缓存: {self.right_texture_id}")
print(f" ✅ 右眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.right_texture_id}")
else:
print(" ❌ 右眼纹理ID无效")
return False
@ -798,6 +842,83 @@ class VRManager(DirectObject):
except Exception as e:
print(f"缓冲区诊断失败: {e}")
def _create_vr_buffers_with_pipeline(self):
"""创建带RenderPipeline的VR渲染缓冲区 - 高级渲染模式"""
try:
print(f"🎨 创建RenderPipeline VR缓冲区:")
print(f" 推荐分辨率: {self.base_eye_width}x{self.base_eye_height}")
print(f" Pipeline缩放: {self.pipeline_resolution_scale}")
# 检查RenderPipeline是否可用
if not hasattr(self.world, 'render_pipeline') or not self.world.render_pipeline:
print("❌ RenderPipeline未初始化无法使用高级渲染模式")
return False
pipeline = self.world.render_pipeline
# 计算RenderPipeline模式下的分辨率
pipeline_width = int(self.base_eye_width * self.pipeline_resolution_scale)
pipeline_height = int(self.base_eye_height * self.pipeline_resolution_scale)
print(f" 实际分辨率: {pipeline_width}x{pipeline_height}")
print(f" 像素减少: {(1 - self.pipeline_resolution_scale**2) * 100:.1f}%")
# 导入RenderPipeline相关模块
try:
from RenderPipelineFile.rpcore.render_target import RenderTarget
except ImportError as e:
print(f"❌ 无法导入RenderPipeline模块: {e}")
return False
# 导入VR stages模块
try:
from .vr_stages import VRPipelineController
except ImportError as e:
print(f"❌ 无法导入VR stages模块: {e}")
return False
# 创建VR Pipeline控制器
print(" 初始化VR Pipeline控制器...")
self.vr_pipeline_controller = VRPipelineController(pipeline)
# 保存分辨率信息将在_setup_vr_cameras中使用
self.pipeline_vr_width = pipeline_width
self.pipeline_vr_height = pipeline_height
# 应用RenderPipeline效果配置
self._apply_pipeline_vr_effects()
self.render_pipeline_enabled = True
print("✅ VR Pipeline控制器已初始化将在相机设置时创建完整管线")
return True
except Exception as e:
print(f"❌ 创建RenderPipeline VR缓冲区失败: {e}")
import traceback
traceback.print_exc()
return False
def _apply_pipeline_vr_effects(self):
"""为VR场景应用RenderPipeline效果配置"""
try:
print("🎨 应用RenderPipeline VR优化配置...")
# 根据配置调整RenderPipeline设置
config = self.pipeline_vr_config
# 注意:实际的插件启用/禁用需要在pipeline.yaml中配置
# 这里只是记录当前的VR优化意图
print(f" 阴影: {'启用' if config['enable_shadows'] else '禁用'}")
print(f" 环境光遮蔽: {'启用' if config['enable_ao'] else '禁用'}")
print(f" 泛光效果: {'启用' if config['enable_bloom'] else '禁用'} (VR推荐禁用)")
print(f" 运动模糊: {'启用' if config['enable_motion_blur'] else '禁用'} (VR推荐禁用)")
print(f" 屏幕空间反射: {'启用' if config['enable_ssr'] else '禁用'} (VR推荐禁用)")
print("✅ RenderPipeline VR效果配置完成")
except Exception as e:
print(f"⚠️ 应用RenderPipeline VR效果失败: {e}")
def _setup_vr_cameras(self):
"""设置VR相机 - 使用锚点层级系统"""
try:
@ -829,18 +950,90 @@ class VRManager(DirectObject):
self.vr_left_camera = self.left_eye_anchor.attachNewNode(left_cam_node)
self.vr_right_camera = self.right_eye_anchor.attachNewNode(right_cam_node)
# 设置显示区域使用标准渲染流程
left_dr = self.vr_left_eye_buffer.makeDisplayRegion()
left_dr.setCamera(self.vr_left_camera)
left_dr.setActive(True)
# 恢复DrawCallback以精确控制渲染时机
left_dr.setDrawCallback(PythonCallbackObject(self.simple_left_cb))
# 设置显示区域 - 区分RenderPipeline和普通模式
if self.vr_render_mode == VRRenderMode.RENDER_PIPELINE and self.vr_pipeline_controller:
# RenderPipeline模式使用VRPipelineController创建完整管线
print(" 使用VR Pipeline创建立体渲染管线...")
right_dr = self.vr_right_eye_buffer.makeDisplayRegion()
right_dr.setCamera(self.vr_right_camera)
right_dr.setActive(True)
# 恢复DrawCallback以精确控制渲染时机
right_dr.setDrawCallback(PythonCallbackObject(self.simple_right_cb))
# 创建立体渲染管线GBuffer + Lighting + Final stages
success = self.vr_pipeline_controller.create_stereo_pipeline(
self.pipeline_vr_width,
self.pipeline_vr_height,
self.vr_left_camera,
self.vr_right_camera
)
if not success:
print("❌ VR Pipeline创建失败")
return False
# 获取GBuffer的内部buffer用于DisplayRegion
self.vr_left_eye_buffer = self.vr_pipeline_controller.get_left_buffer()
self.vr_right_eye_buffer = self.vr_pipeline_controller.get_right_buffer()
# 获取最终输出纹理用于提交到OpenVR
left_textures = self.vr_pipeline_controller.get_left_textures()
right_textures = self.vr_pipeline_controller.get_right_textures()
if left_textures and right_textures:
self.vr_left_texture = left_textures["final"]
self.vr_right_texture = right_textures["final"]
else:
print("❌ 无法获取VR Pipeline输出纹理")
return False
# 设置缓冲区排序和清除颜色
if self.vr_left_eye_buffer:
self.vr_left_eye_buffer.setSort(-100)
self.vr_left_eye_buffer.setClearColorActive(True)
self.vr_left_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1))
if self.vr_right_eye_buffer:
self.vr_right_eye_buffer.setSort(-99)
self.vr_right_eye_buffer.setClearColorActive(True)
self.vr_right_eye_buffer.setClearColor((0.1, 0.2, 0.4, 1))
# 🔧 关键修复为RenderPipeline的GBuffer DisplayRegion设置DrawCallback
# 这确保纹理在渲染完成后被提交到OpenVR
if self.vr_left_eye_buffer and self.vr_right_eye_buffer:
print(" 设置RenderPipeline DisplayRegion回调...")
try:
# 获取RenderTarget创建的DisplayRegion
left_dr = self.vr_left_eye_buffer.get_display_region(0)
right_dr = self.vr_right_eye_buffer.get_display_region(0)
# 设置渲染回调(与普通模式一致)
left_dr.setDrawCallback(PythonCallbackObject(self.simple_left_cb))
right_dr.setDrawCallback(PythonCallbackObject(self.simple_right_cb))
# 确保DisplayRegion处于活动状态
left_dr.setActive(True)
right_dr.setActive(True)
print(" ✅ RenderPipeline DisplayRegion回调已设置")
except Exception as e:
print(f" ⚠️ 设置DisplayRegion回调失败: {e}")
import traceback
traceback.print_exc()
# 准备纹理并缓存OpenGL ID
if not self._prepare_and_cache_textures():
print("❌ RenderPipeline纹理准备失败")
return False
print("✅ VR Pipeline立体渲染管线已准备完成")
else:
# 普通模式创建新的DisplayRegion
print(" 使用普通渲染区域...")
left_dr = self.vr_left_eye_buffer.makeDisplayRegion()
left_dr.setCamera(self.vr_left_camera)
left_dr.setActive(True)
left_dr.setDrawCallback(PythonCallbackObject(self.simple_left_cb))
right_dr = self.vr_right_eye_buffer.makeDisplayRegion()
right_dr.setCamera(self.vr_right_camera)
right_dr.setActive(True)
right_dr.setDrawCallback(PythonCallbackObject(self.simple_right_cb))
print("✓ VR相机锚点层级系统设置完成")
return True
@ -1630,6 +1823,162 @@ class VRManager(DirectObject):
print("✅ VR模式已禁用手柄模型已隐藏")
def set_vr_render_mode(self, mode):
"""切换VR渲染模式
Args:
mode: VRRenderMode枚举值或字符串 ('normal' 'render_pipeline')
Returns:
bool: 切换是否成功
"""
try:
# 转换输入为枚举类型
if isinstance(mode, str):
mode_str = mode.lower()
if mode_str == "normal":
new_mode = VRRenderMode.NORMAL
elif mode_str in ["render_pipeline", "renderpipeline", "pipeline"]:
new_mode = VRRenderMode.RENDER_PIPELINE
else:
print(f"❌ 无效的渲染模式: {mode}")
print(" 支持的模式: 'normal''render_pipeline'")
return False
elif isinstance(mode, VRRenderMode):
new_mode = mode
else:
print(f"❌ 无效的模式类型: {type(mode)}")
return False
# 检查是否与当前模式相同
if new_mode == self.vr_render_mode:
print(f"✓ VR渲染模式已经是 {new_mode.value},无需切换")
return True
print(f"🔄 正在切换VR渲染模式: {self.vr_render_mode.value}{new_mode.value}")
# 检查VR是否已初始化
if not self.vr_initialized:
print("⚠️ VR未初始化仅更新渲染模式配置")
self.vr_render_mode = new_mode
print(f"✓ VR渲染模式已更新为 {new_mode.value}下次启动VR时生效")
return True
# 保存当前VR启用状态
was_enabled = self.vr_enabled
# 如果VR已启用先禁用
if was_enabled:
print(" 暂时禁用VR...")
self.disable_vr()
# 更新渲染模式
old_mode = self.vr_render_mode
self.vr_render_mode = new_mode
# 清理现有缓冲区
print(" 清理现有渲染缓冲区...")
self._cleanup_vr_buffers()
# 根据新模式重建缓冲区
print(f" 创建新的渲染缓冲区({new_mode.value}...")
success = False
if new_mode == VRRenderMode.RENDER_PIPELINE:
success = self._create_vr_buffers_with_pipeline()
if not success:
print("⚠️ RenderPipeline模式创建失败回退到普通渲染模式")
self.vr_render_mode = VRRenderMode.NORMAL
success = self._create_vr_buffers()
else:
success = self._create_vr_buffers()
if not success:
print("❌ 缓冲区创建失败,尝试恢复原模式")
self.vr_render_mode = old_mode
if old_mode == VRRenderMode.RENDER_PIPELINE:
self._create_vr_buffers_with_pipeline()
else:
self._create_vr_buffers()
return False
# 重新设置相机
print(" 重新设置VR相机...")
if not self._setup_vr_cameras():
print("❌ 相机设置失败")
return False
# 如果之前VR是启用的重新启用
if was_enabled:
print(" 重新启用VR...")
self.enable_vr()
print(f"✅ VR渲染模式已切换为 {self.vr_render_mode.value}")
# 保存配置
if self.config_manager:
self.config_manager.save_from_vr_manager(self)
return True
except Exception as e:
print(f"❌ 切换VR渲染模式失败: {e}")
import traceback
traceback.print_exc()
return False
def get_vr_render_mode(self):
"""获取当前VR渲染模式
Returns:
VRRenderMode: 当前渲染模式
"""
return self.vr_render_mode
def _cleanup_vr_buffers(self):
"""清理VR渲染缓冲区"""
try:
# 清理VR Pipeline Controller如果使用RenderPipeline模式
if self.vr_pipeline_controller:
print(" 清理VR Pipeline...")
self.vr_pipeline_controller.cleanup_all()
self.vr_pipeline_controller = None
# 清理左眼缓冲区
if self.vr_left_eye_buffer:
# 如果是RenderPipeline模式buffer已被VRPipelineController清理
# 如果是普通模式,需要手动清理
if self.vr_render_mode == VRRenderMode.NORMAL:
self.world.graphicsEngine.removeWindow(self.vr_left_eye_buffer)
self.vr_left_eye_buffer = None
# 清理右眼缓冲区
if self.vr_right_eye_buffer:
if self.vr_render_mode == VRRenderMode.NORMAL:
self.world.graphicsEngine.removeWindow(self.vr_right_eye_buffer)
self.vr_right_eye_buffer = None
# 清理纹理
self.vr_left_texture = None
self.vr_right_texture = None
self.left_texture_id = None
self.right_texture_id = None
self.textures_prepared = False
# 清理RenderPipeline渲染目标旧版保留兼容性
if self.vr_pipeline_left_target:
self.vr_pipeline_left_target = None
if self.vr_pipeline_right_target:
self.vr_pipeline_right_target = None
self.render_pipeline_enabled = False
print("✓ VR渲染缓冲区已清理")
except Exception as e:
print(f"⚠️ 清理VR缓冲区时出错: {e}")
def cleanup(self):
"""清理VR资源"""
try:
@ -3467,8 +3816,20 @@ class VRManager(DirectObject):
# 清理旧的缓冲区
self._cleanup_vr_buffers()
# 重新创建缓冲区
if self._create_vr_buffers():
# 🔧 关键修复:根据渲染模式选择创建方法
success = False
if self.vr_render_mode == VRRenderMode.RENDER_PIPELINE:
print(f" 使用RenderPipeline模式重建...")
success = self._create_vr_buffers_with_pipeline()
if not success:
print("⚠️ RenderPipeline模式创建失败回退到普通渲染模式")
self.vr_render_mode = VRRenderMode.NORMAL
success = self._create_vr_buffers()
else:
print(f" 使用普通模式重建...")
success = self._create_vr_buffers()
if success:
# 重新设置相机
self._setup_vr_cameras()
print("✅ VR缓冲区重新创建成功")
@ -3483,32 +3844,6 @@ class VRManager(DirectObject):
traceback.print_exc()
return False
def _cleanup_vr_buffers(self):
"""清理旧的VR缓冲区"""
try:
# 清理左眼缓冲区
if hasattr(self, 'vr_left_eye_buffer') and self.vr_left_eye_buffer:
self.vr_left_eye_buffer.removeAllDisplayRegions()
self.world.graphicsEngine.removeWindow(self.vr_left_eye_buffer)
self.vr_left_eye_buffer = None
# 清理右眼缓冲区
if hasattr(self, 'vr_right_eye_buffer') and self.vr_right_eye_buffer:
self.vr_right_eye_buffer.removeAllDisplayRegions()
self.world.graphicsEngine.removeWindow(self.vr_right_eye_buffer)
self.vr_right_eye_buffer = None
# 清理相机
if hasattr(self, 'vr_left_camera') and self.vr_left_camera:
self.vr_left_camera.removeNode()
self.vr_left_camera = None
if hasattr(self, 'vr_right_camera') and self.vr_right_camera:
self.vr_right_camera.removeNode()
self.vr_right_camera = None
except Exception as e:
print(f"⚠️ 清理VR缓冲区时出错: {e}")
def get_resolution_info(self):
"""获取分辨率相关信息"""

834
core/vr_stages.py Normal file
View File

@ -0,0 +1,834 @@
"""
VR专用渲染Stages
为VR创建简化但完整的渲染管线支持
- GBuffer生成延迟渲染基础
- 光照计算复用主Pipeline的LightManager
- 最终合成tone mapping + gamma校正
"""
from panda3d.core import Shader
from RenderPipelineFile.rpcore.render_target import RenderTarget
from RenderPipelineFile.rpcore.globals import Globals
from RenderPipelineFile.rpcore.loader import RPLoader
from RenderPipelineFile.rpcore.util.shader_input_blocks import SimpleInputBlock
def _load_rp_shader(shader_name):
"""
加载RenderPipeline shader
Args:
shader_name: shader文件名"apply_lights.frag.glsl"
Returns:
Shader对象
"""
default_vert = "/$$rp/shader/default_post_process.vert.glsl"
frag_path = f"/$$rp/shader/{shader_name}"
shader = RPLoader.load_shader(default_vert, frag_path)
if shader:
print(f" ✅ Shader加载成功: {shader_name}")
else:
print(f" ❌ Shader加载失败: {shader_name}")
return shader
class VRGBufferStage:
"""VR GBuffer阶段 - 为单个VR眼睛创建GBuffer"""
def __init__(self, name, pipeline):
"""
初始化VR GBuffer Stage
Args:
name: stage名称"VR_Left_GBuffer"
pipeline: RenderPipeline实例引用
"""
self.name = name
self.pipeline = pipeline
self.target = None
self._camera = None
def create(self, width, height, vr_camera):
"""
创建GBuffer渲染目标
Args:
width: 渲染宽度
height: 渲染高度
vr_camera: VR相机NodePath
"""
print(f"🎨 创建{self.name} GBuffer: {width}x{height}")
# 创建RenderTarget
self.target = RenderTarget(self.name)
self.target.size = width, height
# 添加GBuffer纹理附件与主GBufferStage相同
self.target.add_color_attachment(bits=16, alpha=True) # Data0
self.target.add_depth_attachment(bits=32) # Depth
self.target.add_aux_attachments(bits=16, count=2) # Data1, Data2
# 准备渲染(绑定相机)
self.target.prepare_render(vr_camera)
self._camera = vr_camera
# 设置正常的背景色(与主窗口一致的天空色)
if self.target.internal_buffer:
buffer = self.target.internal_buffer
buffer.setClearColorActive(True)
# 使用主RenderPipeline的天空色浅蓝色
buffer.setClearColor((0.53, 0.81, 0.92, 1.0)) # 天空蓝
# 同时也设置DisplayRegion的clear
if hasattr(self.target, '_source_region') and self.target._source_region:
self.target._source_region.setClearColorActive(True)
self.target._source_region.setClearColor((0.53, 0.81, 0.92, 1.0))
print(f"{self.name} GBuffer创建成功")
return True
def get_gbuffer_textures(self):
"""
获取GBuffer纹理字典
Returns:
dict: 包含depth, data0, data1, data2的纹理字典
"""
if not self.target:
return None
return {
"depth": self.target.depth_tex,
"data0": self.target.color_tex,
"data1": self.target.aux_tex[0],
"data2": self.target.aux_tex[1],
}
def make_gbuffer_ubo(self):
"""创建GBuffer UBO与主GBufferStage相同"""
# 注意:必须使用固定的"GBuffer"名字shader期望这个名字
ubo = SimpleInputBlock("GBuffer")
ubo.add_input("Depth", self.target.depth_tex)
ubo.add_input("Data0", self.target.color_tex)
ubo.add_input("Data1", self.target.aux_tex[0])
ubo.add_input("Data2", self.target.aux_tex[1])
return ubo
def get_internal_buffer(self):
"""获取内部GraphicsOutput"""
return self.target.internal_buffer if self.target else None
def cleanup(self):
"""清理资源"""
if self.target:
self.target.remove()
self.target = None
print(f"{self.name} GBuffer已清理")
class VRLightingStage:
"""VR光照阶段 - 使用主Pipeline的LightManager计算光照"""
def __init__(self, name, pipeline):
"""
初始化VR光照Stage
Args:
name: stage名称
pipeline: RenderPipeline实例引用
"""
self.name = name
self.pipeline = pipeline
self.target = None
self._gbuffer_stage = None
self._vr_main_scene_data = None
def create(self, width, height, gbuffer_stage):
"""
创建光照渲染目标
Args:
width: 渲染宽度
height: 渲染高度
gbuffer_stage: VRGBufferStage实例提供GBuffer纹理
"""
print(f"💡 创建{self.name} 光照Stage: {width}x{height}")
self._gbuffer_stage = gbuffer_stage
self._width = width
self._height = height
# 创建光照输出目标
self.target = RenderTarget(self.name)
self.target.size = width, height
self.target.add_color_attachment(bits=16, alpha=True)
self.target.prepare_buffer()
# 1. 创建GBuffer UBO使用SimpleInputBlock
gbuffer_ubo = gbuffer_stage.make_gbuffer_ubo()
gbuffer_ubo.bind_to(self.target)
# 2. 从主Pipeline获取光照相关inputs
self._bind_pipeline_inputs()
# 3. 从主Pipeline获取光照相关pipes
self._bind_pipeline_pipes()
# 4. 覆盖VR特定的MainSceneData字段
self._set_vr_scene_data()
# 5. 加载光照shader
self.target.shader = _load_rp_shader("apply_lights.frag.glsl")
print(f"{self.name} 光照Stage创建成功")
return True
def _bind_pipeline_inputs(self):
"""从主Pipeline获取光照相关shader inputs"""
if not self.pipeline or not self.target:
return
stage_mgr = self.pipeline.stage_mgr
inputs = stage_mgr.inputs
# 必需的光照inputs
required_inputs = [
"AllLightsData", # 光源数据
"ShadowSourceData", # 阴影源数据
"IESDatasetTex", # IES光照纹理
"maxLightIndex", # 最大光源索引
]
for input_name in required_inputs:
if input_name in inputs:
self.target.set_shader_input(input_name, inputs[input_name])
print(f" ✓ 绑定input: {input_name}")
else:
print(f" ⚠️ 缺失input: {input_name}")
# 注意不绑定MainSceneData InputBlock因为它会被主Pipeline每帧覆盖
# 我们会在_set_vr_scene_data()中手动设置所有需要的字段
def _bind_pipeline_pipes(self):
"""从主Pipeline获取光照相关pipes"""
if not self.pipeline or not self.target:
return
stage_mgr = self.pipeline.stage_mgr
pipes = stage_mgr.pipes
# 必需的光照pipes
required_pipes = [
"CellIndices", # 光照剔除cell索引
"PerCellLights", # 每个cell的光源列表
"PerCellLightsCounts", # 每个cell的光源数量
"ShadowAtlas", # 阴影图集
"ShadowAtlasPCF", # PCF阴影图集
]
# 可选的pipes
optional_pipes = [
"CombinedVelocity", # 运动矢量(用于调试模式)
]
for pipe_name in required_pipes:
if pipe_name in pipes:
pipe_value = pipes[pipe_name]
if isinstance(pipe_value, (list, tuple)):
self.target.set_shader_input(pipe_name, *pipe_value)
else:
self.target.set_shader_input(pipe_name, pipe_value)
print(f" ✓ 绑定pipe: {pipe_name}")
else:
print(f" ❌ 缺失必需pipe: {pipe_name}")
for pipe_name in optional_pipes:
if pipe_name in pipes:
pipe_value = pipes[pipe_name]
if isinstance(pipe_value, (list, tuple)):
self.target.set_shader_input(pipe_name, *pipe_value)
else:
self.target.set_shader_input(pipe_name, pipe_value)
print(f" ✓ 绑定可选pipe: {pipe_name}")
def _set_vr_scene_data(self):
"""创建VR专用的MainSceneData UBO从主Pipeline复制覆盖VR特定值"""
from panda3d.core import LVecBase2i
from RenderPipelineFile.rpcore.util.shader_input_blocks import GroupedInputBlock
import math
# 从主Pipeline的MainSceneData InputBlock获取
main_input_block = self.pipeline.stage_mgr.input_blocks.get("MainSceneData")
if not main_input_block:
print(" ⚠️ 主Pipeline的MainSceneData不存在")
return
# 创建VR专用的MainSceneData GroupedInputBlock
vr_main_scene_data = GroupedInputBlock("MainSceneData")
# 注册所有字段与主Pipeline相同的类型
field_definitions = [
("camera_pos", "vec3"),
("view_proj_mat_no_jitter", "mat4"),
("last_view_proj_mat_no_jitter", "mat4"),
("last_inv_view_proj_mat_no_jitter", "mat4"),
("view_mat_z_up", "mat4"),
("proj_mat", "mat4"),
("inv_proj_mat", "mat4"),
("view_mat_billboard", "mat4"),
("frame_delta", "float"),
("smooth_frame_delta", "float"),
("frame_time", "float"),
("current_film_offset", "vec2"),
("frame_index", "int"),
("screen_size", "ivec2"),
("native_screen_size", "ivec2"),
("lc_tile_count", "ivec2"),
("ws_frustum_directions", "mat4"),
("vs_frustum_directions", "mat4"),
]
for field_name, field_type in field_definitions:
vr_main_scene_data.register_pta(field_name, field_type)
# 复制主Pipeline的所有字段值
for field_name, _ in field_definitions:
try:
value = main_input_block.get_input(field_name)
vr_main_scene_data.update_input(field_name, value)
except:
pass # 某些字段可能不存在
# 覆盖VR特定的分辨率字段
vr_resolution = LVecBase2i(self._width, self._height)
vr_main_scene_data.update_input("screen_size", vr_resolution)
vr_main_scene_data.update_input("native_screen_size", vr_resolution)
# 计算并设置VR的光照剔除tile数量
tile_size_x = self.pipeline.settings["lighting.culling_grid_size_x"]
tile_size_y = self.pipeline.settings["lighting.culling_grid_size_y"]
num_tiles_x = int(math.ceil(self._width / float(tile_size_x)))
num_tiles_y = int(math.ceil(self._height / float(tile_size_y)))
vr_tile_count = LVecBase2i(num_tiles_x, num_tiles_y)
vr_main_scene_data.update_input("lc_tile_count", vr_tile_count)
# 绑定VR专用的MainSceneData UBO
vr_main_scene_data.bind_to(self.target)
# 保存引用以便后续更新
self._vr_main_scene_data = vr_main_scene_data
print(f" ✓ 创建VR专用MainSceneData UBO")
print(f" ✓ VR screen_size: {self._width}x{self._height}")
print(f" ✓ VR lc_tile_count: {num_tiles_x}x{num_tiles_y}")
def get_shaded_texture(self):
"""获取光照计算后的纹理"""
return self.target.color_tex if self.target else None
def cleanup(self):
"""清理资源"""
if self.target:
self.target.remove()
self.target = None
print(f"{self.name} 光照Stage已清理")
class VRAmbientStage:
"""VR环境光阶段 - 计算基于图像的照明(IBL)"""
def __init__(self, name, pipeline):
"""
初始化VR环境光Stage
Args:
name: stage名称
pipeline: RenderPipeline实例引用
"""
self.name = name
self.pipeline = pipeline
self.target = None
self._lighting_stage = None
self._gbuffer_stage = None
self._vr_main_scene_data = None
def create(self, width, height, lighting_stage, gbuffer_stage):
"""
创建环境光渲染目标
Args:
width: 渲染宽度
height: 渲染高度
lighting_stage: VRLightingStage实例提供直接光照结果
gbuffer_stage: VRGBufferStage实例提供GBuffer数据
"""
print(f"🌍 创建{self.name} 环境光Stage: {width}x{height}")
self._lighting_stage = lighting_stage
self._gbuffer_stage = gbuffer_stage
self._width = width
self._height = height
# 创建环境光输出目标
self.target = RenderTarget(self.name)
self.target.size = width, height
self.target.add_color_attachment(bits=16, alpha=True)
self.target.prepare_buffer()
# 1. 设置直接光照输入ShadedScene
shaded_tex = lighting_stage.get_shaded_texture()
if shaded_tex:
self.target.set_shader_input("ShadedScene", shaded_tex)
# 2. 绑定GBuffer UBO
gbuffer_ubo = gbuffer_stage.make_gbuffer_ubo()
gbuffer_ubo.bind_to(self.target)
# 3. 从主Pipeline获取环境相关inputs
self._bind_environment_inputs()
# 4. 覆盖VR特定的MainSceneData字段
self._set_vr_scene_data()
# 5. 加载环境光shader
self.target.shader = _load_rp_shader("ambient_stage.frag.glsl")
print(f"{self.name} 环境光Stage创建成功")
return True
def _bind_environment_inputs(self):
"""从主Pipeline获取环境相关shader inputs"""
if not self.pipeline or not self.target:
return
stage_mgr = self.pipeline.stage_mgr
inputs = stage_mgr.inputs
pipes = stage_mgr.pipes
# 必需的环境inputs
required_inputs = [
"DefaultEnvmap", # 默认环境立方体贴图
"PrefilteredBRDF", # 预计算BRDF查找表
"PrefilteredMetalBRDF", # 金属材质BRDF
"PrefilteredCoatBRDF", # 涂层材质BRDF
]
for input_name in required_inputs:
if input_name in inputs:
self.target.set_shader_input(input_name, inputs[input_name])
print(f" ✓ 绑定环境input: {input_name}")
else:
print(f" ⚠️ 缺失环境input: {input_name}")
# 注意不绑定MainSceneData InputBlock因为它会被主Pipeline每帧覆盖
# 我们会在_set_vr_scene_data()中手动设置所有需要的字段
# 可选的AO相关pipes如果启用了AO插件
optional_pipes = [
"AmbientOcclusion", # 环境光遮蔽
"SkyAO", # 天空AO
]
for pipe_name in optional_pipes:
if pipe_name in pipes:
pipe_value = pipes[pipe_name]
if isinstance(pipe_value, (list, tuple)):
self.target.set_shader_input(pipe_name, *pipe_value)
else:
self.target.set_shader_input(pipe_name, pipe_value)
print(f" ✓ 绑定可选AO pipe: {pipe_name}")
def _set_vr_scene_data(self):
"""创建VR专用的MainSceneData UBO从主Pipeline复制覆盖VR特定值"""
from panda3d.core import LVecBase2i
from RenderPipelineFile.rpcore.util.shader_input_blocks import GroupedInputBlock
import math
# 从主Pipeline的MainSceneData InputBlock获取
main_input_block = self.pipeline.stage_mgr.input_blocks.get("MainSceneData")
if not main_input_block:
print(" ⚠️ 主Pipeline的MainSceneData不存在")
return
# 创建VR专用的MainSceneData GroupedInputBlock
vr_main_scene_data = GroupedInputBlock("MainSceneData")
# 注册所有字段与主Pipeline相同的类型
field_definitions = [
("camera_pos", "vec3"),
("view_proj_mat_no_jitter", "mat4"),
("last_view_proj_mat_no_jitter", "mat4"),
("last_inv_view_proj_mat_no_jitter", "mat4"),
("view_mat_z_up", "mat4"),
("proj_mat", "mat4"),
("inv_proj_mat", "mat4"),
("view_mat_billboard", "mat4"),
("frame_delta", "float"),
("smooth_frame_delta", "float"),
("frame_time", "float"),
("current_film_offset", "vec2"),
("frame_index", "int"),
("screen_size", "ivec2"),
("native_screen_size", "ivec2"),
("lc_tile_count", "ivec2"),
("ws_frustum_directions", "mat4"),
("vs_frustum_directions", "mat4"),
]
for field_name, field_type in field_definitions:
vr_main_scene_data.register_pta(field_name, field_type)
# 复制主Pipeline的所有字段值
for field_name, _ in field_definitions:
try:
value = main_input_block.get_input(field_name)
vr_main_scene_data.update_input(field_name, value)
except:
pass # 某些字段可能不存在
# 覆盖VR特定的分辨率字段
vr_resolution = LVecBase2i(self._width, self._height)
vr_main_scene_data.update_input("screen_size", vr_resolution)
vr_main_scene_data.update_input("native_screen_size", vr_resolution)
# 计算并设置VR的光照剔除tile数量
tile_size_x = self.pipeline.settings["lighting.culling_grid_size_x"]
tile_size_y = self.pipeline.settings["lighting.culling_grid_size_y"]
num_tiles_x = int(math.ceil(self._width / float(tile_size_x)))
num_tiles_y = int(math.ceil(self._height / float(tile_size_y)))
vr_tile_count = LVecBase2i(num_tiles_x, num_tiles_y)
vr_main_scene_data.update_input("lc_tile_count", vr_tile_count)
# 绑定VR专用的MainSceneData UBO
vr_main_scene_data.bind_to(self.target)
# 保存引用以便后续更新
self._vr_main_scene_data = vr_main_scene_data
print(f" ✓ 创建VR专用MainSceneData UBO")
print(f" ✓ VR screen_size: {self._width}x{self._height}")
print(f" ✓ VR lc_tile_count: {num_tiles_x}x{num_tiles_y}")
def get_ambient_scene_texture(self):
"""获取带环境光的场景纹理"""
return self.target.color_tex if self.target else None
def cleanup(self):
"""清理资源"""
if self.target:
self.target.remove()
self.target = None
print(f"{self.name} 环境光Stage已清理")
class VRFinalStage:
"""VR最终合成阶段 - Tone mapping + Gamma校正"""
def __init__(self, name, pipeline):
"""
初始化VR最终合成Stage
Args:
name: stage名称
pipeline: RenderPipeline实例引用
"""
self.name = name
self.pipeline = pipeline
self.target = None
self._ambient_stage = None
self._vr_main_scene_data = None
def create(self, width, height, ambient_stage):
"""
创建最终合成目标
Args:
width: 渲染宽度
height: 渲染高度
ambient_stage: VRAmbientStage实例提供带环境光的场景
"""
print(f"🎬 创建{self.name} Final Stage: {width}x{height}")
self._ambient_stage = ambient_stage
self._width = width
self._height = height
# 创建最终输出目标
self.target = RenderTarget(self.name)
self.target.size = width, height
self.target.add_color_attachment(bits=16, alpha=True)
self.target.prepare_buffer()
# 设置带环境光的场景输入
ambient_scene_tex = ambient_stage.get_ambient_scene_texture()
if ambient_scene_tex:
self.target.set_shader_input("ShadedScene", ambient_scene_tex)
# 设置VR专用的MainSceneData UBO
self._set_vr_scene_data()
# 加载最终合成shadertone mapping + gamma校正
self.target.shader = _load_rp_shader("final_stage.frag.glsl")
print(f"{self.name} Final Stage创建成功")
return True
def _set_vr_scene_data(self):
"""创建VR专用的MainSceneData UBO从主Pipeline复制覆盖VR特定值"""
from panda3d.core import LVecBase2i
from RenderPipelineFile.rpcore.util.shader_input_blocks import GroupedInputBlock
import math
# 从主Pipeline的MainSceneData InputBlock获取
main_input_block = self.pipeline.stage_mgr.input_blocks.get("MainSceneData")
if not main_input_block:
print(" ⚠️ 主Pipeline的MainSceneData不存在")
return
# 创建VR专用的MainSceneData GroupedInputBlock
vr_main_scene_data = GroupedInputBlock("MainSceneData")
# 注册所有字段与主Pipeline相同的类型
field_definitions = [
("camera_pos", "vec3"),
("view_proj_mat_no_jitter", "mat4"),
("last_view_proj_mat_no_jitter", "mat4"),
("last_inv_view_proj_mat_no_jitter", "mat4"),
("view_mat_z_up", "mat4"),
("proj_mat", "mat4"),
("inv_proj_mat", "mat4"),
("view_mat_billboard", "mat4"),
("frame_delta", "float"),
("smooth_frame_delta", "float"),
("frame_time", "float"),
("current_film_offset", "vec2"),
("frame_index", "int"),
("screen_size", "ivec2"),
("native_screen_size", "ivec2"),
("lc_tile_count", "ivec2"),
("ws_frustum_directions", "mat4"),
("vs_frustum_directions", "mat4"),
]
for field_name, field_type in field_definitions:
vr_main_scene_data.register_pta(field_name, field_type)
# 复制主Pipeline的所有字段值
for field_name, _ in field_definitions:
try:
value = main_input_block.get_input(field_name)
vr_main_scene_data.update_input(field_name, value)
except:
pass # 某些字段可能不存在
# 覆盖VR特定的分辨率字段
vr_resolution = LVecBase2i(self._width, self._height)
vr_main_scene_data.update_input("screen_size", vr_resolution)
vr_main_scene_data.update_input("native_screen_size", vr_resolution)
# 计算并设置VR的光照剔除tile数量
tile_size_x = self.pipeline.settings["lighting.culling_grid_size_x"]
tile_size_y = self.pipeline.settings["lighting.culling_grid_size_y"]
num_tiles_x = int(math.ceil(self._width / float(tile_size_x)))
num_tiles_y = int(math.ceil(self._height / float(tile_size_y)))
vr_tile_count = LVecBase2i(num_tiles_x, num_tiles_y)
vr_main_scene_data.update_input("lc_tile_count", vr_tile_count)
# 绑定VR专用的MainSceneData UBO
vr_main_scene_data.bind_to(self.target)
# 保存引用以便后续更新
self._vr_main_scene_data = vr_main_scene_data
print(f" ✓ 创建VR专用MainSceneData UBO (Final)")
print(f" ✓ VR screen_size: {self._width}x{self._height}")
print(f" ✓ VR lc_tile_count: {num_tiles_x}x{num_tiles_y}")
def get_final_texture(self):
"""获取最终输出纹理用于提交到OpenVR"""
return self.target.color_tex if self.target else None
def get_internal_buffer(self):
"""获取内部GraphicsOutput"""
return self.target.internal_buffer if self.target else None
def cleanup(self):
"""清理资源"""
if self.target:
self.target.remove()
self.target = None
print(f"{self.name} Final Stage已清理")
class VRPipelineController:
"""VR渲染管线控制器 - 管理左右眼的完整渲染管线"""
def __init__(self, pipeline):
"""
初始化VR Pipeline控制器
Args:
pipeline: 主RenderPipeline实例
"""
self.pipeline = pipeline
# 左眼stages
self.left_gbuffer = None
self.left_lighting = None
self.left_ambient = None
self.left_final = None
# 右眼stages
self.right_gbuffer = None
self.right_lighting = None
self.right_ambient = None
self.right_final = None
def create_eye_pipeline(self, eye_name, width, height, vr_camera):
"""
为单个眼睛创建完整的渲染管线
Args:
eye_name: "Left" "Right"
width: 渲染宽度
height: 渲染高度
vr_camera: VR相机NodePath
Returns:
tuple: (gbuffer_stage, lighting_stage, ambient_stage, final_stage)
"""
print(f"\n🚀 创建VR {eye_name}眼渲染管线")
# 1. 创建GBuffer Stage
gbuffer = VRGBufferStage(f"VR_{eye_name}_GBuffer", self.pipeline)
if not gbuffer.create(width, height, vr_camera):
return None, None, None, None
# 2. 创建光照Stage直接光照
lighting = VRLightingStage(f"VR_{eye_name}_Lighting", self.pipeline)
if not lighting.create(width, height, gbuffer):
gbuffer.cleanup()
return None, None, None, None
# 3. 创建环境光StageIBL
ambient = VRAmbientStage(f"VR_{eye_name}_Ambient", self.pipeline)
if not ambient.create(width, height, lighting, gbuffer):
lighting.cleanup()
gbuffer.cleanup()
return None, None, None, None
# 4. 创建最终合成Stagetone mapping + gamma
final = VRFinalStage(f"VR_{eye_name}_Final", self.pipeline)
if not final.create(width, height, ambient):
ambient.cleanup()
lighting.cleanup()
gbuffer.cleanup()
return None, None, None, None
print(f"✅ VR {eye_name}眼渲染管线创建成功\n")
return gbuffer, lighting, ambient, final
def create_stereo_pipeline(self, width, height, left_camera, right_camera):
"""
创建立体渲染管线左右眼- 完整版本
Args:
width: 每个眼睛的渲染宽度
height: 每个眼睛的渲染高度
left_camera: 左眼相机NodePath
right_camera: 右眼相机NodePath
Returns:
bool: 创建是否成功
"""
print("=" * 60)
print("🎯 开始创建VR立体渲染管线完整版")
print("=" * 60)
# 创建左眼完整管线
result = self.create_eye_pipeline("Left", width, height, left_camera)
if not all(result):
print("❌ 左眼渲染管线创建失败")
return False
self.left_gbuffer, self.left_lighting, self.left_ambient, self.left_final = result
# 创建右眼完整管线
result = self.create_eye_pipeline("Right", width, height, right_camera)
if not all(result):
print("❌ 右眼渲染管线创建失败")
self.cleanup_left()
return False
self.right_gbuffer, self.right_lighting, self.right_ambient, self.right_final = result
print("\n" + "=" * 60)
print("✅ VR立体渲染管线创建成功")
print(" 管线流程: GBuffer → Lighting → Ambient → Final")
print("=" * 60)
return True
def get_left_textures(self):
"""获取左眼的所有纹理"""
if not self.left_final:
return None
return {
"final": self.left_final.get_final_texture(),
"gbuffer": self.left_gbuffer.get_gbuffer_textures() if self.left_gbuffer else None
}
def get_right_textures(self):
"""获取右眼的所有纹理"""
if not self.right_final:
return None
return {
"final": self.right_final.get_final_texture(),
"gbuffer": self.right_gbuffer.get_gbuffer_textures() if self.right_gbuffer else None
}
def get_left_buffer(self):
"""获取左眼Final stage的内部buffer用于设置DrawCallback"""
return self.left_final.get_internal_buffer() if self.left_final else None
def get_right_buffer(self):
"""获取右眼Final stage的内部buffer用于设置DrawCallback"""
return self.right_final.get_internal_buffer() if self.right_final else None
def cleanup_left(self):
"""清理左眼资源"""
if self.left_final:
self.left_final.cleanup()
self.left_final = None
if self.left_ambient:
self.left_ambient.cleanup()
self.left_ambient = None
if self.left_lighting:
self.left_lighting.cleanup()
self.left_lighting = None
if self.left_gbuffer:
self.left_gbuffer.cleanup()
self.left_gbuffer = None
def cleanup_right(self):
"""清理右眼资源"""
if self.right_final:
self.right_final.cleanup()
self.right_final = None
if self.right_ambient:
self.right_ambient.cleanup()
self.right_ambient = None
if self.right_lighting:
self.right_lighting.cleanup()
self.right_lighting = None
if self.right_gbuffer:
self.right_gbuffer.cleanup()
self.right_gbuffer = None
def cleanup_all(self):
"""清理所有VR渲染资源"""
print("\n🧹 清理VR渲染管线...")
self.cleanup_left()
self.cleanup_right()
print("✓ VR渲染管线已清理\n")

View File

@ -21,6 +21,8 @@ from core.selection import SelectionSystem
from core.event_handler import EventHandler
from core.tool_manager import ToolManager
from core.script_system import ScriptManager
from core.patrol_system import PatrolSystem
from core.Command_System import CommandManager
from gui.gui_manager import GUIManager
from core.terrain_manager import TerrainManager
from scene.scene_manager import SceneManager

View File

@ -16,7 +16,7 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction
QLabel, QLineEdit, QFormLayout, QDoubleSpinBox, QScrollArea,
QFileSystemModel, QButtonGroup, QToolButton, QPushButton, QHBoxLayout,
QComboBox, QGroupBox, QInputDialog, QFileDialog, QMessageBox, QDesktopWidget, QDialog,
QSpinBox, QFrame)
QSpinBox, QFrame, QCheckBox, QRadioButton, QTextEdit)
from PyQt5.QtCore import Qt, QDir, QTimer, QSize, QPoint, QUrl, QRect
from direct.showbase.ShowBaseGlobal import aspect2d
from panda3d.core import OrthographicLens
@ -3543,20 +3543,71 @@ class MainWindow(QMainWindow):
status_group.setLayout(status_layout)
layout.addWidget(status_group)
# 🎨 渲染模式设置
render_mode_group = QGroupBox("渲染模式")
render_mode_layout = QVBoxLayout()
# 创建单选按钮组
render_mode_button_group = QButtonGroup(dialog)
normal_render_radio = QRadioButton("普通渲染模式")
pipeline_render_radio = QRadioButton("RenderPipeline高级渲染推荐")
render_mode_button_group.addButton(normal_render_radio, 0)
render_mode_button_group.addButton(pipeline_render_radio, 1)
# 根据当前模式设置选中状态
if hasattr(self.world, 'vr_manager') and self.world.vr_manager:
from core.vr_manager import VRRenderMode
current_mode = self.world.vr_manager.get_vr_render_mode()
if current_mode == VRRenderMode.RENDER_PIPELINE:
pipeline_render_radio.setChecked(True)
else:
normal_render_radio.setChecked(True)
else:
normal_render_radio.setChecked(True)
render_mode_layout.addWidget(normal_render_radio)
render_mode_layout.addWidget(pipeline_render_radio)
# 添加说明文本
info_text = QTextEdit()
info_text.setReadOnly(True)
info_text.setMaximumHeight(60)
info_text.setPlainText(
"• 普通渲染:性能最优,适合低配置\n"
"• RenderPipeline高级图形效果阴影、AO等需要较高性能"
)
render_mode_layout.addWidget(info_text)
render_mode_group.setLayout(render_mode_layout)
layout.addWidget(render_mode_group)
# 保存按钮组引用以便后续使用
dialog.render_mode_button_group = render_mode_button_group
# 🔧 加载配置
vr_config = {}
if hasattr(self.world, 'vr_manager') and self.world.vr_manager and self.world.vr_manager.config_manager:
vr_config = self.world.vr_manager.config_manager.load_config()
# 渲染设置
render_group = QGroupBox("渲染设置")
render_layout = QFormLayout()
# 渲染质量
quality_combo = QComboBox()
quality_combo.addItems(["", "", "", "超高"])
quality_combo.setCurrentText("")
quality_combo.addItems(["", "", ""])
# 从配置加载质量预设
quality_preset = vr_config.get("quality_preset", "balanced")
quality_map = {"performance": "", "balanced": "", "quality": ""}
quality_combo.setCurrentText(quality_map.get(quality_preset, ""))
render_layout.addRow("渲染质量:", quality_combo)
# 抗锯齿
aa_combo = QComboBox()
aa_combo.addItems(["", "2x", "4x", "8x"])
aa_combo.setCurrentText("4x")
aa_combo.setCurrentText(vr_config.get("anti_aliasing", "4x"))
render_layout.addRow("抗锯齿:", aa_combo)
render_group.setLayout(render_layout)
@ -3569,17 +3620,23 @@ class MainWindow(QMainWindow):
# 刷新率
refresh_combo = QComboBox()
refresh_combo.addItems(["72Hz", "90Hz", "120Hz", "144Hz"])
refresh_combo.setCurrentText("90Hz")
refresh_combo.setCurrentText(vr_config.get("refresh_rate", "90Hz"))
perf_layout.addRow("刷新率:", refresh_combo)
# 异步重投影
async_check = QCheckBox("启用异步重投影")
async_check.setChecked(True)
async_check.setChecked(vr_config.get("async_reprojection", True))
perf_layout.addRow("", async_check)
perf_group.setLayout(perf_layout)
layout.addWidget(perf_group)
# 保存控件引用到dialog对象
dialog.quality_combo = quality_combo
dialog.aa_combo = aa_combo
dialog.refresh_combo = refresh_combo
dialog.async_check = async_check
# 按钮
button_layout = QHBoxLayout()
@ -3596,18 +3653,99 @@ class MainWindow(QMainWindow):
# 连接信号
apply_button.clicked.connect(lambda: self.applyVRSettings(dialog))
ok_button.clicked.connect(dialog.accept)
ok_button.clicked.connect(lambda: self.onVRSettingsOK(dialog))
cancel_button.clicked.connect(dialog.reject)
return dialog
def onVRSettingsOK(self, dialog):
"""确定按钮 - 应用设置并关闭对话框"""
# 先应用设置
self.applyVRSettings(dialog)
# 关闭对话框
dialog.accept()
def applyVRSettings(self, dialog):
"""应用VR设置"""
try:
# 这里可以实现设置的保存和应用逻辑
QMessageBox.information(dialog, "成功", "VR设置已应用")
if not hasattr(self.world, 'vr_manager') or not self.world.vr_manager:
QMessageBox.warning(dialog, "错误", "VR管理器不可用")
return
if not self.world.vr_manager.config_manager:
QMessageBox.warning(dialog, "错误", "VR配置管理器不可用")
return
# 1⃣ 读取所有UI控件的值
# 渲染模式
selected_mode_id = dialog.render_mode_button_group.checkedId()
new_mode = "render_pipeline" if selected_mode_id == 1 else "normal"
mode_name = "RenderPipeline高级渲染" if selected_mode_id == 1 else "普通渲染"
# 渲染质量
quality_text = dialog.quality_combo.currentText()
quality_map_reverse = {"": "performance", "": "balanced", "": "quality"}
quality_preset = quality_map_reverse.get(quality_text, "balanced")
# 其他设置
anti_aliasing = dialog.aa_combo.currentText()
refresh_rate = dialog.refresh_combo.currentText()
async_reprojection = dialog.async_check.isChecked()
# 2⃣ 加载当前配置
config = self.world.vr_manager.config_manager.load_config()
# 3⃣ 更新配置
config["quality_preset"] = quality_preset
config["anti_aliasing"] = anti_aliasing
config["refresh_rate"] = refresh_rate
config["async_reprojection"] = async_reprojection
# 4⃣ 检查渲染模式是否改变
from core.vr_manager import VRRenderMode
current_mode = self.world.vr_manager.get_vr_render_mode()
mode_changed = (current_mode.value != new_mode)
# 5⃣ 如果渲染模式改变,询问用户确认
if mode_changed:
reply = QMessageBox.question(
dialog,
"确认切换",
f"确定要切换到{mode_name}模式吗?\n\n注意切换渲染模式将重新创建VR缓冲区可能需要几秒钟。",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.No:
# 用户取消渲染模式切换,但仍然保存其他设置
self.world.vr_manager.config_manager.save_config(config)
QMessageBox.information(dialog, "提示", "已保存其他设置(未切换渲染模式)")
return
# 应用渲染模式切换
success = self.world.vr_manager.set_vr_render_mode(new_mode)
if not success:
QMessageBox.warning(dialog, "失败", f"切换到{mode_name}模式失败!\n请查看控制台输出了解详情。")
return
# 6⃣ 保存配置如果模式改变set_vr_render_mode已经保存了但我们需要确保其他设置也被保存
self.world.vr_manager.config_manager.save_config(config)
# 7⃣ 应用质量预设到VR管理器
if hasattr(self.world.vr_manager, 'current_quality_preset'):
self.world.vr_manager.current_quality_preset = quality_preset
# 8⃣ 显示成功消息
if mode_changed:
QMessageBox.information(dialog, "成功", f"VR设置已应用\n• 渲染模式: {mode_name}\n• 渲染质量: {quality_text}\n配置已自动保存。")
else:
QMessageBox.information(dialog, "成功", f"VR设置已保存\n• 渲染质量: {quality_text}")
except Exception as e:
QMessageBox.critical(dialog, "错误", f"应用VR设置时发生错误\n{str(e)}")
import traceback
traceback.print_exc()
# ==================== VR调试事件处理 ====================