みなさんこんにちは。最近、今更ながら3Dプリンタ欲しい欲しい病にかかっています。プログラムとかCGとか、実体のないものばかりを扱っていると無性に手で触れるものでガッシャンガッシャンやりたくなるのですよね。本当は金属加工とか鋳造とかやりたいのですが、さすがにそれはスペースや安全性の問題で実現できなさそうです。残念無念......
TEXT_痴山紘史 / Hiroshi Chiyama(日本CGサービス)
EDIT_尾形美幸 / Miyuki Ogata(CGWORLD)
アセットの管理方法について考える
今回は、クラウド環境上でのアセット管理について考えていきます。
クラウド環境上でデータを処理するということは、ローカル環境とクラウド環境の2ヶ所にデータを置くことになります。このとき、データがきちんと管理できていないと、ローカル環境にあるファイルが更新されているのにクラウド環境が更新されていない、もしくはその逆といったことが起きてしまい、処理を実行する環境(最悪の場合、各インスタンス)によって結果が異なるという問題が起きてしまいます。こうなると問題解決にばかり時間を取られてしまい、何のためにクラウド環境を使用しているのかわからなくなってしまいます。
この問題を解決する最も単純な方法は、処理を行う度に全てのデータをクラウド環境に転送することです。必要な全てのデータを都度転送することで、環境ごとにデータが異なってしまうという問題が起きなくなります。この方法の問題点は毎回同じだけの転送時間がかかってしまうということです。
都度全部転送はめんどくさいなぁ......ということで、次に候補に挙がるのが前回(第19回)も言及した、社内と社外で常に同期をとっておく方法です。似たようなケースで、複数の会社を跨いで仕事を行うときのデータ共有手段としても検討されることが多いです。ユーザーは一切何も考えなくてよく、同期システムがいい感じに対応してくれて万歳......となればいいのですが、転送時間によるタイムラグや複数の場所で同じファイルが更新されてしまった場合の対応を考える必要があるため、なかなか一筋縄ではいかないです。また、まちがってリリースしたデータも共有されてしまうため、その転送時間が大きなロスになってしまいます。アセットを共有フォルダに置いた後に見直したら、テクスチャがまちがっていて、何度かファイルのコピーを繰り返したり、誰にもわからないだろうと思ってコッソリと共有データを上書きしたりした経験のある人も多いでしょう。
さてさてどうする~??となって救世主の如く現れるのがバージョン管理システムです。映像・ゲーム関連では未だに Subversion(SVN)を使用することが多いようです。SVNはテキストベースのデータの扱いをメインにしており、バイナリデータを扱うことには向いていない上に、ソフトウェアとしても古いので、何で未だに使っているのかちょっと私にはわからないです。お金のあるところはHelix(旧名・Perforce)を使用しているようです。バージョン管理システムを使用すると、バイナリデータの扱いが大きな負荷になってしまうことが多く、インフラにそれなりの投資が必要となります。CEDEC2019でも、スクウェア・エニックスのセッションでPerforceを使用した事例が紹介されていて、最終的には二度ほどサーバの増強をすることで解決したというお話があったと記憶しています。
また、バージョン管理システムを使用しても解決できないのが、複数のバージョンのバイナリデータを同時に確認するのが難しいという問題です。テキストデータであればバージョン間のちがいを目で確認できますが、バイナリデータの場合はアプリケーションで開く必要があるため、ディスク上に実体として存在する必要があります。これは既存のバージョン管理システムの思想と相性が悪いため、なかなか悩ましいところです。また、バージョン管理システムに登録されたファイルを全て共有することも、データ量の多さによって各種問題が引き起こされる原因になりがちです。そもそも、バージョン管理システムの場合、普通は作業環境ごとにデータをコピーして手元に置いて作業することを想定しているので、チームで巨大なデータを共有して作業を進めるという使い方との相性がとても悪いです。
これらの問題をある程度解決しつつ、巨大なバイナリデータを扱うためのしくみとしてGit-LFSもあるので、これを上手く活用しているところもあるのではないかと思います。
ここまでくるとめんどくさくなって「もうクラウドとかメンドイし、いいかな......」っていう気になってきますね :-)。でも、これって実は普段仕事をしていても常につきまとってくる問題なのですよね。今回は、前回の内容をベースに、ある程度簡単に実現できる方法を考えます。
アセット管理ルールを決める
前回決めたルールを元に、アセット管理ルールを決めます。
前回、ローカルの作業環境とAWS上のインスタンスの両方に/gsディレクトリを用意し、ローカル環境の場合は/gsはローカルのファイルサーバを参照し、インスタンスではS3上のデータをs3fsでマウントするようにしました。また、ローカル環境ではS3上のデータをs3fsで/s3fsディレクトリにマウントしています。
さらに、アセットの管理ルールを詰めていきます。
プロジェクト共通で使用するファイルは/gs/assets/(アセット名)/revXXX以下に置きます。revXXXはrev001, rev002, ......と続く連番で、アセットが更新されると新しいリビジョンの中に完全なアセットのファイルが格納されます。この時、一度作成されたリビジョンの中身は絶対に変更しないようにします。ツールを使用して配置を行うのであれば、配置後に読み取り専用に変更することも簡単にできます。
クラウドとのファイルの同期はリビジョン単位で行います。ジョブ投入時に必要なファイルをリストアップし、もし/s3fs以下にあるファイルで、かつファイルの属するリビジョンディレクトリがなければファイルを転送するようにします。
ユーザーの作業領域は/gs/work以下とします。ここにあるファイルは、必要なものをリストアップした上で転送するようにします。
計算結果も/gs/work以下に格納するようにします。
ここまでの内容を図にまとめてみました。
これくらいなら混乱も少なくローカル環境とクラウド環境でデータを共有できそうです。
アセットの用意
アセットは以前から使用しているAnimator Starter Pack(For Short Films or Animations)Final 6.0.0 for Maya(※リンク切れ)を使用します。このアセットは無料で使用できるということで重宝していたのですが、Highend3D.comの閉鎖にともない入手できなくなってしまったようです。残念無念......
事前に変換した.usdファイルを/gs/assets以下に配置し、Mayaから出力したレイアウト用.usdファイルが参照します(これも/gs/assets以下に配置する)。Gafferはレイアウト用.usdファイルを参照してレンダリングを行います。
例えば、/gs/assets以下はこのようになります。
[neo@matrix assets]$ ls /gs/assets
ActingStage Building6_House ParkEntrance_PayStation
ActingStageV2 Building7_House PhoneBooth
AwningBase Building8_House PlantedTree
AwningBaseV2 Building9_House plasticCrate
BenchV1 Building_Apartment_1 Playground_Equiptment
BenchV2 Building_Apartment_2 PoliceCar
BenchV3 Building_SignV1 Raft
BenchV4 Bushel RetainingWall
BrickPattern1 CafeV1 RetentionWallv1
BrickPattern2 CafeV2 Shingles
BrickPattern3 Chinmey ShinglesV2
BrickPattern4 Cone SideWalk_Peice1
BrickPattern5 CrateV1 SmallPieces
Building10_House CrateV2 Steps1
Building10V2_House cube StopLight
Building10V3_House Door1 test
Building11_House DrinkStand1 TextForSigns
Building11v2_House FamilyCar TrainTracksV1
Building11v3_House FamilyCarv2 TrashCan
Building12_House FamilyCar_WithLuggage Tree1
Building13_House FamilyVan Tree2
Building14_House FamWagon Tree3
Building14v2_House Fiat1500 Tree4
Building15_House FireHydrant VentV1
Building15v2_House GrassSectional_1 WindowWoodv1
Building16_House Headstone1 WindowWoodv2
Building16v2_House Headstone2 WoddenFence
Building17_House Headstone3 WoodenBarrel
Building17v2_House IceChest WoodenBarrelV2
Building18_House IceCreamTruck WoodenWheel
Building1_House IPhoneRig WoodFloorV1
Building3_House IVY_basic WoodFloorV2
Building4_House Lightpost WoodFloorV3
Building5_House Lockers WoodSign1
Building5v2_House PappyTruck ZooPictureSign
[neo@matrix assets]$ ls /gs/assets/Fiat1500/rev001/
Fiat1500.abc Fiat1500.usd
[neo@matrix assets]$ ls /s3fs/
foobar Kitchen_set render work
当然のこととして、ローカル環境にアセットを用意しただけなので、S3上にはファイルが存在しません。
[[SplitPage]]必用なファイルのリストアップ
Gafferの.gfrファイルと.usdファイルを調べて、中で使用されている外部データをリストアップします。gafferは引数によって動作モードが変わり、cliではpythonコンソールを使用することができます。例えば、以下のようにして.gfrファイルを読んでノードをリストアップすることができます。
[neo@matrix HTCondor]$ /opt/gaffer-0.54.1.0-linux/bin/gaffer cli
>>> import Gaffer
>>> script = Gaffer.ScriptNode()
>>> script['fileName'].setValue('/home/neo/Documents/HTCondor/layout_Arnold.gfr')
>>> script.load()
>>> for node in script.children(Gaffer.Node):
>>> print(node)
また、cliの代わりにpythonを指定するとpythonスクリプトファイルを実行できるので、これを使用します。
とりあえずざっくりと、.gfrファイルと中で読んでいる.usdファイルを解析して関連するファイルをリストアップします。.usdファイルは本連載の第4回で生成したものを使用して、必要最低限の処理でアセットをリストアップします。
今回はこのようなコードにしました。
[neo@matrix neo]$ cat gfrAnalyzer.py
import sys
import re
import Gaffer
import GafferScene
def listupUSDAssets(path):
ret = []
fp = open(path)
for l in fp.readlines():
m = re.search('references = @([^@]+)@', l)
if m is None:
continue
ret.append(m.group(1))
return ret
def analyze(path):
script = Gaffer.ScriptNode()
script['fileName'].setValue(sceneFileName)
script.load()
ret = []
for node in script.children(Gaffer.Node):
if node.__class__ == GafferScene.SceneReader:
fileName = node['fileName'].getValue()
ret.append(fileName)
ret.extend(listupUSDAssets(fileName))
if node.__class__ == Gaffer.Reference:
ret.append(node.fileName())
return ret
if __name__ == '__main__':
sceneFileName = sys.argv[1]
files = analyze(sceneFileName)
files = list(set(files))
files.sort()
for f in files:
print(f)
[neo@matrix neo]$
これを実行します。
[neo@matrix neo]$ ~/gaffer.sh python gfrAnalyzer.py layout_Arnold.gfr
/gs/assets/BenchV1/rev001/BenchV1.usd
/gs/assets/BrickPattern1/rev001/BrickPattern1.usd
/gs/assets/Building10_House/rev001/Building10_House.usd
/gs/assets/FamilyCar/rev001/FamilyCar.usd
/gs/assets/Tree3/rev001/Tree3.usd
/gs/work/neo/gaffer/projects/default/references/Bench.grf
/gs/work/neo/gaffer/projects/default/references/BrickPattern.grf
/gs/work/neo/gaffer/projects/default/references/FamilyCar.grf
/gs/work/neo/gaffer/projects/default/references/Hotel.grf
/gs/work/neo/gaffer/projects/default/references/Tree.grf
/gs/work/neo/layout.usd
[neo@matrix neo]$
.gfrファイル中で読んでいる.usdファイルと、さらにその中で読んでいるアセット、それからlookdevに使用した.grfファイルがリストアップされています。ここまでくれば、後は前回の方法と組み合わせてクラウド環境でのレンダリングまでできるでしょう。ここから先はこれまでやってきたことの繰り返しになりますし、環境や要望によって詰めていく内容が異なってくるので言及しません。
皆さん各自工夫していただけたらと思います。
まとめ
これで、本連載のクラウド編は一区切りとなります。おつかれさまでした。次回以降、2回ほどクラウドを絡めた番外編を挟み、5月から新章に突入できればいいかなと考えています。今後も引き続きよろしくお願いします。
第21回の公開は、2020年3月を予定しております。
プロフィール