株式会社デジタル・フロンティア-Digital Frontier

Header

Main

  • TOP
  • DF TALK
  • レンダーパッケージのお話

レンダーパッケージのお話

2012/11/26

Tag: ,,,,,,

先月(10/27)にとうとう「バイオハザードダムネーション」が公開になりましたが
みなさんご覧いただけましたでしょうか?
制作中にゾンビになりかけた たつみ(@mtazmi)です。

今回は「レンダーパッケージ」について記事にしたいと思います。

最近開催されているメイキングセミナーやCGWorld11月号(vol.171)の記事などで、
名前だけでも聞いたことがあるかもしれません。

twitterなどでもちらほら反応があったようなので
もしかして需要あるかなと思いましてネタに選んでみました。


 
それではさっそく始めましょう!
 

なぜこのような仕組みが必要なの?

 ・弊社では、立体視の映画をまず左目用のLチャンネルで2D版を作ります
          ↓
 ・2D版がOKになったら今度は右目用のRチャンネルをレンダリングして立体視版にします
          ↓
 ・RのレンダリングはLよりだいぶ後になるので、カットによっては2~3ヶ月後というのもあります
          ↓
 ・その期間にデータが更新されてしまうと、当然LとRで合わなくなってしまいます
 dfTalk_tatsumim_v02_01
 
鉄拳ブラッドベンジェンスでは、こうしたRとLが合わずに結局両方同時にやり直し、
ということが多発してとても大変だったと聞きます
(僕はダムネーションから参加なので直接は知らないのですが、その辺の黒歴史は各所から耳にしていました)

          ↓↓↓

 そこでLをレンダリングする時点でのデータ環境をまるっとタイムカプセルのように
 閉じ込めて残しておき、Rをレンダリングするときに再利用するのがパッケージシステムです!

 
 イメージ図
dfTalk_tatsumim_v02_02
 ・各サーバーはどれも同じ階層構造に統一してあります
 ・そして、パッケージの中身の階層もファイルサーバーとまったく同じ構成になっています
 (小さなサーバー、と言うとイメージしやすいかもしれません)
 
 ・レンダーシーンが参照するリファレンスシーンやテクスチャなどは、
  パッケージのルートフォルダ以下からアクセス出来るように変更します
 ・そのため、パッケージ自体は専用サーバーやローカルPCなど、どこに作ってもかまいません
 
 ・海外のレンダーファームを使う場合は、パッケージのルートフォルダから圧縮すれば1ファイル送るだけで済みます
 
ではお次は、パッケージ処理の大まかな流れについて解説します
 


 

パッケージ処理の大まかな流れ

※解説中のサンプルコードはpymelありきとなっております(maya2011以前のバージョンをお使いの方すいません)
 また、ファイルパスの階層は、事実を元にしたフィクションみたいな感じになっています

 

maya内の処理

1、レンダーシーンを開き、ロードされているプラグインを調べて回収

 以下のようにロードされているプラグインを取得してコピーしてきます
 
 取得の仕方はこんな感じです

import pymel.core as pm
pluginList = []
for plugin in pm.pluginInfo(query=True, listPlugins=True):
    pluginPath = pm.pluginInfo(plugin, query=True, path=True)
    pluginList.append(pluginPath)
print pluginList

 
 メンタルレイはこちら

import pymel.core as pm
mrPluginList = []
for plugin in pm.mrFactory(query=True, allLoaded=True):
    mrPluginList.append(plugin)
print mrPluginList

 

2、レンダー設定で絶対これに統一、という項目は間違いを防ぐためにも改めて設定しておく

 例えば出力する画像形式をOpenEXRに、フレーム番号は4桁にしておくという場合
 

import pymel.core as pm
DRG = pm.PyNode('defaultRenderGlobals')
# padding >> 4
DRG.extensionPadding.set(4)
# imageFormat >> openExr
DRG.imageFormat.set(51)
DRG.imfPluginKey.set('exr')

MRG = pm.PyNode('mentalrayGlobals')
#openexr compression zip
MRG.imageCompression.set(4)

 

3、シーン修正結果をMAで保存し直す

 unknownノードなどゴミ掃除をしてから、次のテキスト処理をかけるためにMA形式で保存します
 


 

maya外のパッケージスクリプトで行うこと

