2011-12-31

camera shake

標準で付いている CameraShakeノードは2D的なもので、3D上でのcameraノードにゆれを加えるようなものは標準では備わってない。
ってことで作ってみました。

http://dl.dropbox.com/u/40054205/cameraShake.nk

パラメーターとして・・・


という感じで、amplifier、waveLength、waveOffset、waveHeight、heightOffset、seedのパラメーターが各回転と各位置についてます。rotationXYZとpositionXYZとして。

ここで、下記ノイズ波形が初期値とすると






amplifier
減衰用の強度です。カメラシェイクのゆれを止まる際にノイズ波形を波の高さ弱めるの使用してください。1→0のアニメーションをつければノイズ波形が弱まりながら止まります。




または、テンションを付けたりするのに用いることができます。

waveLength
ノイズ波形の波長です。大きくなれば山から次の山までが離れて、ゆっくりとした動きになります。つまり大きいほどゆれ頻度が下がります。




waveOffset
ノイズ波形の横方向への(波長方向への、時間方向への)オフセットです。余り使う必要はないかも・・・
 
waveHeight
ノイズ波形の高さです。強度といったところです。大きいほどフレ幅が大きくなります。
 
 
heightOffset
ノイズ波形の縦方向への(高さ方向への、強度方向への)オフセットです。余り使う必要はないかも・・・
 
seed
ランダムシードです。たとえばrotationXとrotationYのパラメーターにすべて同じ数字をいれてるとノイズ波形としてはrotationXも同じになってしまうので、このseedでランダム化させれます。当然ながら、ノイズ波形の強度や波長の長さは一緒のままでランダム化出来ます。
 


一番上にある、amplifier for all と frequency for all は
 

amplifier for all
各rotationXYZ、各positionXYZのamplifierにかけることが出来る係数です。
全体的にとめたい場合などに有効です。

frequency for all
各rotationXYZ、各positionXYZのwaveLengthにかけることの出来る係数です。実際にはこの「frequency for all 」の逆数が全ての各waveLengthに乗算されます。小さくするとノイズ波形の波長を大きくすることができます、つまり動きの頻度をゆっくりにしたりする場合に使えます。

下記例では、上記二つのパラメーターを1→0というアニメーションをつけています。
 
 

2011-12-19

Expressionノードに関して

RGBマスク(って呼ぶのでしょうか?)を出したときに、そのマスクを組み合わせたい場合、Shuffleノードなどでほしいチャンネルだけ分離してきて、それをmergeする なんてことをすればできますが、Expressionノードを用いれば複数のノードを用いなくても出来る場合があります。


たとえばこの画像が以下の3つのマスクのチャンネルを持っているとする。

チャンネル名:MultiMatteElementA

チャンネル名:MultiMatteElementB

チャンネル名:MultiMatteElementC
 たとえば、これらのマスクを組み合わせて、縦(?)一列ずつをRGBでそれぞれ区切りたい、つまり


というようにしたいとする。
Mergeノードを使うならば、MultiMatteElementA,B,Cとすべてplusで合わせてやればいい。

位置がわかりにくいのでハーフで乗っけました。左からMultiMatteElementA,B
こんな感じのMultiMatteElementAとBを


MergeノードのAコネクトとしてMultiMatteElementAを、BコネクトとしてMultiMatteElementBをplusで合わせる。でoutputとして今回は別に「maskA」というチャンネルを設けました。outputを指定することで、Mergeノードでの計算結果がそのoutputで指定されたチャンネルに格納されます。また、MultiMatteElementA,B,Cにはアルファチャネルがないので plus オペレーションで計算してます。
すると、ここまでで


maskAチャンネルとしてこんな結果を得ることが出来ます。
あとは、MultiMatteElementCに対しても同様にしてやると・・・


として、


を得る。
でも、Expressionノードを使うと・・・


channelsでmaskAを指定して、さらにredチャンネル固定で、式として

MultiMatteElementA.red + MultiMatteElementB.red + MultiMatteElementC.red

