日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

3ds Max DAG作業(yè)最佳實踐

1. 準(zhǔn)備工作

1.1. 選擇區(qū)域

所有阿里云服務(wù)都需要使用相同的地域。

1.2. 開通服務(wù)

1.3. 制作鏡像

制作鏡像具體步驟請參考集群鏡像, 請嚴(yán)格按文檔的步驟創(chuàng)建鏡像。鏡像制作完成后,通過以下方式可以獲取到對應(yīng)的鏡像信息。image

1.4. 上傳素材

可以下載 3ds Max 官方提供的免費(fèi)素材包進(jìn)行測試。

通過 OSSBrowser工具將渲染素材到指定的 OSS bucket 中,如下圖:

upload

1.5. 安裝批量計算 SDK

在需要提交作業(yè)的機(jī)器上,安裝批量計算 SDK 庫;已經(jīng)安裝請忽略。Linux 安裝執(zhí)行如下命令;Windows 平臺請參考文檔

pip install batchcompute

2. 編寫work腳本

work.py

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import math
import sys
import re
import argparse
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'):
  '''
  解析連續(xù)幀, 如: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):
  '''
  解析不連續(xù)幀, 如: 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}:  需要渲染的總幀數(shù)列表范圍,可以用"-"表示范圍,不連續(xù)的幀可以使用","隔開, 如: 1,3,5-10 
    @param return_type {string}:  取值范圍[list,range]。 list樣例: [1,2,3], range樣例: (1,3)。 
            注意: render_frames包含","時有效,強(qiáng)制為list。
    @param id, 節(jié)點ID,從0開始。 正式環(huán)境不要填寫,將從環(huán)境變量 BATCH_COMPUTE_DAG_INSTANCE_ID 中取得。
    @param total_nodes, 總共的節(jié)點個數(shù)。正式環(huán)境不要填寫,將從環(huán)境變量 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)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
          formatter_class = argparse.ArgumentDefaultsHelpFormatter,
          description = 'python scripyt for 3dmax dag job',
          usage='render3Dmax.py <positional argument> [<args>]',
      )

    parser.add_argument('-s', '--scene_file', action='store', type=str, required=True, help = 'the name of the file with .max subffix .')
    parser.add_argument('-i', '--input', action='store', type=str, required=True, help = 'the oss dir of the scene_file, eg: xxx.max.')
    parser.add_argument('-o', '--output', action='store', type=str, required=True, help = 'the oss of dir the result file to upload .')
    parser.add_argument('-f', '--frames', action='store', type=str, required=True, help = 'the frames to be renderd, eg: "1-10".')
    parser.add_argument('-t', '--retType', action='store', type=str, default="test.jpg", help = 'the tye of the render result,eg. xxx.jpg/xxx.png.')
    args = parser.parse_args()

    frames=parseFrames(args.frames)
    framestr='-'.join(map(lambda x:str(x), frames))

    s = "cd \"C:\\Program Files\\Autodesk\\3ds Max 2018\\\" && "
    s +='3dsmaxcmd.exe -o="%s%s" -frames=%s "%s\\%s"' % (args.output, args.retType, framestr, args.input, args.scene_file)
    print("exec: %s" % s)

    rc = os.system(s)
    sys.exit(rc>>8)

注意:

  • work.py 只需要被上傳到 OSS bucket中不需要手動執(zhí)行;各項參數(shù)通過作業(yè)提交腳本進(jìn)行傳遞;

  • work.py 的112 行需要根據(jù)鏡像制作過程中 3ds MAX 的位置做對應(yīng)替換;

  • work.py 的 scene_file 參數(shù)表示場景文件;如 Lighting-CB_Arnold_SSurface.max;

  • work.py 的 input 參數(shù)表示素材映射到 VM 中的位置,如:D;

  • work.py 的 output 參數(shù)表示渲染結(jié)果輸出的本地路徑;如 C:\tmp\;

  • work.py 的 frames 參數(shù)表示渲染的幀數(shù),如:1;

  • work.py 的 retType 參數(shù)表示素材映射到 VM 中的位置,如:test.jpg;渲染結(jié)束后如果是多幀,則每幀的名稱為test000.jpg,test001.jpg等。

work

3. 編寫作業(yè)提交腳本

