import bpy import json import mathutils import os import bmesh import sys # 获取命令行参数 def get_json_path(): """从命令行参数获取JSON文件路径""" if len(sys.argv) > 5: # Blender传递的参数格式: blender --background --python script.py -- json_path # 查找 -- 分隔符后的参数 for i, arg in enumerate(sys.argv): if arg == "--" and i + 1 < len(sys.argv): return sys.argv[i + 1] # 如果没有找到参数,使用默认路径 return "./e8caffb4622e03b1495bbc1ed13fce13.json" # 清空场景 bpy.ops.wm.read_factory_settings(use_empty=True) # 设置渲染引擎为Cycles(更好的材质渲染) bpy.context.scene.render.engine = 'CYCLES' # 加载 JSON 文件 json_path = get_json_path() print(f"尝试加载JSON文件:{json_path}") try: with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) print(f"✅ 成功加载JSON文件:{json_path}") print(f"包含 {len(data['children'])} 个对象") except FileNotFoundError: print(f"❌ 错误:找不到JSON文件 {json_path}") sys.exit(1) except json.JSONDecodeError as e: print(f"❌ 错误:JSON文件格式错误 {e}") sys.exit(1) except Exception as e: print(f"❌ 错误:加载JSON文件时出错 {e}") sys.exit(1) # 创建金属材质 def create_metal_material(): mat = bpy.data.materials.new(name="metal") mat.use_nodes = True bsdf = mat.node_tree.nodes["Principled BSDF"] bsdf.inputs["Metallic"].default_value = 1.0 bsdf.inputs["Roughness"].default_value = 0.2 bsdf.inputs["Base Color"].default_value = (0.8, 0.8, 0.9, 1.0) # 淡蓝色金属 return mat # 创建透明材质(用于 nothing) def create_nothing_material(): mat = bpy.data.materials.new(name="nothing") mat.use_nodes = True bsdf = mat.node_tree.nodes["Principled BSDF"] bsdf.inputs["Alpha"].default_value = 0.1 bsdf.inputs["Base Color"].default_value = (1.0, 1.0, 1.0, 0.1) mat.blend_method = 'BLEND' mat.show_transparent_back = False return mat # 创建发光材质(用于特殊对象) def create_emissive_material(): mat = bpy.data.materials.new(name="emissive") mat.use_nodes = True bsdf = mat.node_tree.nodes["Principled BSDF"] # 在Blender 4.0+中,发光属性可能位置不同 try: if "Emission" in bsdf.inputs: bsdf.inputs["Emission"].default_value = (1.0, 0.8, 0.2, 1.0) elif "Emission Color" in bsdf.inputs: bsdf.inputs["Emission Color"].default_value = (1.0, 0.8, 0.2, 1.0) if "Emission Strength" in bsdf.inputs: bsdf.inputs["Emission Strength"].default_value = 2.0 else: # 备用方案:使用较亮的基础颜色 bsdf.inputs["Base Color"].default_value = (1.0, 0.8, 0.2, 1.0) except Exception as e: print(f"设置发光材质时出错: {e}") # 备用方案:创建简单的明亮材质 bsdf.inputs["Base Color"].default_value = (1.0, 0.8, 0.2, 1.0) bsdf.inputs["Roughness"].default_value = 0.1 return mat # 创建材质 metal_mat = create_metal_material() nothing_mat = create_nothing_material() emissive_mat = create_emissive_material() # 创建更精确的抛物面 def create_parabolic_surface(face_f, size=2.0, resolution=32): bm = bmesh.new() # 创建抛物面网格 for i in range(resolution): for j in range(resolution): u = (i / (resolution - 1) - 0.5) * size v = (j / (resolution - 1) - 0.5) * size # 抛物面方程: z = (u^2 + v^2) / (4*f) if face_f > 0 and face_f < 1e9: # 避免除零和无限大 z = (u*u + v*v) / (4 * face_f) else: z = 0 bm.verts.new((u, v, z)) bm.verts.ensure_lookup_table() # 创建面 for i in range(resolution - 1): for j in range(resolution - 1): v1 = i * resolution + j v2 = i * resolution + j + 1 v3 = (i + 1) * resolution + j + 1 v4 = (i + 1) * resolution + j bm.faces.new([bm.verts[v1], bm.verts[v2], bm.verts[v3], bm.verts[v4]]) # 创建网格对象 mesh = bpy.data.meshes.new("parabolic_surface") bm.to_mesh(mesh) bm.free() obj = bpy.data.objects.new("parabolic_surface", mesh) bpy.context.collection.objects.link(obj) return obj # 创建双曲面 def create_hyperbolic_surface(face_f, face_g, size=2.0, resolution=32): bm = bmesh.new() # 创建双曲面网格 for i in range(resolution): for j in range(resolution): u = (i / (resolution - 1) - 0.5) * size v = (j / (resolution - 1) - 0.5) * size # 双曲面方程: z^2/f^2 - (u^2 + v^2)/g^2 = 1 if abs(face_g) > 0.1 and abs(face_f) > 0.1: try: z_squared = face_f * face_f * (1 + (u*u + v*v) / (face_g * face_g)) if z_squared >= 0: z = (face_f if face_f > 0 else -face_f) * (z_squared ** 0.5) else: z = 0 except: z = 0 else: z = 0 bm.verts.new((u, v, z)) bm.verts.ensure_lookup_table() # 创建面 for i in range(resolution - 1): for j in range(resolution - 1): v1 = i * resolution + j v2 = i * resolution + j + 1 v3 = (i + 1) * resolution + j + 1 v4 = (i + 1) * resolution + j bm.faces.new([bm.verts[v1], bm.verts[v2], bm.verts[v3], bm.verts[v4]]) mesh = bpy.data.meshes.new("hyperbolic_surface") bm.to_mesh(mesh) bm.free() obj = bpy.data.objects.new("hyperbolic_surface", mesh) bpy.context.collection.objects.link(obj) return obj # 根据面类型创建物体 def create_geometry(obj_data): face_geom = obj_data.get("face_geometry", "plane") face_type = obj_data.get("face_type", "") name = obj_data.get("name", "noname") pos = obj_data["p"] quat = obj_data["q"] material_name = obj_data.get("draw_material", "metal") face_f = obj_data.get("face_f", 1.0) face_g = obj_data.get("face_g", 1.0) print(f"创建对象: {name} ({face_geom})") mesh = None try: if face_geom == "plane": bpy.ops.mesh.primitive_plane_add(size=2) mesh = bpy.context.active_object elif face_geom == "parabola": if face_f < 1e9 and face_f > 0.1: # 使用精确抛物面 mesh = create_parabolic_surface(face_f) else: # 使用简化圆锥 bpy.ops.mesh.primitive_cone_add(vertices=32, radius1=1.5, radius2=0.1, depth=2.0) mesh = bpy.context.active_object elif face_geom == "hyperbola": if abs(face_f) > 0.1 and abs(face_g) > 0.1: # 使用精确双曲面 mesh = create_hyperbolic_surface(face_f, face_g) else: # 使用简化环面 bpy.ops.mesh.primitive_torus_add(major_radius=1.0, minor_radius=0.3) mesh = bpy.context.active_object else: # 默认立方体 bpy.ops.mesh.primitive_cube_add(size=1.0) mesh = bpy.context.active_object except Exception as e: print(f"创建几何体时出错: {e}") # 备用:创建简单立方体 bpy.ops.mesh.primitive_cube_add(size=0.5) mesh = bpy.context.active_object # 设置对象属性 mesh.name = name mesh.location = pos mesh.rotation_mode = 'QUATERNION' mesh.rotation_quaternion = mathutils.Quaternion(quat) # 设置材质 if material_name == "metal": mesh.data.materials.append(metal_mat) elif material_name == "nothing": mesh.data.materials.append(nothing_mat) else: mesh.data.materials.append(emissive_mat) return mesh # 创建集合来组织对象 collection = bpy.data.collections.new("OpticalSystem") bpy.context.scene.collection.children.link(collection) # 遍历并创建所有子对象 created_objects = [] for i, child in enumerate(data["children"]): try: obj = create_geometry(child) created_objects.append(obj) # 将对象移动到专用集合 bpy.context.collection.objects.unlink(obj) collection.objects.link(obj) except Exception as e: print(f"创建对象 {i} 时出错: {e}") print(f"成功创建了 {len(created_objects)} 个对象") # 添加相机和灯光 def setup_scene(): # 添加相机 bpy.ops.object.camera_add(location=(15, -15, 10)) camera = bpy.context.active_object camera.rotation_euler = (1.1, 0, 0.785) bpy.context.scene.camera = camera # 添加主灯光 bpy.ops.object.light_add(type='SUN', location=(5, 5, 10)) sun = bpy.context.active_object sun.data.energy = 3.0 # 添加环境光 bpy.ops.object.light_add(type='AREA', location=(-5, -5, 8)) area = bpy.context.active_object area.data.energy = 50.0 area.data.size = 10.0 setup_scene() # 设置渲染参数 scene = bpy.context.scene scene.render.resolution_x = 1920 scene.render.resolution_y = 1080 scene.render.image_settings.file_format = 'PNG' # 导出函数 def export_model(): print(f"准备导出 {len(created_objects)} 个对象...") if not created_objects: print("警告:没有创建任何对象,跳过导出") return # 选择所有创建的对象 bpy.ops.object.select_all(action='DESELECT') for obj in created_objects: if obj and obj.name in bpy.data.objects: obj.select_set(True) print(f"已选择对象: {obj.name}") try: # 导出为OBJ格式 obj_path = os.path.abspath("./optical_system.obj") bpy.ops.export_scene.obj(filepath=obj_path, use_selection=True, use_materials=True) print(f"模型已导出为: {obj_path}") except Exception as e: print(f"OBJ导出失败: {e}") try: # 导出为GLB格式(适合web展示) glb_path = os.path.abspath("./optical_system.glb") bpy.ops.export_scene.gltf(filepath=glb_path, use_selection=True, export_materials='EXPORT') print(f"模型已导出为: {glb_path}") except Exception as e: print(f"GLB导出失败: {e}") try: # 保存Blender文件 blend_path = os.path.abspath("./optical_system.blend") bpy.ops.wm.save_as_mainfile(filepath=blend_path) print(f"Blender文件已保存为: {blend_path}") except Exception as e: print(f"Blender文件保存失败: {e}") # 渲染图像 def render_image(): try: render_path = os.path.abspath("./optical_system_render.png") scene.render.filepath = render_path print(f"开始渲染到: {render_path}") bpy.ops.render.render(write_still=True) print(f"渲染图像已保存为: {render_path}") except Exception as e: print(f"渲染失败: {e}") import traceback traceback.print_exc() # 执行导出和渲染 try: print("开始导出模型...") export_model() print("开始渲染图像...") render_image() print("脚本执行完成!") print("生成的文件:") print("- optical_system.obj (3D模型)") print("- optical_system.glb (Web友好格式)") print("- optical_system.blend (Blender工程文件)") print("- optical_system_render.png (渲染图像)") except Exception as e: print(f"执行导出/渲染时出错: {e}") import traceback traceback.print_exc()