とする。同様にgreen、blueってやるとExpressionノード一つで得ることが出来る。要するに、Mergeノード2つでやっていた3つのチャンネルの足し算をExpressionノード一つでできる。
たとえば、ShufflleノードMultiMatteElementCをMultiMatteElementBなどに変換してやればShuffleとMergeの組み合わせでもできるが、Expressionノードを用いて一つで出来るほうがすっきりとはする。

次に、


というようなものを作ろうとすると・・・


こんな感じで、RGBを変換してやる必要があるのでShuffleノードなどで調整が必要だ。しかも、これだけやっても、得ることの出来る画像は


と一列分であと2回同様の工程が必要だ。
でもExpressionノードを使えば、


って感じで、一気に


を得ることが出来る。
また、


こんな感じでも、


として一つのExpressionノードで作成が可能だ。

さらに、計算式を少し工夫してやると、


これで、


って感じのものを作ることも出来る。

Expressionノードはコネクトが一つしかないが、MergeExpressionノードというものもあり、それは2つをソースに出来る。
なので、


とすると、このカラフルなグラデーションを動かすことができる。左右はループしている。



2011-12-14

VectorBlurに関して

VectorBlurだけど、デフォルト値だとちょっと不都合が多い。
デフォルト値で「method」が「backward」になっているが、


これだとキレイにかからない・・・
この「method」を「forward」に変更すると、


と、「backward」でやっていたときは、ボケがアルファの内側に限定されていたような感じだったのが、ちゃんと、外にはみ出てきてきてる。説明によると「backward」はモーションベクターが連続している部分でのに正しく評価されるようで、ここを「forward」にすると、全てのピクセルに対して、モーションベクターによって特定の方向にブラーが入り、ちゃんとそれが積み重なるようです。

また、「alpha」に下画と合成する際のアルファチャンネルをセットすると、


といった具合に、上手く合成できます。

2011-12-12

project directory に関して

Project Settings(ノードグラフでショートカット[s])に 「project directory」 というものがある。


これはReadノードなどで、たとえば

C:\Users\hogehoge\111211\fooage\hoge.jpg

のように読み込んできているファイルパスに対して、たとえばこの「project directory」が

C:\Users\hogehoge\111211

とすると、さっきのReadノードの ファイルパスは

./fooage/hoge.jpg

と出来る。つまりは 「.(ドット)」 =  C:\Users\hogehoge\111211 であり、これこそが、さきほど「project directory」に入れたパスである。つまりが、「project directory」のファイルパスが、「.(ドット)」で置きかえることが出来る。


また、Project Settingsの「project directory」の横にある、「Scripts Directory」ボタンを押すと、


「project directory」のファイルパスとして、[python {nuke.script_directory()}]という文字列が自動的に入る。これは.nkのセーブされているパスを示す。この場合、

C:/Users/Jean/Documents/111211/nukeScripts

ということになる。
たとえば、あるReadノードが.nkが保存されてる階層より、同階層またはそれ以下でない場合、


C:/Users/Jean/Documents/111211/footage/screen.png

こんな風に、
111211
    |-footage
             |-screen.png
    |-nukeScripts-filePath.nk
             |-filePath.nk

となっており、.nkの保存パスの同階層もしくはそれ以下でなく、一旦上の階層に上らないとReadノードのパスが指定できないばあいは、


../footage/screen.png

といった具合に、「../」とドットを二つ重ねて(一つ上に上がるの意味)「C:/Users/Jean/Documents/111211」から見たパスで置き替えが可能だ。

さらに6.3v2以前はWindowsではWriteノードでこの「.(ドット)」による置き換えが出来なかった。これは、.nkの保存パスを表すエクスプレッション[python {nuke.script_directory()}]で書くと、Windowsの場合標準のファイルパス表記である「¥(\)」が用いらるが、nuke場合、基本的にファイルパスがすべて「/(スラッシュ)」で無ければならないためであるが、6.3v3よりこれが改善され、Writeノードでも可能になっている。
といっても、以前も問題があったのはWriteノードだけであるが。Readノードは以前から可能でした。

これによって、相対パスでファイルパスを指定することが可能。

