From 3f0baae7d343c581cb517c8e89a63b608b646183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=85=9C?= <2082529121@qq.com> Date: Fri, 13 Feb 2026 11:11:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=9B=BE=E5=92=8C=E6=B3=95=E7=BA=BF=E5=9B=BE=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增场景属性 `sna_render_resolution` 用于选择渲染分辨率(1024x1024 或 2048x2048) - 修改 `Comfy_OT_Mvgen_B6229` 操作符,在执行后自动渲染深度图和法线图 - 新增 `_render_depth` 和 `_render_normal` 函数,从 `Materials.blend` 资产文件加载专用材质和节点组进行渲染 - 在 UI 面板中添加分辨率选择下拉菜单并更新按钮文本 - 在 `unregister` 函数中安全清理新增的场景属性 --- __init__.py | 11 +++++ utils.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 135 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index f2d2fc7..81184b5 100644 --- a/__init__.py +++ b/__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"): diff --git a/utils.py b/utils.py index 5bbec8e..868ccd3 100644 --- a/utils.py +++ b/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 = (