You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动