注意:deadlineからレンダリングしようとすると、各スレーブたちは一旦ローカルのtempとかに.nkを保存しなおして(ダウンロードしてきて)レンダリングをしようとするとの、 .nkの保存パスを表すエクスプレッション[python {nuke.script_directory()}]を使っていると、そのtempフォルダから見たパスとかになってしまい、当然そこには読み込み先も書き出し先も無かったりするので、相対パスを使うのは危険。
ただ、作業中はこの、 Project Settingsの「project directory」のファイルパスを[python {nuke.script_directory()}]としてやっておいて、deadlineに投げる時に、そこを絶対パスに入れ替えればよい。そうすればたとえReadノードが多数あっても、「.(ドット)」をつかって、ファイルパスを書いておけば、たった一つの「project directory」のファイルパスだけを書き換えるだけで問題なくレンダリングできる。

2011-12-09

ToolSets と python の組み合わせ その2

と、前回は基本的なpythonコードをnukeから実行するという説明でした。しかしながら、別にファイルコピーが出来たからってルックがあがるわけでもなく、アーティストレベルだとそんなに要らないんじゃないか?そんな効率が上がったところでトラッキングがぴったり合うわけでも、クロマキーで撮った素材のなじみがグッと良くなるわけでもないかもです。
ただ、トライアンドエラーの回数が増えれば自ずとクオリティーはあげていけるんじゃないかと。

とあるアニメーションがすごく上手な方に訊いてみました・・・


半ば誘導尋問ではありますが、上手い人でも何度も確認してっていう工程があると思います。経験をつめばある程度その部分が早くなっていくんでしょうが。

で、話はnukeに戻して、nukeの問題点、そのうちの一つはやはりプレビューにあると思います。再生ボタンおして aggressive cache をオンにしていても、fpsがまともに出なくて、やはり引っかかる部分もあるし、AfterEffectsのRAMプレビューのような快適さと賢さは備わっていないと思います。ぜんぜん aggressive じゃありません。

それを回避するために、6.2ぐらいから、「Flipbook this viewer」という機能が標準で付きました。


結果を見たいviewerを選んで実行すると、レンダリングが始まりレンダリング終了後にFrameCyclerが立ち上がります。FrameCyclerはRAMにキャッシュさせて再生が可能なので、それだと指定したfpsをしっかりと実現して確実なプレビューができます。
こういったものを使えば、一々Writeノードでファイルパスなどをつけたり、ディレクトリを切ったりとという煩わしさも無くプレビュー・確認の回数を気軽に増やすことが可能です。
ただ、FrameCyclerが重いし使い辛い。しかも、nukeに標準でついてくるFrameCyclerはどうやらnukeと連動してでしか使えないようで、単体で立ち上げようとしてもライセンスエラーなどで立ち上がりません。
この「Flipbook this viewer」、実は、Flipbookの部分でFrameCyclerしか選べないんですが、pyをいじることでPDPlayerのような別のシーケンスビューワーを選ぶことも可能といえば可能です。このただ「Flipbook this viewer」のインターフェイスを用いてやるには諸々と改良が必要ですが・・・

と前置きがまたもやかなり長くはなりましたが、ToolSets と python を組み合わせて使うことで自前でプレビューを簡単に作る仕組みを作ることを今回の目的としています。
流れとしては・・・

1.選んだノードに新規でWriteノードをぶら下げる。
2.そのWriteノードはQuickTimeをレンダリングするようにしておき
3.勝手にレンダリングを行い、終了後そのQuickTimeを自動的に開く

ということをしようと思います。これで選んだノードでストレス無くプレビューを作れ、かつバージョン保存等も可能です。
1.の 選んだノードに新規でWriteノードをぶら下げる ですが、コレは、以前の「ToolSets と python の組み合わせ その1」ほぼ同じです。なんですが、今回はToolSetsを利用します。どういうことかというと、QuickTimeなどの面倒な設定をpythonから呼び出すのではなくToolSetsとしてテンプレートを記録しておきます。AfterEffectsのレンダリング設定のテンプレートのようなものです。 

まずはテンプレートとなるノード群を作成します。とりあえずシンプルにWriteノードで出力先を指定せずにQuickTimeのコーデックの種類等を決定します。


ファイル名(ファイルパス)は後ほど設定します。
さらにフレーム番号を右下に表示させたいので、Textノードを追加して、


で、このTextノードは


