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_activeobject = bpy.props.PointerProperty(type=bpy.types.Object)
|
||||||
bpy.types.Scene.sna_bakecam = 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_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():
|
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"):
|
if hasattr(bpy.types.Scene, "sna_activeobject_name"):
|
||||||
del bpy.types.Scene.sna_activeobject_name
|
del bpy.types.Scene.sna_activeobject_name
|
||||||
if hasattr(bpy.types.Scene, "sna_bakecam"):
|
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"].render.engine = "CYCLES"
|
||||||
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_z = True
|
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_z = True
|
||||||
bpy.data.scenes["Scene"].cycles.samples = 16
|
bpy.data.scenes["Scene"].cycles.samples = 16
|
||||||
bpy.data.scenes["Scene"].render.resolution_x = 1024
|
res = int(bpy.context.scene.sna_render_resolution)
|
||||||
bpy.data.scenes["Scene"].render.resolution_y = 1024
|
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.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_normal = True
|
||||||
bpy.context.scene.sna_activeobject = (
|
bpy.context.scene.sna_activeobject = (
|
||||||
bpy.context.view_layer.objects.active
|
bpy.context.view_layer.objects.active
|
||||||
@@ -103,10 +104,119 @@ class Comfy_OT_Aovbake_Ad980(bpy.types.Operator):
|
|||||||
return self.execute(context)
|
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):
|
class Comfy_OT_Mvgen_B6229(bpy.types.Operator):
|
||||||
bl_idname = "comfyui.mvgen_b6229"
|
bl_idname = "comfyui.mvgen_b6229"
|
||||||
bl_label = "mvgen"
|
bl_label = "生成多视角视图"
|
||||||
bl_description = ""
|
bl_description = "Generate multi-view maps including depth and normal"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -149,6 +259,13 @@ class Comfy_OT_Mvgen_B6229(bpy.types.Operator):
|
|||||||
bpy.ops.comfyui.aovbake_ad980('INVOKE_DEFAULT', )
|
bpy.ops.comfyui.aovbake_ad980('INVOKE_DEFAULT', )
|
||||||
bpy.data.objects[bpy.context.scene.sna_activeobject_name].modifiers.clear()
|
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, )
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, _event):
|
def invoke(self, context, _event):
|
||||||
@@ -162,10 +279,10 @@ class Comfy_PT_ComfyProjectorPanel(bpy.types.Panel):
|
|||||||
bl_region_type = "UI"
|
bl_region_type = "UI"
|
||||||
bl_category = "ComfyProjector"
|
bl_category = "ComfyProjector"
|
||||||
|
|
||||||
def draw(self, _context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator("comfyui.aovbake_ad980", text="AOV Bake")
|
layout.prop(context.scene, "sna_render_resolution", text="Resolution")
|
||||||
layout.operator("comfyui.mvgen_b6229", text="mvgen")
|
layout.operator("comfyui.mvgen_b6229", text="生成多视角视图")
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
|||||||
Reference in New Issue
Block a user