在 BatchCompute 上玩转 Blender 渲染

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
对象存储 OSS,恶意文件检测 1000次 1年
简介: > 本篇主要是介绍如何基于 BatchCompute 的 App,提交 Blender 渲染作业。 ## 1. 准备工作 ### (1) 开通服务 * 开通批量计算服务(BatchCompute): https://help.

本篇主要是介绍如何基于 BatchCompute 的 App,提交 Blender 渲染作业。

1. 准备工作

(1) 开通服务

如果已经开通,请忽略此步骤。

(2) 地域的选择

本篇例子所有阿里云服务都需要使用相同的地域。

本篇例子使用地域: 华南1(深圳)

(3) 准备OSS Bucket

本篇例子假设创建的 bucket 名称为:blender-demo, 地域在华南1(深圳)。

注意: 使用批量计算时,地域需要和 OSS bucket 的地域相同。

注意: 实际操作时,需要将例子中的bucket 名称修改为您自己创建的真实的bucket名称。

2. 制作 Blender Docker 镜像

(1) 创建一个Dockerfile文件

文件名:Dockerfile, 内容如下:

FROM ubuntu:latest

MAINTAINER your-name<your-email>

# 更新源
RUN apt update

# 清除缓存
RUN apt autoclean

# 安装
RUN apt install python python-pip curl pulseaudio blender -y

# 启动时运行这个命令
CMD ["/bin/bash"]

(2) build

docker build -t ubuntu-blender ./ 

等待完成,然后使用下面的命令查看是否有 ubuntu-blender

docker images

(3) check

docker run -t ubuntu-blender blender -v

显示:

Blender 2.79 (sub 0)

看到版本信息了吧!

3. Docker镜像上传

您需要将 ubuntu-blender 上传到 BatchCompute 支持Registry。

BatchCompute支持2种Registry:阿里云的 CR(Container Registry)和阿里云的OSS。

选择一种即可,推荐第一种: CR。

如何上传,请参考这2篇文档:

docker 镜像上传到CR

docker 镜像上传到OSS

假设已经上传到CR(地域:华南1-深圳),名称为: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0

4. 创建 App

BatchCompute 提交作业,需要配置很多参数。
BatchCompute 提供的 App 模板机制,让用户很方便预设参数默认值,提交作业时,只需填写少量参数即可。

下面我们来创建一个 Blender 渲染 App。

(1) 开始创建 App

打开批量计算控制台: https://batchcompute.console.aliyun.com

image.png

填写基本信息

Docker 镜像名称,填写您已经上传到CR的镜像名称,如: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0

image.png

运行时参数

只需修改 实例类型为 8核16GB规格,其他的默认即可。

image.png

(2) 命令行和参数配置

image.png

命令行填写:

python -c "import os;import sys;sys.path.append('/home/scripts/'); from render import parseFrames; frames=parseFrames('${frames}'); framestr=','.join(map(lambda x:str(x), frames)); s='blender -b /home/input/${scene_file_path} -o /home/output/result/${output_name_format} -F ${format} -f %s' % framestr; print('exec: %s' % s); os.system(s);"
  • ${..} 都是变量,可以作为输入和输出参数。在使用此App提交作业的时候,传入的参数将替换掉这些变量。
  • 参考文档 Blender 2.79 命令行参数

输入参数

注意: 实际操作时,需要将例子中的bucket 名称修改为您自己创建的真实的bucket名称。

名称 默认值 允许覆盖 本地目录绝对路径 备注
scripts_oss_folder oss://blender-demo/scripts/ /home/scripts/ 输入oss目录路径,该路径将挂载到虚拟机的/home/scripts/,应该包含要渲染的render.py文件, 如: oss://bucket/scripts/
input_oss_folder ️是 /home/input/ 输入oss目录路径,该路径将挂载到虚拟机的/home/input/,应该包含要渲染的.blend文件, 如: oss://bucket/input/
scene_file_path 渲染场景文件的路径,相对于input_oss_folder的目录路径, 如:a.blend 或者 folder_name/a.blend
frames ️是 支持连续帧:"1-10", 支持多帧(逗号隔开,无空格):"1,3,5-10"
format PNG ️是 渲染输出格式,支持: TGA,RAWTGA,JPEG,IRIS,IRIZ,AVIRAW,AVIJPEG,PNG,BMP
output_name_format ####.png ️是 输出文件名,#会被替代为帧序号,不足位补零。举例: test_###.png 变成 test_001.png,可以在前面加目录名: test/test_###.png