というような感じで、諸々が設定できるので、
まず、文字のサイズ。HDサイズで「75」ポイントとして、そのほかのサイズがきても、ちょうどいいサイズになるように、


75 * float(nuke.value(nuke.thisNode().input(0).name() + ".bbox.h")) / 1080

エクスプレッションとしてpythonコードをいれた。
細かく見ると、

nuke.thisNode().input(0).name() でこのノードの親ノードの名前
nuke.value(nuke.thisNode().input(0).name() + ".bbox.h") で上記の親ノードの名前の縦の解像度

floatをつけているのは、どうやら nuke.value(nuke.thisNode().input(0).name() + ".bbox.h") が文字列で帰ってくるみたいなので、floatをつけて数字として扱えるようにしている。
あとはその縦の解像度を 1080 で割ってHDサイズとの比率をだして、最後に75を掛けている。

とそんな感じで、どんな解像度がきても同じ位置に、解像度に対して同じようなサイズでフレーム数が入るようにした。

で、この二つのノードをToolSetsとして登録する。


二つのノードを選択して、「ToolSets」→「Create」で


適当な名前をつけて保存する。
以前にも説明したが、一度登録すると、ホームディレクトリの「 .nuke」フォルダに先ほどつけた名前の.nkで保存され、「ToolSets」から呼び出せるようになる。

 
こんな感じで。
あとは、このToolSetsをpythonから呼び出し、レンダリングされるQuickTimeのファイルパスに与えてやり、それをショートカットに割り当てれば大まかに完成だ。(勝手にレンダリングを始めるようにしたり、プロキシーモードで実行したり、レンダリング後勝手にQuickTimeを開いたり・・・ 色々とあるが)

ということで、コレを呼び出すというのをpythonで表現すると・・・

--------------------------------------------------------
nuke.loadToolset(.nkのファイルパス)
--------------------------------------------------------

になる。.nkは先ほども書いたがホームディレクトリの「 .nuke」フォルダに保存されてる。
ホームディレクトリは

C:\Users\hogehoge\.nuke\

のように直接的に明示することも出来るが、せっかくpythonを用いるのだから環境に依存しないように、ある程度どこで開いても可能なように書きたいので、

-------------------------------------------------------- 
import os 

os.environ['HOME']
-------------------------------------------------------- 


でホームディレクトリを表せることをもちいて、

-------------------------------------------------------- 

import os
mynukePath = os.environ['HOME'] + r"/.nuke"
nuke.loadToolset(mynukePath + r"/ToolSets/Preview.nk")

-------------------------------------------------------- 

で、いま登録したToolSetsが呼び出せる。

呼び出した後は、それで作成されたノードが選択されているので、それを利用して、Writeノードのファイルパスを設定する。

-------------------------------------------------------- 
nuke.selectedNodes('Write') 
-------------------------------------------------------- 

とすると、選択しているノード群の中のWriteノードを表すことが出来ます。ただし、  

nuke.selectedNodes


と、たとえ表現したいWriteノードが一つでも「s」をつける必要があり、「s」が付いているので、返ってくるのはリストです。今回の場合は呼び出したToolSetsにWriteノードは一つしかないので、このToolSetsを呼び出した直後だと

-------------------------------------------------------- 
nuke.selectedNodes('Write')[0]
--------------------------------------------------------

