Orange7033 2025-01-22 10:01 采纳率: 0%
浏览 6
已结题

(标签-blender|关键词-apt)

我在搞一个网格轻量化的流程,现在卡到用blender为简化后的网格重新烘焙颜色贴图的这个步骤上了,有ABC个文件夹,我想把A文件夹中的原网格的纹理烘焙到重新展UV,然后清理材质之后的B文件夹中的目标网格上,然后输出到输出文件夹C中;A文件夹中的网格是ContextCapture Center Master输出的带颜色贴图的OBJ文件。B文件夹中的网格是通过MeshLab简化过的OBJ文件,但是我目前无法正常处理B文件夹中有多个材质球的网格的情况,而且我python算瞎折腾,学的也不怎么明白,求指点或者麻烦帮忙改改

import bpy
import os


class BakePanel(bpy.types.Panel):
    bl_label = "Bake Panel"
    bl_idname = "OBJECT_PT_bake_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "橙_重烘焙"

    def draw(self, context):
        layout = self.layout
        layout.label(text="输入路径 A(原网格):")
        layout.prop(context.scene, "path_a")
        layout.label(text="输入路径 B(目标网格):")
        layout.prop(context.scene, "path_b")
        layout.label(text="输出路径:")
        layout.prop(context.scene, "output_path")
        layout.operator("object.bake_meshes", text="开始烘焙")


class BakeMeshesOperator(bpy.types.Operator):
    bl_idname = "object.bake_meshes"
    bl_label = "Bake Meshes"

    def execute(self, context):
        path_a = bpy.context.scene.path_a
        path_b = bpy.context.scene.path_b
        output_path = bpy.context.scene.output_path
        log_path = os.path.join(output_path, "skipped_files.log")  # 日志文件路径
        skipped_files = []

        def get_matching_file(base_name):
            target_name = base_name.replace(".obj", "_out.obj")
            for file in os.listdir(path_b):
                if file == target_name:
                    return os.path.join(path_b, file)
            return None

        def clear_scene():
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete()
            bpy.ops.outliner.orphans_purge()  # 清空缓存

        clear_scene()  # 先清理场景

        def bake_and_export(base_name, target_file):
            clear_scene()  # 清空场景
            # 导入原网格和目标网格
            obj_path = os.path.join(path_a, base_name)
            bpy.ops.wm.obj_import(filepath=obj_path)
            obj_path = target_file
            bpy.ops.wm.obj_import(filepath=obj_path)
            original_mesh = bpy.data.objects[base_name.replace(".obj", "")]
            target_mesh = bpy.data.objects[base_name.replace(".obj", "_out")]

            # 将原网格和目标网格的旋转值改为 0
            original_mesh.rotation_euler = (0, 0, 0)
            target_mesh.rotation_euler = (0, 0, 0)

            # 切换到编辑模式并选中目标网格的所有面
            bpy.context.view_layer.objects.active = target_mesh
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.select_all(action='SELECT')
            # 自动展 UV
            bpy.ops.uv.smart_project(
                # 角度限制,角度越小,投影组越多,失真越小;角度越大,投影组越少,失真越大
                angle_limit=1.15192,
                # 边距方法
                # SCALED – 使用现有 UV 的缩放比例来乘以边距。
                # ADD – 只添加边距,忽略任何 UV 缩放。
                # FRACTION – 指定最终 UV 输出的精确分数边距。
                margin_method='SCALED',
                # 旋转方法
                # AXIS_ALIGNED – 旋转到最小矩形,可以是垂直或水平。
                # AXIS_ALIGNED_X – 水平对齐岛屿。
                # AXIS_ALIGNED_Y – 垂直对齐岛屿。
                rotate_method='AXIS_ALIGNED_Y',
                # 岛屿边距,相邻岛屿之间的边距
                island_margin=0.0,
                # 面积权重,按面的面积加权投影向量
                area_weight=0.0,
                # 修正纵横比,根据图像纵横比映射 UV
                correct_aspect=True,
                # 缩放到边界,展开后缩放 UV 坐标到边界
                scale_to_bounds=False
            )
            bpy.ops.object.mode_set(mode='OBJECT')  # 回到物体模式

            # 切换到 UV 编辑模式
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.uv.select_all(action='SELECT')  # 全选 UV 编辑器中的面
            # 对 UV 进行拼排孤岛操作
            bpy.ops.uv.pack_islands(
                udim_source='CLOSEST_UDIM', 
                rotate=True, rotate_method='ANY', 
                scale=True, merge_overlap=False,
                margin_method='SCALED', 
                margin=0.001, 
                pin=False,
                pin_method='LOCKED', 
                shape_method='CONCAVE'
            )
            bpy.ops.object.mode_set(mode='OBJECT')  # 回到物体模式

            # 确保两个对象都存在
            if original_mesh and target_mesh:
                # 将原网格设置为选中对象,目标网格设置为活动对象
                original_mesh.select_set(True)
                bpy.context.view_layer.objects.active = target_mesh

                # 定义 bake_settings
                bake_settings = bpy.context.scene.render.bake
                # 烘焙纹理贴图
                bpy.context.scene.render.engine = 'CYCLES'
                bake_settings.use_pass_direct = False
                bake_settings.use_pass_indirect = False
                bake_settings.use_pass_color = True
                bake_settings.width = 2048
                bake_settings.height = 2048
                bake_settings.use_selected_to_active = True
                bake_settings.use_clear = True
                # 设置 cage_extrusion 为 0.1
                bake_settings.cage_extrusion = 0.1
                # 确保使用 selected_to_active 模式进行正确烘焙
                bpy.ops.object.bake(type='DIFFUSE')

                # 查找生成的纹理
                baked_image = None
                if target_mesh.data.materials:
                    material = target_mesh.data.materials[0]
                    if material.use_nodes:
                        node_tree = material.node_tree
                        bsdf_node = node_tree.nodes.get('Principled BSDF')
                        if bsdf_node and bsdf_node.inputs['Base Color'].is_linked:
                            texture_node = bsdf_node.inputs['Base Color'].links[0].from_node
                            if texture_node.type == 'TEX_IMAGE':
                                baked_image = texture_node.image

                if baked_image:
                    # 生成保存纹理的文件名
                    texture_filename = base_name.replace(".obj", "_baked_texture.png")
                    texture_path = os.path.join(output_path, texture_filename)
                    # 保存纹理
                    baked_image.save_render(texture_path)


                # 取消原网格的选择,只保留目标网格的选择
                original_mesh.select_set(False)
                target_mesh.select_set(True)
                bpy.context.view_layer.objects.active = target_mesh

                # 保存目标网格为 OBJ 文件,并生成 MTL 文件
                obj_output_path = os.path.join(output_path, base_name.replace(".obj", "_baked.obj") )
                bpy.ops.wm.obj_export(
                    filepath=obj_output_path,
                    export_animation=False,
                    start_frame=-2147483648,
                    end_frame=2147483647,
                    forward_axis='NEGATIVE_Z',
                    up_axis='Y',
                    global_scale=1.0,
                    apply_modifiers=True,
                    export_eval_mode='DAG_EVAL_VIEWPORT',
                    export_selected_objects=True,  # 仅导出选中对象
                    export_uv=True,
                    export_normals=True,
                    export_colors=False,
                    export_materials=True,
                    export_pbr_extensions=False,
                    path_mode='AUTO',
                    export_triangulated_mesh=False,
                    export_curves_as_nurbs=False,
                    export_object_groups=False,
                    export_material_groups=False,
                    export_vertex_groups=False,
                    export_smooth_groups=False,
                    smooth_group_bitflags=False,
                    filter_glob='*.obj;*.mtl',
                    collection=''
                )
            clear_scene()  # 在每一对模型完成处理后清空场景

        for file in os.listdir(path_a):
            if file.endswith(".obj"):
                target_file = get_matching_file(file)
                if target_file:
                    bake_and_export(file, target_file)
                else:
                    skipped_files.append(file)

        if skipped_files:
            with open(log_path, 'a') as log_file:  # 以追加模式打开日志文件
                for skipped_file in skipped_files:
                    log_file.write(skipped_file + "\n")

        # 添加绿色提示信息
        # 确保提示信息显示在状态栏中
        self.report({'INFO'}, "处理完成")

        return {'FINISHED'}


