Merge remote-tracking branch 'origin/VR' into addRender
# Conflicts: # ui/main_window.py
This commit is contained in:
commit
a0ef6d216a
18
config/vr_settings.json
Normal file
18
config/vr_settings.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"render_mode": "render_pipeline",
|
||||
"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": "144Hz",
|
||||
"async_reprojection": true
|
||||
}
|
||||
273
core/vr_config.py
Normal file
273
core/vr_config.py
Normal 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
|
||||
@ -404,6 +404,34 @@ class VRController(DirectObject):
|
||||
except Exception as e:
|
||||
print(f"⚠️ 轴数据调试失败: {e}")
|
||||
|
||||
def recreate_visualizer(self):
|
||||
"""重新创建visualizer - 用于渲染模式切换后刷新
|
||||
|
||||
当VR渲染模式在运行时改变时调用此方法,以确保visualizer
|
||||
使用正确的渲染设置(普通模式 vs RenderPipeline模式)
|
||||
"""
|
||||
# 清理旧visualizer
|
||||
if self.visualizer:
|
||||
try:
|
||||
self.visualizer.cleanup()
|
||||
self.visualizer = None
|
||||
print(f"🧹 {self.name}手柄visualizer已清理")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 清理{self.name}手柄visualizer失败: {e}")
|
||||
|
||||
# 重新创建visualizer
|
||||
if self.anchor_node:
|
||||
self._create_visualizer()
|
||||
if self.visualizer:
|
||||
print(f"✨ {self.name}手柄visualizer已重建(使用当前渲染模式)")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ {self.name}手柄visualizer重建失败")
|
||||
return False
|
||||
else:
|
||||
print(f"⚠️ {self.name}手柄anchor_node不存在,无法重建visualizer")
|
||||
return False
|
||||
|
||||
def cleanup(self):
|
||||
"""清理资源"""
|
||||
self.ignoreAll()
|
||||
|
||||
@ -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,7 +567,21 @@ class VRManager(DirectObject):
|
||||
self.game_poses = poses_t() # 游戏逻辑姿态(当前的)
|
||||
print("✓ VR渲染和游戏姿态数组已创建")
|
||||
|
||||
# 创建VR渲染缓冲区
|
||||
# 🔧 关键修复:统一初始化流程
|
||||
# 如果目标模式是RenderPipeline,先用普通模式初始化,然后切换
|
||||
# 这样两个场景都走相同的、已验证的代码路径
|
||||
target_render_mode = self.vr_render_mode
|
||||
use_deferred_pipeline_switch = False
|
||||
|
||||
if target_render_mode == VRRenderMode.RENDER_PIPELINE:
|
||||
print("🎨 目标VR渲染模式: RenderPipeline")
|
||||
print(" 策略:先用普通模式初始化,然后切换到RenderPipeline")
|
||||
self.vr_render_mode = VRRenderMode.NORMAL
|
||||
use_deferred_pipeline_switch = True
|
||||
else:
|
||||
print(f"🎨 VR渲染模式: {self.vr_render_mode.value}")
|
||||
|
||||
# 创建VR渲染缓冲区 - 始终用普通模式初始化
|
||||
if not self._create_vr_buffers():
|
||||
print("❌ 创建VR渲染缓冲区失败")
|
||||
return False
|
||||
@ -598,7 +649,18 @@ class VRManager(DirectObject):
|
||||
self._start_vr_task()
|
||||
|
||||
self.vr_initialized = True
|
||||
print("✅ VR系统初始化成功")
|
||||
print("✅ VR系统初始化成功(普通模式)")
|
||||
|
||||
# 🔧 关键修复:如果目标是RenderPipeline模式,现在切换过去
|
||||
# 这样场景2(先设RP再进VR)会走场景1(先VR再设RP)相同的代码路径
|
||||
if use_deferred_pipeline_switch:
|
||||
print("\n🔄 现在切换到目标渲染模式: RenderPipeline")
|
||||
print(" 这将触发buffer重建和visualizer刷新...")
|
||||
if self.set_vr_render_mode(target_render_mode):
|
||||
print("✅ 已成功切换到RenderPipeline模式")
|
||||
else:
|
||||
print("⚠️ 切换到RenderPipeline失败,保持普通模式")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@ -679,6 +741,13 @@ class VRManager(DirectObject):
|
||||
def _prepare_and_cache_textures(self):
|
||||
"""准备纹理并缓存OpenGL ID - 解决重复准备问题"""
|
||||
try:
|
||||
# 🔧 验证纹理对象存在
|
||||
if not self.vr_left_texture or not self.vr_right_texture:
|
||||
print("❌ VR纹理对象不存在")
|
||||
print(f" 左眼纹理: {self.vr_left_texture}")
|
||||
print(f" 右眼纹理: {self.vr_right_texture}")
|
||||
return False
|
||||
|
||||
# 获取graphics state guardian和prepared objects
|
||||
gsg = self.world.win.getGsg()
|
||||
if not gsg:
|
||||
@ -691,34 +760,32 @@ class VRManager(DirectObject):
|
||||
return False
|
||||
|
||||
# 准备左眼纹理并缓存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}")
|
||||
else:
|
||||
print(" ❌ 左眼纹理ID无效")
|
||||
return False
|
||||
print(f" 准备左眼纹理: {self.vr_left_texture.getXSize()}x{self.vr_left_texture.getYSize()}")
|
||||
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" ✅ 左眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.left_texture_id}")
|
||||
else:
|
||||
print(" ❌ 左眼纹理准备失败")
|
||||
print(" ❌ 左眼纹理ID无效")
|
||||
return False
|
||||
else:
|
||||
print(" ❌ 左眼纹理准备失败")
|
||||
return False
|
||||
|
||||
# 准备右眼纹理并缓存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}")
|
||||
else:
|
||||
print(" ❌ 右眼纹理ID无效")
|
||||
return False
|
||||
print(f" 准备右眼纹理: {self.vr_right_texture.getXSize()}x{self.vr_right_texture.getYSize()}")
|
||||
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" ✅ 右眼纹理准备完成 ({self.vr_render_mode.value}): ID={self.right_texture_id}")
|
||||
else:
|
||||
print(" ❌ 右眼纹理准备失败")
|
||||
print(" ❌ 右眼纹理ID无效")
|
||||
return False
|
||||
else:
|
||||
print(" ❌ 右眼纹理准备失败")
|
||||
return False
|
||||
|
||||
# 标记纹理已准备
|
||||
self.textures_prepared = True
|
||||
@ -798,6 +865,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 +973,118 @@ 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()
|
||||
|
||||
# 🔧 关键修复:验证buffer有效性
|
||||
if not self.vr_left_eye_buffer or not self.vr_right_eye_buffer:
|
||||
print("❌ VR Pipeline buffer创建失败")
|
||||
print(f" 左眼buffer: {self.vr_left_eye_buffer}")
|
||||
print(f" 右眼buffer: {self.vr_right_eye_buffer}")
|
||||
return False
|
||||
|
||||
# 获取最终输出纹理(用于提交到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:
|
||||
# 🔧 验证DisplayRegion数量
|
||||
left_dr_count = self.vr_left_eye_buffer.getNumDisplayRegions()
|
||||
right_dr_count = self.vr_right_eye_buffer.getNumDisplayRegions()
|
||||
|
||||
print(f" 左眼buffer DisplayRegion数量: {left_dr_count}")
|
||||
print(f" 右眼buffer DisplayRegion数量: {right_dr_count}")
|
||||
|
||||
if left_dr_count == 0 or right_dr_count == 0:
|
||||
print(" ❌ DisplayRegion未创建,无法设置回调")
|
||||
return False
|
||||
|
||||
# 获取RenderTarget创建的DisplayRegion
|
||||
left_dr = self.vr_left_eye_buffer.get_display_region(0)
|
||||
right_dr = self.vr_right_eye_buffer.get_display_region(0)
|
||||
|
||||
# 验证DisplayRegion有效性
|
||||
if not left_dr or not right_dr:
|
||||
print(" ❌ DisplayRegion无效")
|
||||
return False
|
||||
|
||||
# 设置渲染回调(与普通模式一致)
|
||||
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()
|
||||
return False
|
||||
|
||||
# 🔧 关键修复:强制同步GraphicsEngine,确保所有buffer完全初始化
|
||||
print(" 同步GraphicsEngine...")
|
||||
self.world.graphicsEngine.renderFrame()
|
||||
|
||||
# 准备纹理并缓存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
|
||||
@ -1586,6 +1830,22 @@ class VRManager(DirectObject):
|
||||
self.world.qtWidget.synchronizer.setInterval(int(1000/144))
|
||||
print("✓ Qt Timer调整为144Hz,让OpenVR控制VR渲染节奏")
|
||||
|
||||
# 🔧 关键修复:检测并重建缺失的手柄visualizer
|
||||
# 当渲染模式切换时,visualizer可能被清理但控制器对象仍存在
|
||||
if hasattr(self, 'left_controller') and self.left_controller:
|
||||
if not self.left_controller.visualizer and self.left_controller.anchor_node:
|
||||
print("🔧 检测到左手柄visualizer缺失,正在重建...")
|
||||
self.left_controller._create_visualizer()
|
||||
if self.left_controller.visualizer:
|
||||
print("✅ 左手柄visualizer已重建")
|
||||
|
||||
if hasattr(self, 'right_controller') and self.right_controller:
|
||||
if not self.right_controller.visualizer and self.right_controller.anchor_node:
|
||||
print("🔧 检测到右手柄visualizer缺失,正在重建...")
|
||||
self.right_controller._create_visualizer()
|
||||
if self.right_controller.visualizer:
|
||||
print("✅ 右手柄visualizer已重建")
|
||||
|
||||
print("✅ VR模式已启用")
|
||||
return True
|
||||
|
||||
@ -1630,6 +1890,170 @@ 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()
|
||||
|
||||
# 🔧 关键修复:重建所有手柄的visualizer以适配新渲染模式
|
||||
print(" 刷新手柄visualizer以适配新渲染模式...")
|
||||
if hasattr(self, 'left_controller') and self.left_controller:
|
||||
self.left_controller.recreate_visualizer()
|
||||
|
||||
if hasattr(self, 'right_controller') and self.right_controller:
|
||||
self.right_controller.recreate_visualizer()
|
||||
|
||||
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 +3891,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 +3919,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):
|
||||
"""获取分辨率相关信息"""
|
||||
|
||||
844
core/vr_stages.py
Normal file
844
core/vr_stages.py
Normal file
@ -0,0 +1,844 @@
|
||||
"""
|
||||
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()
|
||||
|
||||
# 加载最终合成shader(tone 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. 创建环境光Stage(IBL)
|
||||
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. 创建最终合成Stage(tone 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
|
||||
|
||||
# 🔧 关键修复:确保左眼buffer完全初始化后再创建右眼
|
||||
# 验证左眼buffer状态
|
||||
left_buffer = self.left_final.get_internal_buffer() if self.left_final else None
|
||||
if not left_buffer:
|
||||
print("❌ 左眼内部buffer无效")
|
||||
self.cleanup_left()
|
||||
return False
|
||||
|
||||
print(" ✅ 左眼管线验证通过,准备创建右眼...")
|
||||
|
||||
# 创建右眼完整管线
|
||||
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")
|
||||
@ -114,8 +114,8 @@ class VRControllerVisualizer:
|
||||
# 暂时注释身份标记功能,避免额外几何体造成悬空零件
|
||||
# self._apply_controller_identity_marker(steamvr_model)
|
||||
|
||||
# 设置手柄始终显示在上层
|
||||
self._set_always_on_top(steamvr_model)
|
||||
# 根据渲染模式设置渲染属性
|
||||
self._apply_render_mode_settings(steamvr_model)
|
||||
|
||||
print(f"✅ {self.controller.name}手柄已加载SteamVR官方模型(缩放: 1.0,实体渲染模式)")
|
||||
else:
|
||||
@ -363,6 +363,9 @@ class VRControllerVisualizer:
|
||||
trackpad_node.setColor(self.button_colors['trackpad'])
|
||||
trackpad_node.setMaterial(material)
|
||||
|
||||
# 应用渲染模式设置
|
||||
self._apply_render_mode_settings(self.model_node)
|
||||
|
||||
def _create_box_geometry(self, width, length, height):
|
||||
"""创建立方体几何体"""
|
||||
# 创建顶点格式
|
||||
@ -683,8 +686,57 @@ class VRControllerVisualizer:
|
||||
self.ray_node.removeNode()
|
||||
self._create_interaction_ray()
|
||||
|
||||
def _apply_render_mode_settings(self, model_node):
|
||||
"""根据当前渲染模式应用渲染设置
|
||||
|
||||
Args:
|
||||
model_node: 手柄模型节点
|
||||
"""
|
||||
if not model_node:
|
||||
return
|
||||
|
||||
# 检测是否启用RenderPipeline模式
|
||||
is_render_pipeline = False
|
||||
try:
|
||||
# 通过VR管理器获取渲染模式
|
||||
vr_manager = self.controller.vr_manager
|
||||
if hasattr(vr_manager, 'vr_render_mode'):
|
||||
from core.vr_manager import VRRenderMode
|
||||
is_render_pipeline = (vr_manager.vr_render_mode == VRRenderMode.RENDER_PIPELINE and
|
||||
vr_manager.render_pipeline_enabled)
|
||||
except Exception as e:
|
||||
print(f"⚠️ 检测渲染模式失败: {e}")
|
||||
|
||||
if is_render_pipeline:
|
||||
# RenderPipeline模式:使用正常深度测试,添加着色器标签
|
||||
print(f"🎨 {self.controller.name}手柄:应用RenderPipeline渲染模式")
|
||||
|
||||
# 设置着色器标签,使模型通过RenderPipeline的GBuffer渲染
|
||||
model_node.setTag("RenderPipeline", "1")
|
||||
|
||||
# 使用正常的深度测试和深度写入
|
||||
model_node.setDepthTest(True)
|
||||
model_node.setDepthWrite(True)
|
||||
|
||||
# 设置合适的渲染bin(transparent bin用于透明度支持)
|
||||
# 使用默认的opaque bin确保正常渲染
|
||||
model_node.clearBin()
|
||||
|
||||
# 递归设置所有子节点
|
||||
for child in model_node.findAllMatches("**"):
|
||||
child.setTag("RenderPipeline", "1")
|
||||
child.setDepthTest(True)
|
||||
child.setDepthWrite(True)
|
||||
child.clearBin()
|
||||
|
||||
print(f"✅ {self.controller.name}手柄已配置RenderPipeline渲染")
|
||||
else:
|
||||
# 普通模式:使用always-on-top设置
|
||||
print(f"🎨 {self.controller.name}手柄:应用普通渲染模式(always-on-top)")
|
||||
self._set_always_on_top(model_node)
|
||||
|
||||
def _set_always_on_top(self, model_node):
|
||||
"""设置手柄模型始终显示在上层,不被其他物体遮挡"""
|
||||
"""设置手柄模型始终显示在上层,不被其他物体遮挡(仅普通渲染模式)"""
|
||||
if not model_node:
|
||||
return
|
||||
|
||||
|
||||
2
main.py
2
main.py
@ -23,6 +23,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
|
||||
|
||||
@ -5178,20 +5178,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)
|
||||
@ -5204,17 +5255,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()
|
||||
|
||||
@ -5231,18 +5288,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调试事件处理 ====================
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user