test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from batchcompute import Client, ClientError
from batchcompute.resources import (
    ClusterDescription, GroupDescription, Configs, Networks, VPC,
    JobDescription, TaskDescription, DAG,Mounts,
    AutoCluster,Disks,Notification,
)
import time
import argparse

from batchcompute import CN_SHANGHAI as REGION #需要根據(jù) region 做適配
access_key_id = "xxxx" # your access key id
access_key_secret = "xxxx" # your access key secret

instance_type = "ecs.g5.4xlarge" # instance type  #需要根據(jù) 業(yè)務(wù)需要 做適配

image_id = "m-xxx"

workossPath = "oss://xxxxx/work/work.py"

client = Client(REGION, access_key_id, access_key_secret)

def getAutoClusterDesc(InstanceCount):
    auto_desc = AutoCluster()

    auto_desc.ECSImageId = image_id

    #任務(wù)失敗保留環(huán)境,程序調(diào)試階段設(shè)置。環(huán)境保留費(fèi)用會繼續(xù)產(chǎn)生請注意及時手動清除環(huán)境任務(wù)失敗保留環(huán)境,
    # 程序調(diào)試階段設(shè)置。環(huán)境保留費(fèi)用會繼續(xù)產(chǎn)生請注意及時手動清除環(huán)境
    auto_desc.ReserveOnFail = False

    # 實例規(guī)格
    auto_desc.InstanceType = instance_type

    #case3 按量
    auto_desc.ResourceType = "OnDemand"

    #Configs
    configs = Configs()
    #Configs.Networks
    networks  = Networks()
    vpc = VPC()

    # CidrBlock和VpcId 都傳入,必須保證VpcId的CidrBlock 和傳入的CidrBlock保持一致
    vpc.CidrBlock = '172.26.0.0/16'
    # vpc.VpcId = "vpc-8vbfxdyhx9p2flummuwmq"

    networks.VPC = vpc
    configs.Networks = networks

    # 設(shè)置系統(tǒng)盤type(cloud_efficiency/cloud_ssd)以及size(單位GB)
    configs.add_system_disk(size=40, type_='cloud_efficiency')

    #設(shè)置數(shù)據(jù)盤type(必須和系統(tǒng)盤type保持一致) size(單位GB) 掛載點
    # case1 linux環(huán)境
    # configs.add_data_disk(size=40, type_='cloud_efficiency', mount_point='/path/to/mount/')

    # 設(shè)置節(jié)點個數(shù)
    configs.InstanceCount = InstanceCount
    auto_desc.Configs = configs
    return auto_desc

def getTaskDesc(inputOssPath, outputossPath, scene_file, frames, retType, clusterId, InstanceCount):
    taskDesc = TaskDescription()

    timestamp = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
    inputLocalPath = "D:"
    outputLocalPath = "C:\\\\tmp\\\\" + timestamp + "\\\\"
    outputossBase = outputossPath + timestamp + "/"
    stdoutOssPath = outputossBase + "stdout/" #your stdout oss path
    stderrOssPath = outputossBase + "stderr/" #your stderr oss path
    outputossret = outputossBase + "ret/"

    taskDesc.InputMapping = {inputOssPath: inputLocalPath}
    taskDesc.OutputMapping = {outputLocalPath: outputossret}

    taskDesc.Parameters.InputMappingConfig.Lock = True

    # 設(shè)置程序的標(biāo)準(zhǔn)輸出地址,程序中的print打印會實時上傳到指定的oss地址
    taskDesc.Parameters.StdoutRedirectPath = stdoutOssPath

    # 設(shè)置程序的標(biāo)準(zhǔn)錯誤輸出地址,程序拋出的異常錯誤會實時上傳到指定的oss地址
    taskDesc.Parameters.StderrRedirectPath = stderrOssPath

    #觸發(fā)程序運(yùn)行的命令行
    # PackagePath存放commandLine中的可執(zhí)行文件或者二進(jìn)制包
    taskDesc.Parameters.Command.PackagePath = workossPath
    taskDesc.Parameters.Command.CommandLine = "python work.py -i %s -o %s -s %s -f %s -t %s" % (inputLocalPath, outputLocalPath, scene_file, frames, retType)

    # 設(shè)置任務(wù)的超時時間
    taskDesc.Timeout = 86400

    # 設(shè)置任務(wù)所需實例個數(shù)
    taskDesc.InstanceCount = InstanceCount

    # 設(shè)置任務(wù)失敗后重試次數(shù)
    taskDesc.MaxRetryCount = 3

    if clusterId:
        # 采用固定集群提交作業(yè)
        taskDesc.ClusterId = clusterId
    else:
        #采用auto集群提交作業(yè)
        taskDesc.AutoCluster = getAutoClusterDesc(InstanceCount)

    return taskDesc