def register():
    bpy.utils.register_class(BakePanel)
    bpy.utils.register_class(BakeMeshesOperator)
    bpy.types.Scene.path_a = bpy.props.StringProperty(name="Path A", subtype='DIR_PATH')
    bpy.types.Scene.path_b = bpy.props.StringProperty(name="Path B", subtype='DIR_PATH')
    bpy.types.Scene.output_path = bpy.props.StringProperty(name="Output Path", subtype='DIR_PATH')


def unregister():
    bpy.utils.unregister_class(BakePanel)
    bpy.utils.unregister_class(BakeMeshesOperator)
    del bpy.types.Scene.path_a
    del bpy.types.Scene.path_b
    del bpy.types.Scene.output_path


if __name__ == "__main__":
    register()

  • 写回答

2条回答 默认 最新

  • 关注

    以下回复参考:皆我百晓生券券喵儿等免费微信小程序作答:

    从你的问题描述来看,你正在尝试使用Blender的Python API来处理网格的烘焙操作,并且遇到了处理多个材质球网格和目标网格的问题。在你的代码中,对于每一个源网格(带有纹理贴图的OBJ文件),你希望找到对应的简化过的目标网格(带有不同材质球的OBJ文件),然后进行纹理烘焙,并将结果输出到一个新的文件夹中。这是一个复杂的任务,需要对Blender的Python API和纹理烘焙流程有深入的理解。下面是一些针对你的问题的解答和建议:

    首先,你需要理解Blender的材质系统是如何工作的。每一个对象可以有一个或多个材质,每一个材质都有自己的纹理贴图。在你的情况下,你需要处理的是目标网格有多个材质球的情况。这意味着你需要为每个材质球单独进行烘焙操作。Blender Python API提供了修改和获取材质和纹理的方法,你可以使用这些方法来实现你的需求。

    其次,关于你的代码,我有以下几点建议:

    1. 在处理目标网格的多个材质球时,你可能需要遍历每个材质球并分别进行烘焙操作。你可以通过访问target_mesh.data.materials来获取所有的材质,然后遍历它们。
    2. 在进行烘焙操作时,你可能需要设置正确的UV映射。你可以使用Blender的UV映射工具来自动进行UV映射,或者在Python脚本中手动设置UV映射。
    3. 在导出烘焙后的目标网格时,你需要确保所有的材质和纹理都被正确地导出。你可以使用Blender的导出功能来导出OBJ和MTL文件。

    对于具体的代码修改,由于涉及的内容较多,我会给出一个大致的框架和思路,具体的实现需要你自己根据这个框架和思路来完成。你可以创建一个函数来处理目标网格的每一个材质球,然后在遍历源网格文件和目标网格文件时,对每个匹配的文件对进行烘焙操作。在烘焙操作完成后,你可以使用Blender的导出功能将烘焙后的目标网格导出为OBJ和MTL文件。

    最后,我建议你查阅Blender的官方文档和相关的教程,以了解更多的关于Blender Python API和纹理烘焙的信息。这可以帮助你更好地理解如何实现你的需求,并解决可能遇到的问题。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 2月6日
  • 创建了问题 1月22日