本篇主要是介绍如何将渲染软件 Blender 创建成 BatchCompute 的 App,并通过此 App 提交 Blender 渲染作业。
Blender 是目前最流行的一款开源的跨平台全能三维动画制作软件,提供从建模、动画、材质、渲染、到音频处理、视频剪辑等一系列动画短片制作解决方案。 具体介绍可以看这里:https://www.blender.org/features/。
如果已经开通,请忽略此步骤。
本篇例子所有阿里云服务都需要使用相同的地域。
本篇例子使用地域: 华南1(深圳)
本篇例子假设创建的 bucket 名称为:blender-demo
, 地域在华南1(深圳)。
注意: 使用批量计算时,地域需要和 OSS bucket 的地域相同。
注意: 实际操作时,需要将例子中的bucket 名称修改为您自己创建的真实的bucket名称。
文件名: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"]
docker build -t ubuntu-blender ./
等待完成,然后使用下面的命令查看是否有 ubuntu-blender
docker images
docker run -t ubuntu-blender blender -v
显示:
Blender 2.79 (sub 0)
记住此版本信息,下面要用到。
您需要将 ubuntu-blender 上传到 BatchCompute 支持Registry。
BatchCompute支持2种Registry:阿里云的 CR(Container Registry)和阿里云的OSS。
选择一种即可,推荐第一种: CR。
如何上传,请参考这2篇文档:
假设已经上传到CR(地域:华南1-深圳),名称为: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0
BatchCompute 提交作业,需要配置很多参数。BatchCompute 提供的 App 模板机制,让用户很方便预设参数默认值,提交作业时,只需填写少量参数即可。
下面我们来创建一个 Blender 渲染 App。
打开批量计算控制台: https://batchcompute.console.aliyun.com
Docker 镜像名称,填写您已经上传到CR的镜像名称,如: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0
只需修改 实例类型为 8核16GB规格,其他的默认即可。
python -c "import os;import sys;sys.path.append('/home/scripts/'); from framer 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/ ,应该包含要渲染的 framer.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/ 。 |
环境变量可以不用配置, 直接提交即可。
在App列表中可以看到已经创建好的 ubuntu-blender, 点击”提交作业”。
在提交作业前,还有一些准备工作。
分帧器python代码(见附录),上传到您的OSS目录下,比如: oss://blender-demo/scripts/framer.py
Blender 官网提供了好多 demo 文件: https://www.blender.org/download/demo-files/
本例子需要下载 2.79 版本(注意:要和镜像中安装的Blender版本相同。不同版本的可能渲染不出来)
素材下载后,解压得到目录: splash279/
将整个目录上传到 oss://blender-demo/input/
下面,即:oss://blender-demo/input/splash279/
。
注意: 实际操作时,需要将例子中的 bucket 名称修改为您自己创建的真实的bucket名称。
参数 | 值 | 说明 |
---|---|---|
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 | 渲染输出文件名,默认即可 |
参数 | 值 | 说明 |
---|---|---|
input_oss_folder | oss://blender-demo/output/ |
输出OSS目录, 渲染结果图片将保存到此目录的 result/ 子目录下 |
参数 | 值 | 说明 |
---|---|---|
StdoutPath | oss://blender-demo/log/ |
stdout日志输出到此 |
StderrPath | oss://blender-demo/log/ |
stderr日志输出到此 |
填好后点击提交即可。
oss://blender-demo/output/result/
实例规格 | 节点数 | 渲染帧数 | 时长 |
---|---|---|---|
ecs.sn1ne.2xlarge(8核16GB) | 2 | 1-4 | 9-12分钟 |
ecs.sn1ne.4xlarge (16核/32GB) | 2 | 1-4 | 4-6分钟 |
framer.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)
们需要采用一些方法策略实现个人隐私与功能之间的平衡,并保护其云中的应用程序...
现代科技公司的同事们平日一起交流开发规约和产品需求 肩上共同扛着业务发展和同...
01.? 什么是「全景录制」? 不同业务场景的录制需求往往较为复杂,涉及多人上下...
本文转载自微信公众号「程序新视界」,作者丑胖侠二师兄 。转载本文请联系程序新...
最近在阿里云ECS上安装软件,出现了glibc版本的问题,初步一看是由于glibc版本太...
1. 接口描述 接口请求域名: as.tencentcloudapi.com 。 本接口 (CreatePaiInsta...
引起租用或者托管服务器故障的原因总汇 服务器托管 、 服务器租用 是近年来用户...
在后疫情时代,云计算的能量得到加速释放。有数据显示,2020年,我国云计算市场...
阿里云支持通过运维编排服务OOS、快照、ECS实例以及Packer构建自定义镜像,还支...
系统事件是影响实例运行状态的有计划的底层运维事件,或非预期维修事件。特指因...