EG/core/vr/visualization/effects.py
2025-10-11 16:41:59 +08:00

227 lines
6.8 KiB
Python

"""
VR Effects Manager - 为VR场景中的模型自动应用RenderPipeline Effects
主要功能:
1. 扫描VR场景中的所有模型节点
2. 自动应用RenderPipeline的默认effect配置
3. 设置正确的shader tags以确保模型能正确渲染到GBuffer
4. 支持批量应用和单个模型应用
"""
from panda3d.core import NodePath, GeomNode
class VREffectsManager:
"""VR场景的RenderPipeline Effects管理器"""
def __init__(self, pipeline):
"""
初始化VR Effects Manager
Args:
pipeline: RenderPipeline实例
"""
self.pipeline = pipeline
self.applied_models = set() # 记录已应用effects的模型
# 默认的effect配置
self.default_effect_config = {
"normal_mapping": True, # 法线贴图
"render_gbuffer": True, # 渲染到GBuffer
"alpha_testing": False, # Alpha测试
"parallax_mapping": False, # 视差贴图(性能考虑,VR中默认关闭)
"render_shadow": True, # 投射阴影
"render_envmap": True, # 环境映射
}
def apply_effects_to_scene(self, scene_root):
"""
为场景根节点下的所有模型应用effects
Args:
scene_root: 场景根节点(通常是render或其子节点)
Returns:
int: 应用effects的模型数量
"""
if not self.pipeline:
print("⚠️ RenderPipeline未初始化,无法应用effects")
return 0
print("🎨 开始为VR场景应用RenderPipeline Effects...")
count = 0
# 递归遍历所有子节点
for node in scene_root.findAllMatches("**/+GeomNode"):
if self._should_apply_effect(node):
if self.apply_effect_to_model(node):
count += 1
print(f"✅ 为 {count} 个模型应用了RenderPipeline Effects")
return count
def apply_effect_to_model(self, model_node, effect_config=None):
"""
为单个模型节点应用RenderPipeline effect
Args:
model_node: 模型的NodePath
effect_config: 自定义effect配置(可选,默认使用default_effect_config)
Returns:
bool: 是否成功应用
"""
if not isinstance(model_node, NodePath):
print(f"⚠️ 无效的模型节点: {model_node}")
return False
# 使用提供的配置或默认配置
config = effect_config or self.default_effect_config
try:
# 应用RenderPipeline effect
self.pipeline.set_effect(
model_node,
"effects/default.yaml",
config,
sort=50 # 默认排序值
)
# 设置RenderPipeline shader tag
# 这个tag告诉RenderPipeline这个模型需要通过GBuffer渲染
model_node.setTag("RenderPipeline", "1")
# 递归设置所有子节点的tag
for child in model_node.findAllMatches("**/+GeomNode"):
child.setTag("RenderPipeline", "1")
# 记录已应用的模型
model_id = id(model_node.node())
self.applied_models.add(model_id)
return True
except Exception as e:
print(f"❌ 应用effect失败: {model_node.getName()} - {e}")
return False
def apply_effect_to_model_simple(self, model_node):
"""
为模型应用简化版effect(只设置tags,不调用set_effect)
适用于某些特殊情况,例如:
- 模型已经有自定义shader
- 只需要确保模型能渲染到GBuffer
- 避免覆盖现有的shader配置
Args:
model_node: 模型的NodePath
Returns:
bool: 是否成功
"""
try:
model_node.setTag("RenderPipeline", "1")
# 递归设置所有子节点
for child in model_node.findAllMatches("**/+GeomNode"):
child.setTag("RenderPipeline", "1")
return True
except Exception as e:
print(f"❌ 设置tags失败: {model_node.getName()} - {e}")
return False
def _should_apply_effect(self, node):
"""
判断是否应该为节点应用effect
Args:
node: 待检查的节点
Returns:
bool: 是否应该应用effect
"""
# 跳过已应用的模型
model_id = id(node.node())
if model_id in self.applied_models:
return False
# 跳过没有几何数据的节点
if not node.node().getNumGeoms() > 0:
return False
# 跳过特殊标记的节点(例如:UI元素、调试几何体等)
# 检查节点是否有"skip_pipeline"标签
if node.hasTag("skip_pipeline"):
return False
# 跳过隐藏的节点
if node.isHidden():
return False
return True
def apply_effects_to_new_models(self, model_list):
"""
为新添加的模型列表批量应用effects
Args:
model_list: 模型NodePath列表
Returns:
int: 成功应用effects的模型数量
"""
count = 0
for model in model_list:
if self.apply_effect_to_model(model):
count += 1
print(f"✅ 为 {count}/{len(model_list)} 个新模型应用了effects")
return count
def update_effect_config(self, new_config):
"""
更新默认effect配置
Args:
new_config: 新的配置字典(会合并到现有配置)
"""
self.default_effect_config.update(new_config)
print(f"✓ 更新effect配置: {new_config}")
def get_applied_models_count(self):
"""获取已应用effects的模型数量"""
return len(self.applied_models)
def clear_applied_models_cache(self):
"""清空已应用模型的缓存(用于场景重置等情况)"""
self.applied_models.clear()
print("✓ 已清空effects应用缓存")
def setup_vr_model_effects(world, vr_root=None):
"""
便捷函数:为VR场景设置RenderPipeline Effects
Args:
world: CoreWorld实例(需要有render_pipeline属性)
vr_root: VR场景根节点(可选,默认使用world.render)
Returns:
VREffectsManager实例,或None(如果失败)
"""
if not hasattr(world, 'render_pipeline') or not world.render_pipeline:
print("⚠️ RenderPipeline未初始化")
return None
# 创建Effects Manager
effects_mgr = VREffectsManager(world.render_pipeline)
# 应用effects到场景
scene_root = vr_root or world.render
effects_mgr.apply_effects_to_scene(scene_root)
return effects_mgr