feat: 添加深度图和法线图渲染功能
- 新增场景属性 `sna_render_resolution` 用于选择渲染分辨率(1024x1024 或 2048x2048) - 修改 `Comfy_OT_Mvgen_B6229` 操作符,在执行后自动渲染深度图和法线图 - 新增 `_render_depth` 和 `_render_normal` 函数,从 `Materials.blend` 资产文件加载专用材质和节点组进行渲染 - 在 UI 面板中添加分辨率选择下拉菜单并更新按钮文本 - 在 `unregister` 函数中安全清理新增的场景属性
This commit is contained in:
11
__init__.py
11
__init__.py
@@ -21,9 +21,20 @@ def register():
|
||||
bpy.types.Scene.sna_activeobject = bpy.props.PointerProperty(type=bpy.types.Object)
|
||||
bpy.types.Scene.sna_bakecam = bpy.props.PointerProperty(type=bpy.types.Object)
|
||||
bpy.types.Scene.sna_activeobject_name = bpy.props.StringProperty()
|
||||
bpy.types.Scene.sna_render_resolution = bpy.props.EnumProperty(
|
||||
name="Render Resolution",
|
||||
description="Resolution for depth and normal map renders",
|
||||
items=[
|
||||
('1024', '1024 x 1024', ''),
|
||||
('2048', '2048 x 2048', ''),
|
||||
],
|
||||
default='1024',
|
||||
)
|
||||
|
||||
|
||||
def unregister():
|
||||
if hasattr(bpy.types.Scene, "sna_render_resolution"):
|
||||
del bpy.types.Scene.sna_render_resolution
|
||||
if hasattr(bpy.types.Scene, "sna_activeobject_name"):
|
||||
del bpy.types.Scene.sna_activeobject_name
|
||||
if hasattr(bpy.types.Scene, "sna_bakecam"):
|
||||
|
||||
131
utils.py
131
utils.py
@@ -35,8 +35,9 @@ class Comfy_OT_Aovbake_Ad980(bpy.types.Operator):
|
||||
bpy.data.scenes["Scene"].render.engine = "CYCLES"
|
||||
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_z = True
|
||||
bpy.data.scenes["Scene"].cycles.samples = 16
|
||||
bpy.data.scenes["Scene"].render.resolution_x = 1024
|
||||
bpy.data.scenes["Scene"].render.resolution_y = 1024
|
||||
res = int(bpy.context.scene.sna_render_resolution)
|
||||
bpy.data.scenes["Scene"].render.resolution_x = res
|
||||
bpy.data.scenes["Scene"].render.resolution_y = res
|
||||
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_normal = True
|
||||
bpy.context.scene.sna_activeobject = (
|
||||
bpy.context.view_layer.objects.active
|
||||
@@ -103,10 +104,119 @@ class Comfy_OT_Aovbake_Ad980(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
def _ensure_output_dir():
|
||||
"""Ensure output directory exists next to the blend file and return its path."""
|
||||
blend_dir = os.path.dirname(bpy.data.filepath)
|
||||
output_dir = os.path.join(blend_dir, 'output')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
return output_dir
|
||||
|
||||
|
||||
def _append_from_materials_blend(data_type, name):
|
||||
"""Append a data-block from Materials.blend if it doesn't already exist.
|
||||
data_type: 'NodeTree' or 'Material'
|
||||
"""
|
||||
collection_map = {
|
||||
'NodeTree': bpy.data.node_groups,
|
||||
'Material': bpy.data.materials,
|
||||
}
|
||||
collection = collection_map[data_type]
|
||||
if name in collection:
|
||||
return collection[name]
|
||||
blend_path = os.path.join(os.path.dirname(__file__), 'assets', 'Materials.blend')
|
||||
bpy.ops.wm.append(
|
||||
directory=blend_path + '\\' + data_type + '\\',
|
||||
filename=name,
|
||||
link=False,
|
||||
)
|
||||
return collection.get(name)
|
||||
|
||||
|
||||
def _render_depth(operator):
|
||||
"""Render depth map using Depth_Mat compositor NodeTree."""
|
||||
scene = bpy.context.scene
|
||||
output_dir = _ensure_output_dir()
|
||||
|
||||
# Append the Depth_Mat compositor node tree
|
||||
depth_tree = _append_from_materials_blend('NodeTree', 'Depth_Mat')
|
||||
if depth_tree is None:
|
||||
operator.report({'ERROR'}, message='Failed to load Depth_Mat from Materials.blend')
|
||||
return False
|
||||
|
||||
# Save original compositor state
|
||||
orig_use_nodes = scene.use_nodes
|
||||
orig_comp_group = scene.compositing_node_group
|
||||
|
||||
# Enable compositor and assign Depth_Mat as the compositing node group
|
||||
scene.use_nodes = True
|
||||
scene.compositing_node_group = depth_tree
|
||||
|
||||
# Setup output
|
||||
scene.render.filepath = os.path.join(output_dir, 'depth.png')
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.image_settings.color_mode = 'RGB'
|
||||
scene.view_layers["ViewLayer"].use_pass_z = True
|
||||
|
||||
# Render
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
# Restore original compositor state
|
||||
scene.compositing_node_group = orig_comp_group
|
||||
scene.use_nodes = orig_use_nodes
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _render_normal(operator):
|
||||
"""Render normal map by applying Normal_Mat material to active object."""
|
||||
scene = bpy.context.scene
|
||||
obj = scene.sna_activeobject
|
||||
output_dir = _ensure_output_dir()
|
||||
|
||||
# Append the Normal_Mat material
|
||||
normal_mat = _append_from_materials_blend('Material', 'Normal_Mat')
|
||||
if normal_mat is None:
|
||||
operator.report({'ERROR'}, message='Failed to load Normal_Mat from Materials.blend')
|
||||
return False
|
||||
|
||||
# Find the converted object (the active object after convert)
|
||||
converted_obj = bpy.context.view_layer.objects.active
|
||||
if converted_obj is None or converted_obj.type != 'MESH':
|
||||
converted_obj = obj
|
||||
|
||||
# Save original materials
|
||||
orig_materials = [slot.material for slot in converted_obj.material_slots]
|
||||
|
||||
# Clear and assign Normal_Mat
|
||||
converted_obj.data.materials.clear()
|
||||
converted_obj.data.materials.append(normal_mat)
|
||||
|
||||
# Disable compositor for normal pass (direct render)
|
||||
orig_use_nodes = scene.use_nodes
|
||||
scene.use_nodes = False
|
||||
|
||||
# Setup output
|
||||
scene.render.filepath = os.path.join(output_dir, 'normal.png')
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.image_settings.color_mode = 'RGB'
|
||||
|
||||
# Render
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
# Restore original materials
|
||||
converted_obj.data.materials.clear()
|
||||
for mat in orig_materials:
|
||||
converted_obj.data.materials.append(mat)
|
||||
|
||||
scene.use_nodes = orig_use_nodes
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Comfy_OT_Mvgen_B6229(bpy.types.Operator):
|
||||
bl_idname = "comfyui.mvgen_b6229"
|
||||
bl_label = "mvgen"
|
||||
bl_description = ""
|
||||
bl_label = "生成多视角视图"
|
||||
bl_description = "Generate multi-view maps including depth and normal"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
@@ -149,6 +259,13 @@ class Comfy_OT_Mvgen_B6229(bpy.types.Operator):
|
||||
bpy.ops.comfyui.aovbake_ad980('INVOKE_DEFAULT', )
|
||||
bpy.data.objects[bpy.context.scene.sna_activeobject_name].modifiers.clear()
|
||||
bpy.context.view_layer.objects[bpy.context.scene.sna_activeobject_name].hide_set(state=True, )
|
||||
|
||||
# --- Render Depth Map ---
|
||||
_render_depth(self)
|
||||
|
||||
# --- Render Normal Map ---
|
||||
_render_normal(self)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, _event):
|
||||
@@ -162,10 +279,10 @@ class Comfy_PT_ComfyProjectorPanel(bpy.types.Panel):
|
||||
bl_region_type = "UI"
|
||||
bl_category = "ComfyProjector"
|
||||
|
||||
def draw(self, _context):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("comfyui.aovbake_ad980", text="AOV Bake")
|
||||
layout.operator("comfyui.mvgen_b6229", text="mvgen")
|
||||
layout.prop(context.scene, "sna_render_resolution", text="Resolution")
|
||||
layout.operator("comfyui.mvgen_b6229", text="生成多视角视图")
|
||||
|
||||
|
||||
classes = (
|
||||
|
||||
Reference in New Issue
Block a user