commit 2fad4e7b64a740a6f374481dda19d747393460cf Author: nicomacbookpro <805879871@qq.com> Date: Tue Jul 22 09:28:32 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cafc1c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7d78a5d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "mathutils" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e79fd5e --- /dev/null +++ b/README.md @@ -0,0 +1,172 @@ +# 3D光学系统建模工具 + +这个项目可以将JSON格式的光学系统数据转换为3D模型,使用Blender生成高质量的可视化结果。 + +## 🚀 功能特色 + +- **精确几何建模**: 支持平面、抛物面、双曲面等光学面型 +- **智能材质系统**: 自动应用金属、透明、发光材质 +- **多格式输出**: 生成OBJ、GLB、Blender文件和渲染图像 +- **自动场景设置**: 包含相机、灯光和渲染配置 +- **错误处理**: 完善的异常处理和日志输出 + +## 📋 系统要求 + +- **Blender 3.6+** (推荐 4.0+) +- **Python 3.7+** +- **操作系统**: Windows, macOS, Linux + +## 🛠️ 安装 + +### 1. 安装Blender +从官网下载并安装: https://www.blender.org/download/ + +### 2. 验证安装 +```bash +# 确保可以从命令行访问Blender +blender --version +``` + +### 3. 准备数据文件 +确保您的JSON数据文件格式正确,包含以下结构: +```json +{ + "p": [0.0, 0.0, 0.0], + "q": [1.0, 0.0, 0.0, 0.0], + "name": "", + "children": [ + { + "p": [x, y, z], + "q": [w, x, y, z], + "name": "对象名称", + "face_type": "symm", + "face_geometry": "plane|parabola|hyperbola", + "face_f": 1.0, + "face_g": 1.0, + "draw_material": "metal|nothing" + } + ] +} +``` + +## 🎯 使用方法 + +### 方法1: 使用运行脚本(推荐) +```bash +python run_blender.py +``` + +### 方法2: 直接运行Blender脚本 +```bash +blender --background --python toModel.py +``` + +### 方法3: 在Blender GUI中运行 +1. 打开Blender +2. 切换到Scripting工作区 +3. 打开 `toModel.py` 文件 +4. 点击运行按钮 + +## 📁 输出文件 + +运行成功后,将生成以下文件: + +| 文件名 | 格式 | 用途 | +|--------|------|------| +| `optical_system.obj` | OBJ | 通用3D模型格式,可在大多数3D软件中打开 | +| `optical_system.glb` | GLB | Web友好格式,适合在线展示 | +| `optical_system.blend` | Blender | 完整的Blender工程文件,包含所有材质和设置 | +| `optical_system_render.png` | PNG | 高质量渲染图像 | + +## ⚙️ 配置选项 + +### 几何体类型支持 +- **plane**: 平面镜/透镜 +- **parabola**: 抛物面镜,使用 `face_f` 参数控制焦距 +- **hyperbola**: 双曲面镜,使用 `face_f` 和 `face_g` 参数 + +### 材质类型 +- **metal**: 金属反射材质(淡蓝色) +- **nothing**: 透明材质(10%不透明度) +- **其他**: 发光材质(橙色光) + +### 渲染设置 +- 分辨率: 1920x1080 +- 引擎: Cycles(支持高质量材质) +- 格式: PNG + +## 🔧 自定义 + +### 修改材质 +编辑 `toModel.py` 中的材质创建函数: +```python +def create_metal_material(): + # 修改金属材质属性 + bsdf.inputs["Base Color"].default_value = (R, G, B, A) + bsdf.inputs["Metallic"].default_value = 0.0-1.0 + bsdf.inputs["Roughness"].default_value = 0.0-1.0 +``` + +### 调整几何体精度 +修改分辨率参数: +```python +mesh = create_parabolic_surface(face_f, size=2.0, resolution=64) # 更高精度 +``` + +### 修改渲染设置 +```python +scene.render.resolution_x = 3840 # 4K分辨率 +scene.render.resolution_y = 2160 +``` + +## 🐛 故障排除 + +### 常见问题 + +**Q: 找不到Blender** +``` +❌ 错误:未找到Blender安装 +``` +A: 确保Blender已正确安装并添加到系统PATH中 + +**Q: 几何体创建失败** +``` +创建几何体时出错: ... +``` +A: 检查JSON数据中的 `face_f` 和 `face_g` 参数是否为有效数值 + +**Q: 导出失败** +``` +模型导出失败 +``` +A: 确保有写入权限,检查磁盘空间 + +### 调试技巧 + +1. **查看详细日志**: 脚本会输出详细的创建过程 +2. **检查JSON数据**: 确保所有必需字段都存在 +3. **逐步运行**: 在Blender GUI中逐步执行脚本 + +## 📈 性能优化 + +### 大型数据集 +- 对于超过100个对象的数据集,考虑分批处理 +- 降低几何体分辨率以提高性能 +- 使用后台模式避免GUI开销 + +### 内存使用 +- 大型模型可能需要8GB+内存 +- 监控内存使用,必要时重启Blender + +## 🤝 贡献 + +欢迎提交Issue和Pull Request来改进这个工具! + +## 📄 许可证 + +本项目采用MIT许可证。 + +## 🙏 致谢 + +- Blender Foundation 提供的优秀3D建模软件 +- 光学系统设计社区的支持和反馈 \ No newline at end of file diff --git a/e8caffb4622e03b1495bbc1ed13fce13.json b/e8caffb4622e03b1495bbc1ed13fce13.json new file mode 100644 index 0000000..2a367f5 --- /dev/null +++ b/e8caffb4622e03b1495bbc1ed13fce13.json @@ -0,0 +1,339 @@ +{ + "p": [ + 0.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "", + "children": [ + { + "p": [ + -6.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "E0", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "nothing" + }, + { + "p": [ + -6.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "M00", + "face_type": "symm", + "face_geometry": "parabola", + "face_f": 5.0, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + -6.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "M10", + "face_type": "symm", + "face_geometry": "parabola", + "face_f": 1.0, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + -6.0, + 0.0, + -5.974533048268448 + ], + "q": [ + 0.9238795325112867, + -0.0, + 0.3826834323650898, + 0.0 + ], + "name": "M20", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 6.0, + 0.0, + 0.0 + ], + "q": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "E1", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "nothing" + }, + { + "p": [ + 6.0, + 0.0, + 0.0 + ], + "q": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "M01", + "face_type": "symm", + "face_geometry": "parabola", + "face_f": 5.0, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 6.0, + 0.0, + 0.0 + ], + "q": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "M11", + "face_type": "symm", + "face_geometry": "parabola", + "face_f": 1.0, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 6.0, + 0.0, + -5.974533048268448 + ], + "q": [ + 0.0, + -0.3826834323650898, + 0.0, + 0.9238795325112867 + ], + "name": "M21", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + -1.9818052586129662, + 0.0, + -5.974533048268448 + ], + "q": [ + 0.9238795325112867, + 0.0, + -0.3826834323650898, + 0.0 + ], + "name": "M30", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 1.9818052586129662, + 0.0, + -5.974533048268448 + ], + "q": [ + 0.9238795325112867, + -0.0, + 0.3826834323650898, + 0.0 + ], + "name": "M31", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 0.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "M6", + "face_type": "symm", + "face_geometry": "parabola", + "face_f": 5.0, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 0.0, + 0.0, + 0.0 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "M7", + "face_type": "symm", + "face_geometry": "hyperbola", + "face_f": 1.0, + "face_g": -6.875220831710909, + "draw_material": "metal" + }, + { + "p": [ + 0.0, + 0.0, + -7.875220831710909 + ], + "q": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "name": "D0", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + -1.9818052586129662, + 0.0, + 0.0 + ], + "q": [ + 0.9238795325112867, + 0.0, + -0.3826834323650898, + 0.0 + ], + "name": "M40", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + -1.0, + 0.0, + 0.0 + ], + "q": [ + 0.9238795325112867, + -0.0, + 0.3826834323650898, + 0.0 + ], + "name": "M50", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 1.9818052586129662, + 0.0, + 0.0 + ], + "q": [ + 0.9238795325112867, + -0.0, + 0.3826834323650898, + 0.0 + ], + "name": "M41", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + }, + { + "p": [ + 1.0, + 0.0, + 0.0 + ], + "q": [ + 0.9238795325112867, + 0.0, + -0.3826834323650898, + 0.0 + ], + "name": "M51", + "face_type": "symm", + "face_geometry": "plane", + "face_f": 1e10, + "face_g": 1e10, + "draw_material": "metal" + } + ] +} \ No newline at end of file diff --git a/ea0eb63c015d34fdb4b1147339f51351.py b/ea0eb63c015d34fdb4b1147339f51351.py new file mode 100644 index 0000000..12574e3 --- /dev/null +++ b/ea0eb63c015d34fdb4b1147339f51351.py @@ -0,0 +1,6 @@ +import silent_raindrops.distoptics.SIM as op +import json + + +the_sim = op.SIM(init_b=12.0) +print(json.dumps(the_sim.to_dict_tsingtao())) diff --git a/optical_system.blend b/optical_system.blend new file mode 100644 index 0000000..b09abf1 Binary files /dev/null and b/optical_system.blend differ diff --git a/optical_system.glb b/optical_system.glb new file mode 100644 index 0000000..a061330 Binary files /dev/null and b/optical_system.glb differ diff --git a/optical_system_render.png b/optical_system_render.png new file mode 100644 index 0000000..f84a2b2 Binary files /dev/null and b/optical_system_render.png differ diff --git a/run_blender.py b/run_blender.py new file mode 100644 index 0000000..d9ead0b --- /dev/null +++ b/run_blender.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +""" +运行脚本 - 使用Blender生成3D光学系统模型 + +使用方法: +1. 确保已安装Blender +2. 运行此脚本: python run_blender.py + +输出文件: +- optical_system.obj - 3D模型文件 +- optical_system.glb - Web友好格式 +- optical_system.blend - Blender工程文件 +- optical_system_render.png - 渲染图像 +""" + +import subprocess +import sys +import os +import platform + +def find_blender(): + """查找Blender可执行文件""" + system = platform.system() + + common_paths = [] + if system == "Windows": + common_paths = [ + r"C:\Program Files\Blender Foundation\Blender 3.6\blender.exe", + r"C:\Program Files\Blender Foundation\Blender 4.0\blender.exe", + r"C:\Program Files (x86)\Blender Foundation\Blender 3.6\blender.exe", + ] + elif system == "Darwin": # macOS + common_paths = [ + "/Applications/Blender.app/Contents/MacOS/Blender", + "/usr/local/bin/blender", + ] + elif system == "Linux": + common_paths = [ + "/usr/bin/blender", + "/usr/local/bin/blender", + "/snap/bin/blender", + ] + + # 首先尝试从PATH中找到blender + try: + result = subprocess.run(["which", "blender"], capture_output=True, text=True) + if result.returncode == 0: + return result.stdout.strip() + except: + pass + + # 尝试常见路径 + for path in common_paths: + if os.path.exists(path): + return path + + return None + +def run_blender_script(): + """运行Blender脚本""" + blender_path = find_blender() + + if not blender_path: + print("❌ 错误:未找到Blender安装") + print("\n请确保已安装Blender并且可以从命令行访问") + print("下载地址: https://www.blender.org/download/") + return False + + print(f"✅ 找到Blender: {blender_path}") + + script_path = os.path.abspath("toModel.py") + if not os.path.exists(script_path): + print(f"❌ 错误:未找到脚本文件 {script_path}") + return False + + json_path = os.path.abspath("e8caffb4622e03b1495bbc1ed13fce13.json") + if not os.path.exists(json_path): + print(f"❌ 错误:未找到JSON数据文件 {json_path}") + return False + + print("🚀 开始运行Blender脚本...") + print("这可能需要几分钟时间,请耐心等待...") + + try: + # 运行Blender脚本 + cmd = [ + blender_path, + "--background", # 后台运行 + "--python", script_path + ] + + result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) + + if result.returncode == 0: + print("✅ Blender脚本执行成功!") + print("\n输出信息:") + print(result.stdout) + + # 检查生成的文件 + expected_files = [ + "optical_system.obj", + "optical_system.glb", + "optical_system.blend", + "optical_system_render.png" + ] + + print("\n📁 生成的文件:") + for filename in expected_files: + if os.path.exists(filename): + size = os.path.getsize(filename) + print(f"✅ {filename} ({size:,} bytes)") + else: + print(f"❌ {filename} (未找到)") + + return True + else: + print("❌ Blender脚本执行失败") + print("错误输出:") + print(result.stderr) + if result.stdout: + print("标准输出:") + print(result.stdout) + return False + + except subprocess.TimeoutExpired: + print("❌ 脚本执行超时(超过5分钟)") + return False + except Exception as e: + print(f"❌ 执行时出错: {e}") + return False + +def main(): + print("🎨 Blender 3D光学系统建模脚本") + print("=" * 40) + + if run_blender_script(): + print("\n🎉 任务完成!") + print("\n您可以:") + print("1. 使用Blender打开 optical_system.blend 文件进行编辑") + print("2. 在3D软件中导入 optical_system.obj 文件") + print("3. 在Web浏览器中查看 optical_system.glb 文件") + print("4. 查看渲染图像 optical_system_render.png") + else: + print("\n❌ 任务失败,请检查错误信息") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/toModel.py b/toModel.py new file mode 100644 index 0000000..2882b1a --- /dev/null +++ b/toModel.py @@ -0,0 +1,330 @@ +import bpy +import json +import mathutils +import os +import bmesh + +# 清空场景 +bpy.ops.wm.read_factory_settings(use_empty=True) + +# 设置渲染引擎为Cycles(更好的材质渲染) +bpy.context.scene.render.engine = 'CYCLES' + +# 加载 JSON 文件 +json_path = "./e8caffb4622e03b1495bbc1ed13fce13.json" +with open(json_path, 'r', encoding='utf-8') as f: + data = json.load(f) + +print(f"已加载JSON文件:{json_path}") +print(f"包含 {len(data['children'])} 个对象") + +# 创建金属材质 +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()