となります。
でファイルパスを指定するのですが、今回は簡単にするために.nkと同じところに.nkと同じ名前で保存することにします。
(以前のエントリー 「関数と引数 その1 (python入門)のコメントでtakkunさんに教えていただいたos.path.dirname などを使えばもっと色々と出来ます。

.nkのファイルパスは

--------------------------------------------------------
nuke.root().name()

--------------------------------------------------------  

なので、

--------------------------------------------------------
wrNode = nuke.selectedNodes('Write')[0]
nkName = nuke.root().name()
qtName = nkName.replace(".nk", ".mov") 
qtProxy = nkName.replace(".nk", "_proxy.mov")
wrNode.knob('file').setValue(qtName)
wrNode.knob('proxy').setValue(qtProxy)
 --------------------------------------------------------

となります。プロキシーにも自動的に_proxyで設定するようにしました。
 あと、レンダリングのフレームレンジですが、シーンと同じ尺として、

--------------------------------------------------------
fframe = nuke.root().knob('first_frame').value()
lframe = nuke.root().knob('last_frame').value()
wrNode.knob('first').setValue(fframe)
wrNode.knob('last').setValue(lframe)
--------------------------------------------------------

です。
で、コレを実行するには

--------------------------------------------------------
nuke.execute(wrNode, fframe, lframe)
--------------------------------------------------------

です。さらに、レンダリング後にこのQuickTimeが自動的に立ち上がるようにするために

-------------------------------------------------------- 
import os
os.popen(qtName)
--------------------------------------------------------


とし、レンダリング結果のファイルパス(qtName)をpopenから実行することでそのQuickTimeを開くようにしました。



またレンダリング終了後このノードを消したい場合は、

--------------------------------------------------------
for n in nuke.selectedNodes():
    nuke.delete(n)

--------------------------------------------------------

です。
これらをまとめると、

-------------------------------------------------------- 

import os

#ToolSetsの呼び出し
mynukePath = os.environ['HOME'] + r"/.nuke"
nuke.loadToolset(mynukePath + r"/ToolSets/Preview.nk")

#ToolSetsのWriteノードの各種設定
wrNode = nuke.selectedNodes('Write')[0]
nkName = nuke.root().name()
qtName = nkName.replace(".nk", ".mov") 
qtProxy = nkName.replace(".nk", "_proxy.mov")
wrNode.knob('file').setValue(qtName)
wrNode.knob('proxy').setValue(qtProxy)
fframe = nuke.root().knob('first_frame').value()
lframe = nuke.root().knob('last_frame').value()
wrNode.knob('first').setValue(fframe)
wrNode.knob('last').setValue(lframe)

#レンダリング
nuke.execute(wrNode, fframe, lframe)



#レンダリング後のQuickTimeの実行
os.popen(qtName)

#レンダリング後のノードの削除
for n in nuke.selectedNodes():
    nuke.delete(n)

--------------------------------------------------------


となります。これをショートカットに割り当てて容易に呼び出せるようにするために関数かします。といっても引数は無いのでそんなに難しくはありません。

-------------------------------------------------------- 

import nuke
import os

def previewQT():
    #ToolSetsの呼び出し
    mynukePath = os.environ['HOME'] + r"/.nuke"
    nuke.loadToolset(mynukePath + r"/ToolSets/Preview.nk")

    #ToolSetsのWriteノードの各種設定
    wrNode = nuke.selectedNodes('Write')[0]
    nkName = nuke.root().name()
    qtName = nkName.replace(".nk", ".mov") 
    qtProxy = nkName.replace(".nk", "_proxy.mov")
    wrNode.knob('file').setValue(qtName)
    wrNode.knob('proxy').setValue(qtProxy)
    fframe = nuke.root().knob('first_frame').value()
   
lframe = nuke.root().knob('last_frame').value()
    wrNode.knob('first').setValue(fframe)
    wrNode.knob('last').setValue(lframe)

    #レンダリング
    nuke.execute(wrNode, fframe, lframe)



    #レンダリング後のQuickTimeの実行
    os.popen(qtName)


    #レンダリング後のノードの削除
    for n in nuke.selectedNodes():
        nuke.delete(n)

--------------------------------------------------------

とし、previewQT.pyという名前で.nuke フォルダ に保存して、previewQT() で呼び出せるようにしました。

あとは、.nuke フォルダにある menu.py に


--------------------------------------------------------
import previewQT
m.addCommand( "previewQT", "previewQT.previewQT()", "Alt+r")
--------------------------------------------------------

と記述を加えると、 「Alt+r」で呼び出せます。
最初の 「previewQT」はコマンド名なので、適当につけて問題ないです。
次の「previewQT.previewQT()」はあらかじめ「import previewQT」でインポートしたpreviewQT.pyの関数previewQT()ということになります。

これで、previewしたいノードを選んでショートカットを押せば自動的にレンダリングされ、プレビューとなります。
 
もう少しカスタマイズすれば、プロキシーでレンダリングしたり、1フレ飛ばしでレンダリングしたりと色々と可能です。