输出参数

名称 默认值 允许覆盖 本地目录绝对路径 备注
output_oss_folder ️是 /home/output/ 输出oss目录路径, 如: oss://bucket/output/

环境变量

环境变量可以不用配置, 直接提交即可。

4. 提交渲染作业

在App列表中可以看到已经创建好的 ubuntu-blender, 点击"提交作业"。

image.png

(1) 准备工作

在提交作业前,还有一些准备工作。

手动上传分帧器

分帧器python代码(见附录),上传到您的OSS目录下,比如: oss://blender-demo/scripts/render.py

手动上传blender场景文件

Blender 官网提供了好多 demo 文件: https://www.blender.org/download/demo-files/

本例子需要下载 2.79 版本(注意:要和镜像中安装的Blender版本相同。不同版本的可能渲染不出来)

image.png

素材下载后,解压得到目录: splash279/ 将整个目录上传到 oss://blender-demo/input/ 下面,即:oss://blender-demo/input/splash279/

(2) 开始提交作业

image.png

  • 实例类型要选大一点的,比如: 8核16GB。
  • 实例数量本例子填 2 个。

(3) 参数配置

注意: 实际操作时,需要将例子中的 bucket 名称修改为您自己创建的真实的bucket名称。

image.png

输入:

参数 说明
input_oss_folder oss://blender-demo/input/ 场景文件所在OSS目录
scene_file_path splash279/splash279.blend 场景文件名
frames 1-4 渲染1到4帧
format PNG 渲染输出格式,默认即可
output_name_format ####.png 渲染输出文件名,默认即可
  • scripts_oss_folder 设置了默认值,且不允许覆盖,可以不用填。

输出:

参数 说明
input_oss_folder oss://blender-demo/output/ 输出OSS目录, 渲染结果图片将保存到此目录的 result/ 子目录下

Loggin(日志目录配置):

参数 说明
StdoutPath oss://blender-demo/log/ stdout日志输出到此
StderrPath oss://blender-demo/log/ stderr日志输出到此

填好后点击提交即可。

5. 查看作业状态和结果

(1) 查看作业状态

image.png

(2) 查看结果

oss://blender-demo/output/result/

image.png

6. 附录

分帧器代码(python):

render.py:

#!/usr/bin/python 
# -*- coding: UTF-8 -*- 
import os
import math 
import sys  
import re 

NOTHING_TO_DO = 'Nothing to do, exit'

def _calcRange(a,b, id, step):
  start = min(id * step + a, b)
  end  = min((id+1) * step + a-1, b)
  return (start, end)

def _parseContinuedFrames(render_frames, total_nodes, id=None, return_type='list'):
  '''
  解析连续帧, 如: 1-10
  '''
  [a,b]=render_frames.split('-')
  a=int(a)
  b=int(b)
  #print(a,b)
  step = int(math.ceil((b-a+1)*1.0/total_nodes))
  
  #print('step:', step) 
  mod =  (b-a+1) % total_nodes
  #print('mod:', mod)

  if mod==0 or id < mod:
    (start, end) = _calcRange(a,b, id, step) 
    #print('--->',start, end)
    return (start, end) if return_type!='list' else range(start, end+1)
  else:
    a1 =  step * mod + a
    #print('less', a1, b, id)
    (start, end) = _calcRange(a1 ,b, id-mod, step-1)
    
    #print('--->',start, end)
    return (start, end)  if return_type!='list' else range(start, end+1)
  