def getDagJobDesc(inputOssPath, outputossPath, scene_file, frames, retType, clusterId = None, instanceNum = 1):
    job_desc = JobDescription()
    dag_desc = DAG()

    job_desc.Name = "testBatch"
    job_desc.Description = "test 3dMAX job"
    job_desc.Priority = 1

    # 任務(wù)失敗
    job_desc.JobFailOnInstanceFail = False

    # 作業(yè)運(yùn)行成功后戶自動會被立即釋放掉
    job_desc.AutoRelease = False
    job_desc.Type = "DAG"

    render = getTaskDesc(inputOssPath, outputossPath, scene_file, frames, retType, clusterId, instanceNum)

    # 添加任務(wù)
    dag_desc.add_task('render', render)

    job_desc.DAG = dag_desc
    return job_desc

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        formatter_class = argparse.ArgumentDefaultsHelpFormatter,
        description = 'python scripyt for 3dmax dag job',
        usage='render3Dmax.py <positional argument> [<args>]',
    )

    parser.add_argument('-n','--instanceNum', action='store',type = int, default = 1,help = 'the parell instance num .')
    parser.add_argument('-s', '--scene_file', action='store', type=str, required=True, help = 'the name of the file with .max subffix .')
    parser.add_argument('-i', '--inputoss', action='store', type=str, required=True, help = 'the oss dir of the scene_file, eg: xxx.max.')
    parser.add_argument('-o', '--outputoss', action='store', type=str, required=True, help = 'the oss of dir the result file to upload .')
    parser.add_argument('-f', '--frames', action='store', type=str, required=True, help = 'the frames to be renderd, eg: "1-10".')
    parser.add_argument('-t', '--retType', action='store', type=str, default = "test.jpg", help = 'the tye of the render result,eg. xxx.jpg/xxx.png.')
    parser.add_argument('-c', '--clusterId', action='store', type=str, default=None, help = 'the clusterId to be render .')

    args = parser.parse_args()

    try:
        job_desc = getDagJobDesc(args.inputoss, args.outputoss, args.scene_file, args.frames,args.retType, args.clusterId, args.instanceNum)
        # print job_desc
        job_id = client.create_job(job_desc).Id
        print('job created: %s' % job_id)
    except ClientError,e:
        print (e.get_status_code(), e.get_code(), e.get_requestid(), e.get_msg())

注意:

  • 代碼中 12~20 行需要根據(jù)做適配,如 AK 信息需要填寫賬號對應(yīng)的AK信息;鏡像Id 就是1.3 中制作的鏡像 Id;workosspath 是步驟 2 work.py 在OSS上的位置;

  • 參數(shù) instanceNum 表示當(dāng)前渲染作業(yè)需要幾個節(jié)點參與,默認(rèn)是1個節(jié)點;若是設(shè)置為多個節(jié)點,work.py 會自動做均分;

  • 參數(shù) scene_file 表示需要渲染的場景文件,傳給 work.py;

  • 參數(shù) inputoss 表示素材上傳到 OSS 上的位置,也即1.4 中的 OSS 位置;

  • 參數(shù) outputoss 表示最終結(jié)果上傳到 Oss 上的位置;

  • 參數(shù) frames 表示需要渲染的場景文件的幀數(shù),傳給 work.py;3ds MAX 不支持隔幀渲染,只能是連續(xù)幀,如1-10;

  • 參數(shù) retType 表示需要渲染結(jié)果名稱,傳給 work.py,默認(rèn)是 test.jpg,則最終得到test000.jpg

  • 參數(shù) clusterId 表示采用固定集群做渲染時,固定集群的Id。

4. 提交作業(yè)

根據(jù)以上示例文檔,執(zhí)行以下命令:

python test.py -s Lighting-CB_Arnold_SSurface.max -i oss://bcs-test-sh/3dmaxdemo/Scenes/Lighting/ -o oss://bcs-test-sh/test/ -f 1-1 -t 123.jpg

示例運(yùn)行結(jié)果:

restulr

picture