first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.venv/
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"mathutils"
|
||||
]
|
||||
}
|
||||
172
README.md
Normal file
172
README.md
Normal file
@@ -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建模软件
|
||||
- 光学系统设计社区的支持和反馈
|
||||
339
e8caffb4622e03b1495bbc1ed13fce13.json
Normal file
339
e8caffb4622e03b1495bbc1ed13fce13.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
ea0eb63c015d34fdb4b1147339f51351.py
Normal file
6
ea0eb63c015d34fdb4b1147339f51351.py
Normal file
@@ -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()))
|
||||
BIN
optical_system.blend
Normal file
BIN
optical_system.blend
Normal file
Binary file not shown.
BIN
optical_system.glb
Normal file
BIN
optical_system.glb
Normal file
Binary file not shown.
BIN
optical_system_render.png
Normal file
BIN
optical_system_render.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
148
run_blender.py
Normal file
148
run_blender.py
Normal file
@@ -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()
|
||||
330
toModel.py
Normal file
330
toModel.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user