def _parseIntermittentFrames(render_frames, total_nodes, id=None):
  '''
  解析不连续帧, 如: 1,3,8-10,21
  '''
  a1=render_frames.split(',')
  a2=[]
  for n in a1:
    a=n.split('-')
    a2.append(range(int(a[0]),int(a[1])+1) if len(a)==2 else [int(a[0])])

  a3=[]
  for n in a2: 
    a3=a3+n
  #print('a3',a3)
  
  step = int(math.ceil(len(a3)*1.0/total_nodes))
  #print('step',step)
  
  mod =  len(a3) % total_nodes
  #print('mod:', mod)

  if mod==0 or id < mod:
    (start, end) = _calcRange(0, len(a3)-1, id, step) 
    #print(start, end)
    a4= a3[start: end+1] 
    #print('--->', a4)
    return a4
  else:
    #print('less',  step * mod  , len(a3)-1, id)
    (start, end) = _calcRange( step * mod   ,len(a3)-1, id-mod, step-1)
    if start > len(a3)-1:
      print(NOTHING_TO_DO)
      sys.exit(0)
    #print(start, end)
    a4= a3[start: end+1] 
    #print('--->', a4)
    return a4
   

def parseFrames(render_frames, return_type='list', id=None, total_nodes=None):
  '''
  @param render_frames {string}:  需要渲染的总帧数列表范围,可以用"-"表示范围,不连续的帧可以使用","隔开, 如: 1,3,5-10 
  @param return_type {string}:  取值范围[list,range]。 list样例: [1,2,3], range样例: (1,3)。 
         注意: render_frames包含","时有效,强制为list。
  @param id, 节点ID,从0开始。 正式环境不要填写,将从环境变量 BATCH_COMPUTE_DAG_INSTANCE_ID 中取得。
  @param total_nodes, 总共的节点个数。正式环境不要填写,将从环境变量 BATCH_COMPUTE_DAG_INSTANCE_COUNT 中取得。
  '''
   
  if id==None:
    id=os.environ['BATCH_COMPUTE_DAG_INSTANCE_ID']
  if type(id)==str:
    id = int(id)

  if total_nodes==None:
    total_nodes = os.environ['BATCH_COMPUTE_DAG_INSTANCE_COUNT']
  if type(total_nodes)==str:
    total_nodes = int(total_nodes)

  if re.match(r'^(\d+)\-(\d+)$',render_frames):
    # 1-2
    # continued frames
    return _parseContinuedFrames(render_frames, total_nodes, id, return_type)

  else:
    # intermittent frames
    return _parseIntermittentFrames(render_frames, total_nodes, id)
目录
相关文章
|
7月前
|
JavaScript API
Vue使用vue-3d-model组件预览3D三维文件、立体文件,支持旋转、自动播放
Vue使用vue-3d-model组件预览3D三维文件、立体文件,支持旋转、自动播放
|
编解码
Blender视图渲染知识
Blender视图渲染知识
Blender视图渲染知识
|
2月前
Threejs中导入GLTF模型克隆后合并
这篇文章详细说明了在Three.js中如何导入GLTF模型,对其进行克隆,并将多个克隆模型合并成一个整体模型的过程。
91 2
Threejs中导入GLTF模型克隆后合并
|
4月前
|
Web App开发 JSON JavaScript
WebGL简易教程(十五):加载gltf模型
WebGL简易教程(十五):加载gltf模型
150 1
|
JSON 算法 数据可视化
⚡Three.js-在场景中导入3D动画模型
⚡Three.js-在场景中导入3D动画模型
47187 4
⚡Three.js-在场景中导入3D动画模型
|
存储 定位技术 图形学
Blender插件:GLTF 2.0 导入/导出工具
Blender插件:GLTF 2.0 导入/导出工具
339 0
【Redshift渲染器渲染出图片有色差(红移渲染器)】
【Redshift渲染器渲染出图片有色差(红移渲染器)】
210 0
【Redshift渲染器渲染出图片有色差(红移渲染器)】
|
JSON 数据格式
Cesium开发:模型的CZML文件
Cesium开发:模型的CZML文件
465 0
|
数据可视化 开发者
实用技巧 | Pyecharts可视化渲染为图片保存
使用 pyecharts 渲染成图片一直是开发者比较关心的功能,pyecharts提供了 selenium、phantomjs 和 pyppeteer 三种方式
1633 0
|
存储 缓存 异构计算
Metal 案例06:视频文件渲染
本案例的目的在于理解本地视频文件渲染的过程
739 0
Metal 案例06:视频文件渲染