如何在Blender GUI外通过Python调用bpy实现分类型Blend渲染?
在Flask系统中调用Blender(无GUI)实现模型图渲染的方案
刚好做过类似的Flask+Blender渲染需求,给你梳理下具体的实现步骤,亲测可行~
一、核心思路:Blender后台模式+Python脚本控制
Blender支持在无GUI的后台模式下运行,通过--background参数启动,同时可以指定一个Python脚本,用Blender自带的bpy库完成加载指定.blend文件、替换用户上传的纹理图、设置渲染参数、输出最终渲染结果的全流程,完全不需要打开Blender的GUI界面,非常适合集成到Web系统里。
二、编写Blender渲染脚本(基于bpy)
你需要写一个单独的Python脚本(比如render_model.py),用来处理加载对应版型的.blend文件、替换用户上传的图片到模型材质、设置渲染参数、输出渲染结果这一系列操作。
以竖版的portrait.blend为例,脚本示例如下:
import bpy import sys import os def main(): # 从命令行获取外部传入的参数:用户上传图片路径、渲染输出路径、目标blend文件路径 # 注:sys.argv前3项是blender、--background、脚本名,所以从第4项开始取业务参数 args = sys.argv[4:] if len(args) != 3: print("Usage: blender --background --python render_model.py -- <input_image_path> <output_path> <blend_file_path>") return input_image_path = args[0] output_path = args[1] blend_file_path = args[2] # 清空默认场景,加载指定的blend文件 bpy.ops.wm.read_factory_settings(use_empty=True) bpy.ops.wm.open_mainfile(filepath=blend_file_path) # 替换材质中的纹理图片(这里假设你的blend文件里有一个名为"ModelTexture"的图像纹理节点) for image in bpy.data.images: if image.name == "ModelTexture": image.filepath = input_image_path image.reload() break # 设置渲染参数(可根据版型调整分辨率,比如竖版1080x1920) scene = bpy.context.scene scene.render.image_settings.file_format = 'PNG' scene.render.filepath = output_path # 若blend文件已预设好分辨率,可注释掉下面两行 # scene.render.resolution_x = 1080 # scene.render.resolution_y = 1920 # 执行渲染并保存结果 bpy.ops.render.render(write_still=True) if __name__ == "__main__": main()
三、在Flask中调用Blender命令
在Flask的视图函数里,当用户上传图片并完成裁剪后,根据版型选择对应的.blend文件,然后用subprocess模块调用Blender的命令行来执行上面的脚本。
示例代码如下:
from flask import Flask, request, jsonify import subprocess import os from werkzeug.utils import secure_filename app = Flask(__name__) # 配置文件存储路径 app.config['UPLOAD_FOLDER'] = './uploads' app.config['RENDER_FOLDER'] = './renders' # 确保文件夹存在 os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(app.config['RENDER_FOLDER'], exist_ok=True) # 版型与对应blend文件的映射关系 LAYOUT_BLEND_MAP = { 'portrait': './blends/portrait.blend', # 竖版 'square': './blends/square.blend', # 方形 'landscape': './blends/landscape.blend' # 横版 } @app.route('/render-model', methods=['POST']) def render_model(): # 校验上传文件和版型参数 if 'image' not in request.files: return jsonify({'error': '未上传图片文件'}), 400 file = request.files['image'] layout = request.form.get('layout', 'square') if layout not in LAYOUT_BLEND_MAP: return jsonify({'error': '无效的版型类型'}), 400 # 保存裁剪后的用户上传图片 filename = secure_filename(file.filename) input_image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(input_image_path) # 设置渲染输出路径 output_filename = f'rendered_{filename}' output_path = os.path.join(app.config['RENDER_FOLDER'], output_filename) # 获取对应版型的blend文件路径 blend_file_path = LAYOUT_BLEND_MAP[layout] # 构建Blender执行命令(注意:若Blender不在系统PATH中,需写绝对路径,比如Windows下的"C:\Program Files\Blender Foundation\Blender 3.6\blender.exe") blender_cmd = [ 'blender', '--background', '--python', './render_model.py', '--', # 分隔符,后面的参数会传递给渲染脚本 input_image_path, output_path, blend_file_path ] try: # 执行渲染命令,捕获输出信息 result = subprocess.run(blender_cmd, capture_output=True, text=True, check=True) print("渲染日志:", result.stdout) return jsonify({'message': '渲染完成', 'output_path': output_path}), 200 except subprocess.CalledProcessError as e: print("渲染错误:", e.stderr) return jsonify({'error': f'渲染失败:{e.stderr}'}), 500 if __name__ == '__main__': app.run(debug=True)
四、关键注意事项
- Blender路径配置:确保Flask进程能找到Blender可执行文件,若不在系统PATH中,直接写绝对路径即可。
- 文件权限:Flask运行的用户需要拥有读取.blend文件、写入渲染输出文件夹的权限。
- 纹理节点命名:脚本中要替换的纹理节点名称(比如示例中的"ModelTexture")必须和你的.blend文件里的节点名称完全一致。
- 性能优化:渲染是耗时操作,建议把渲染任务放到异步队列(比如Celery)中,避免阻塞Flask的请求响应。
- 路径处理:尽量使用绝对路径,避免相对路径带来的文件找不到问题,可通过
os.path.abspath()转换路径。
内容的提问来源于stack exchange,提问作者Seb