1、プロジェクト用に用意されているスクリプト、プラグインを回収

 固めている場所は決められた場所なので、フォルダごとまるっとパッケージフォルダ内へコピーします
 

2、MAファイルの書き換え

 maya内編3で保存したMAファイルをテキストとして読み込み、
 中の記述からファイルパスと思われる部分をパスの一部を環境変数($DF_RENDER_PACKAGE_PATH)に置き換えて再度保存
 
 基本的にファイルパスならなんでも拾うようにしたおかげで、あとから○○ファイルを追加対応という手間が省けました

 こうすることでパッケージフォルダからレンダリング出来るように準備が出来ます
 

# -*- coding: utf-8 -*-
#===========================================================
#   MAファイルを読み込んでいろいろする
#===========================================================

def readMayaFile(mayaFilePath):
    f = open(mayaFilePath, 'r')
    lines = f.readlines()
    f.close()

    result = []     # 処理結果を入れておくもの
    # 文字列からファイルパスっぽい対象を検索し、見つけたら置き換え処理にまわす
    for line in lines:
        # コメントアウトは無視
        if line.startswith('//'):
            result.append(line)
            continue

        # ドットを含まなければ拡張子無しとしてスルー
        if not '.' in line:
            result.append(line)
            continue

        # ドライブっぽい表記があればファイルパスっぽい気がするー
        if ':/' in line or ':\\' in line or '//' in line or '\\\\' in line:
            replacedLine = パスの置き換え処理へ(line)
            result.append(replacedLine)
            ファイルコピー処理(line)

        # 特に何も無かった(´・ω・`)
        result.append(line)

    return result

 
  ↑の「パスの置き換え処理へ」と書かれた部分
  正規表現を使って、例えばMAファイル内の記述を、
   setAttr “.ftn” -type “string” “Z:/DA/asset/chara/LEON/sourceimages/LEON_color01.tex”;
          ↓
   setAttr “.ftn” -type “string” “$DF_RENDER_PACKAGE_PATH/asset/chara/LEON/sourceimages/LEON_color01.tex”;
   というように書き換えを行います(DAはダムネーションの頭文字です)
 

# -*- coding: utf-8 -*-
#===========================================================
#   渡された文字列からパス書き換え対象部分を処理して返す
#===========================================================

import re

def replacePath(getLine):
    env = '$DF_RENDER_PACKAGE_PATH'

    # 置き換え用正規表現キーワード
    regex = re.compile('(?P<head>.*").*[\\\\/][dD][aA](?P<tail>[\\\\/].*)')
    result = None
    m = regex.match(getLine)

    # 正規表現が正しく取れない場合スキップ
    if not m:
        print None
        return getLine

    # 正規表現が成立したら置き換え文字列を作って返す
    result = m.group('head') + env + m.group('tail') + '\n'
    return result

 
mayaではファイルパスが /区切りの場合と、\区切りの場合があるので苦労しました。。
 

4、置き換え処理をしたファイルもパッケージ階層に回収する

 3の置き換えと同じような感じでファイルパスを抜き出してきてファイルをコピーしてきます
 

5、パッケージ内からレンダリングジョブを送るためのbatを作る

 レンダリング管理ソフトによって違うと思いますが、
 どのソフトでもレンダリングするカメラを何にするかフラグがありますので、
 カメラは変数として出しておけば簡単にLのbatがRをレンダリングするbatに切り替えられます

SET CAMERA=L
    ↓
SET CAMERA=R

 

6、環境変数をセットしてレンダリング

 パッケージ内でレンダリングするための必要な環境変数は、
 レンダリング管理ソフトでレンダリングが始まる前に動くbatで設定されます
 
 パッケージルートは必ずレンダーシーンの4つ上の階層ということを利用し、
 そこからさかのぼって行った先のディレクトリを取得して設定しています

cd "レンダーシーンがあるフォルダ"
cd..
cd..
cd..
cd..
SET DF_RENDER_PACKAGE_PATH=%cd%

 
  その他、スクリプトやプラグイン、シェーダなどのパスも設定します

SET MAYA_PLUG_IN_PATH=%DF_RENDER_PACKAGE_PATH%\tool\plug-ins;%MAYA_PLUG_IN_PATH%; 
SET MAYA_SCRIPT_PATH=%DF_RENDER_PACKAGE_PATH%\tool\scripts;%MAYA_SCRIPT_PATH%;
SET PYTHONPATH=%DF_RENDER_PACKAGE_PATH%\Assets\tool\python;%PYTHONPATH%;
SET MI_LIBRARY_PATH=%DF_RENDER_PACKAGE_PATH%Assets\tool\mentalRay\lib;%MI_LIBRARY_PATH%;
SET MI_CUSTOM_SHADER_PATH=%DF_RENDER_PACKAGE_PATH%\tool\mentalRay\include;%MI_CUSTOM_SHADER_PATH%;

という感じです。
 


 

~実際に起きたトラブルと対処法~

その1!

 書き換え処理をしたMAファイルをテキストエディタで開いて検索しても全て置き換わっているのに
 回収されていないテクスチャがあるし、実際シーンを開いてみたら置き換え処理されていないファイルノードが…なぜ(´・ω・`)
    ↓
 どうやらレンダーシーンでリファレンスしてるデータに特に何も変更が無い場合、
 maファイルに保存されない場合があるようなのです!

 というわけで、同じ値をセットし直して、変更したという状況を作りました
 こんな感じで

import pymel.core as pm
for fileNode in pm.ls(type='file'):
    filePath = fileNode.fileTextureName.get()
    if filePath:
        fileNode.fileTextureName.set(filePath, type='string')

for fileNode in pm.ls(type='mentalrayTexture'):
    filePath = file.fileTextureName.get()
    if filePath:
        fileNode.fileTextureName.set(filePath, type='string')

 
 はい解決
 

その2!

 わぁ、ログが真っ赤だぁ!(某目薬のCMのように)

 メンタルレイは環境変数を含むパスだとエラーになってしまう!
    ↓
 これについてはシーンを開いたときに実行されるイベント処理で、
 環境変数を実際のパスに書き戻す処理を追加しました

 これはプラグイン実装でしたが、PreRenderMELに仕込んでもいいです

 パッケージ化中に以下のコマンドでPreRenderMELを仕込んでおく

import pymel.core as pm
DRG = pm.PyNode('defaultRenderGlobals')
DRG.preMel.set('DA_preRender();')

 
 実行するPreRenderMEL
 preRender.mel

global proc DA_preRender(){
    python("import forcePackagePath")
    python("forcePackagePath.setMrTexPath()")
}

 
 PreRenderMELが呼ぶ処理
 forcePackagePath.py

# -*- coding: utf-8 -*-
import pymel.core as pm
def setMrTexPath():
    packagePath = os.environ['DF_RENDER_PACKAGE_PATH']
    for tex in pm.ls(typ='mentalrayTexture'):
        # ファイルパスが空っぽならスキップ
        getPath = tex.fileTextureName.get()
        if not getPath:
            continue

        # 環境変数始まりなら置き換え処理
        if getPath.startswith('$'):
            newPath = getPath.replace('$DF_RENDER_PACKAGE_PATH', packagePath)
            tex.fileTextureName.set(newPath, typ='string')

 いっちょあがりっ!
 
 

そのた

 ・初めの頃に正規表現にパスの区切り文字(/, )を入れ忘れていたせいで、
  エイダ・ウォン(ADA)、携帯端末(PDA)といったIDに、プロジェクト名のDAが引っかかり軽い惨事になってしまった
 ・スペースが入ったフォルダ名で回収漏れ
 ・土日分レンダリングする用に金曜日にせっせとパッケージを作って帰ったら、
  ツールバグのせいで数百ジョブがレンダリング出来ない悲惨な状況に。。
 ・海外のレンダーファームが謎の不調に
 ・パッケージデータが大量になりサーバーを圧迫

 ・休日かかってくる電話に恐怖
 ・初めて話をする人がツールバグの謝罪
 ・2kg痩せた
 


 
 いかがだったでしょうか?
 かなり規模が大きいツールなので全部紹介しきれていませんが、何かのヒントになれば幸いです。
 
 パッケージツールはレンダリングと並行してツールを作っていたため、
 いろんなトラブルに見舞われましたが、こうして公開にこぎつけることが出来て本当によかったです。
 エンドクレジットに載った名前を見たときは感動でした。
 

Pocket

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

*