2014-12-16

expression in "filter" knob

TransformノードやReformatノードのfilterにexpressionを張りたいときは、
If you want to add expression at "filter" in Transform node or Reformat node,

ノード選択してコピーして、Script Editorでペーストする。
Select the node and copy and then paste in Script Editor.


ここでnameの直前に、インデントをあわせて、filter {{Transform1.filter}} を加える。
Here, you just need to add "filter {{Transform1.filter}}" avobe the "name" line. Please don't forget there should be " " means space at the top of this line, I mean it's kinda indent.

 filter {{Transform1.filter}}


この場合、Transform1のfilterに向けてリンクがはられる。
In this case, this is the "link" to the "filter" knob of Transform1.



あとは、このScript Editorの内容をノードグラフすればTransform1のfilterにリンクしたTransformノードができる。
And then, once you copy and paste these code in Script Editor on NodeGraph, you'll get Transform node which has the expression for "Transform1" in its filter knob.






2014-11-03

ColorMatrix

ColorMatrixノードはピクセルのrgb値に対して指定したmatrixを掛けてくれるものだ。
たとえば、world spaceのnormalパスがあるとして、これをcamera spaceのnormalパスに変換したい場合、world spaceのnormalにカメラの回転マトリクスの逆行列をかければいい。

CameraMatrix allow us to multiply a "matrix" by the value of rgb.
For example, here is kinda normal pass which was rendered in CG as normal in world space, and then we will convert it to one in camera space. How? We can do this with multiplication the value of rgb of normal in world space and the inverse matrix which is "rotation matrix" of camera.






また、カメラの回転マトリクスは、カメラのスケールが(1,1,1)であれば、"Camera"ノードのworld matrixの((0,1,2),  (4,5,6), (8,9,10)) として得ることができる。

Well, when scale of Camera node is (1, 1, 1), you can handle as the world matrix of Camera node as the "rotation matrix" of camera. Actually it should be in m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9] and m[10].



なので、これをColorMatrixノードに代入する。逆行列ってのはこのColorMatrixのinverseにチェックをいれればOKです。

And then, put them in ColorMatrix node. Because we use it as inverse matrix, just check "inverse" box.




その後、このColorMatrixノードをworld spaceのnormalパスにつなげると

After that, we connect the input of this ColorMatrix to the Read node which has normal pass in world space......





normal pass(world space) > normal pass(camera space) の変換ができる。
逆に、camera space を world space に変えたい場合は、inverseのチェックをはずせばいい。

And We can get the normal pass in camera space!
In case that camera space one would be converted to world space one, we don't need to check "inverse" box.




この方法で、normal pass の回転も可能です。

You can rotate your normal pass in this way.

pyAlembic

You can find pyAlembic for windows in the following link.

http://pyalembic.sourceforge.net/
http://sourceforge.net/projects/pyalembic/files/


Actually in my Environment,

AlembicPyIex.dll
AlembicPyImath.dll
awBoost_python-1_52.dll

alembic.pyd
iex.pyd
imath.pyd

I have these files in the folder which is contained in result of "nuke.pluginPath()"



2014-10-22

matrix


matrixに値を入れたいとき、
node['matrix'].setValueAt(value, frame, index) 
matrixの値を取得したいとき、
node['matrix'].getValueAt(frame)[index]



When you want to set the value into matrix,
node['matrix'].setValueAt(value, frame, index) 
When you want to get the value of matrix,
node['matrix'].getValueAt(frame)[index]


2014-10-15

ノードグラフ上でノードを移動するツール

あれ?これって標準であったりする?
ノードグラフ上でグリッドの値だけノードをオフセットするツール。

NodeShift.py
import nuke def shiftHP():     shiftH = nuke.toNode('preferences')['GridWidth'].value()     sn = nuke.selectedNodes()     for n in sn:         posX = n['xpos'].value()         newPosX = posX + shiftH         n['xpos'].setValue(newPosX) def shiftHN():     shiftH = nuke.toNode('preferences')['GridWidth'].value()     sn = nuke.selectedNodes()     for n in sn:         posX = n['xpos'].value()         newPosX = posX - shiftH         n['xpos'].setValue(newPosX) def shiftVP():     shiftV = nuke.toNode('preferences')['GridHeight'].value()     sn = nuke.selectedNodes()     for n in sn:         posY = n['ypos'].value()         newPosY = posY - shiftV         n['ypos'].setValue(newPosY) def shiftVN():     shiftV = nuke.toNode('preferences')['GridHeight'].value()     sn = nuke.selectedNodes()     for n in sn:         posY = n['ypos'].value()         newPosY = posY + shiftV         n['ypos'].setValue(newPosY)


でmenu.pyに

menu.py
import NodeShift m.addCommand("shiftHP", "NodeShift.shiftHP()", "ctrl+shift+Right") m.addCommand("shiftHN", "NodeShift.shiftHN()", "ctrl+shift+Left") m.addCommand("shiftVP", "NodeShift.shiftVP()", "ctrl+shift+Up") m.addCommand("shiftVN", "NodeShift.shiftVN()", "ctrl+shift+Down")

の記述を加えれば。ctrl + shift + カーソルキー(矢印キー)で選んだノードを矢印方向に1グリッドだけ動かせる。複数選択でもOK。

2014-10-09

キーフレームにアクセスする

今回も、果たして誰かの役にたっているのだろうか・・・ というかなり備忘録的なpythonに関してです。

あるattributeにアニメーションがついているとして、そのキーフレームにアクセスして値を取得したり、シフトしたりしたいとします。
たとえばその場合、Transform1ノードのtranslateにx,yについている2個目のキーフレームの値を取得したい場合。



nuke.toNode('Transform1')['translate'].animation(0).keys()[1].y
となり、これが、xのアニメーション( animation(0) )の2個目のキーフレーム( keys()[1] )の値( y ) となります。
また、その2番目のキーフレームが何フレーム目に存在しているかってのは、

nuke.toNode('Transform1')['translate'].animation(0).keys()[1].x

となります。実行すると、そのフレームナンバーが返ってきます。
Graph Editor上でのxとy、つまり時間軸とその値です。

また、そのキーフレームの値を変更する場合は・・・

nuke.animation("Transform1.translate.x" , "x", ("2", "10"))

これで、Transform1 ってノードのattribute "translate.x" ( 位置のx座標 ) をフレームナンバー的( x ) に3個目のキーフレーム ( "2" ) を10フレーム目 ( "10" ) に移動させるといったことになります。

nuke.animation("Transform1.translate.x" , "y", ("2", "-30"))

これで、Transform1 の translate.x の3個目のキーフレームの値を( GraphEditor的にy値を )、-30 にするってことになります。

値を取得する場合は、

animation(i).keys() 

で、値を変更したい場合は、

nuke.animation("ノード名.アトリビュート名" , "コマンド名", (コマンドの種類に応じた引数))

となります。
後者はココが参考になるかと。
http://www.nukepedia.com/python/using-nukeanimation-without-tearing-your-hair-out


2014-10-03

ディレクトリをOSのファイルブラウザから開く

nukeは、ファイルブラウズにOSのファインダーやエクスプロラーなどといったものを使わずにnuke独自のものを使ってファイルを読み込んだりする。
が、時にこれが不便だったりする。特にReadノードのファイルパスの階層を開きたいとか、同じくWriteノードで指定しているファイルパスの階層にいきたいとか・・・

そんなときに長方するスクリプトです。

dirOpen.py
import nuke
import os
def main():
    sn = nuke.selectedNode()
    fP = sn['file'].value()
    dirP = os.path.dirname(fP)
    dirP = dirP.replace("/", "\\")
    dirP = dirP + "\\"
    
    #print dirP
    os.popen("explorer.exe" + " " + os.path.dirname(dirP))

dirOpen,pyとか適当に名前をつけて保存して、nukeのpluginPathに保存します。
ちなみにmacの場合は・・・

import nuke
import os
def main():
    sn = nuke.selectedNode()
    fP = sn['file'].value()
    dirP = os.path.dirname(fP)
    os.popen("open" + " " + os.path.dirname(dirP))

あとは、menu.pyに登録します。

menu.py
import dirOpen
m = nuke.menu("Nodes")
m.addCommand("dirOpen", "dirOpen.main()", "ctrl+shift+o") 

この場合、ctrl + shift + o ってショートカットを当ててます。
ReadノードやWriteノードを選んで、そのショートカットを実行するとOSのファイルブラウザでその階層が開けます。

2014-10-01

ノードにあるpythonタブの活用 とか諸々

ノードによってはPropertyにPythonタブがあるものがある。
たとえば、Writeノードとか。


ここにpythonを読み込むと、たとえば、Writeノードのファイルパス(指定のファイルパスのフォルダディレクトリ/フォルダ)がなければ、レンダー時にそのディレクトリを作成するといったことも可能だ。
具体的には、まずこのWriteノード( nuke.thisNode() )のファイルパス( ['file'].value() )が、存在しなければ( if not os.path.exists() )そのディレクトリを作成する( os.makedirs() )、って言う具合なpythonを書く。


import os
sn = nuke.thisNode()
fp = sn['file'].value()
dirP = os.path.dirname(fp)

if not os.path.exists(dirP):
    os.makedirs(dirP)

こんな感じ。
これをファンクションとして定義して、.pyで保存する。

makeDir.py
import os
import nuke
def main():
    sn = nuke.thisNode()
    fp = sn['file'].value()
    dirP = os.path.dirname(fp)

    if not os.path.exists(dirP):
        os.makedirs(dirP)


これを、プラグインパスとして読み込むことのできる場所においてやる。
ちなみに、プラグインパス の調べ方は、スクリプトエディタで nuke.pluginPath() とやればOKです。


こんな感じででます。もし、この際あたらしいパスを追加したいって場合は、こちらを参照してください。

ともあれ、その場所に上記 makeDir.py をおいたらWriteノードのPythonタブのbefore renderのところに、



import makeDir;makeDir.main()



という具合に書けば、あとは、Writeノードのファイルパスがなければ、自動的にそのフォルダ構造をつくってくれます。
その一行の中にある ; (セミコロン)は改行をしめしている。
つまり、本当は、



import makeDir
makeDir.main()

って二行を表現している。 プラグインパス内にあるmakeDir.pyを読んできて、そのmakeDir.pyのmain()って関数を実行する makeDir.main() という意味だ。

もどって、Writeノードのファイルパスに適当なパスを入れてみて、


これで、このパスは実際には(このA/B/Cって3つのフォルダが)存在しないのだけど・・・


レンダリングすると、


バージョンアップ(v001→v002)とかだと結構便利に使えます。ファイルパスのバージョンアップには最適です。
ちなみに、/test.v001/test.v001.%04d.exr だとか、/test_v001/test_v001_%04d.exr ってなファイルパスを含むReadノードやWriteノードを選択した状態で、alt+キーボードの矢印の上下(↑↓)で、バージョンを上げ下げできます。

また、こういうのはWriteノード作るたびにPythonタブのところに、上記の一行をいれるのは非常に面倒なので、menu.pyに

nuke.knobDefault("Write.beforeRender", "import makeDir;makeDir.main()")

の一行を書き加えておく。そうすると起動時にWriteノードにデフォルトでクダンの一行が書き加わり、今後新しく作るWriteノードにはその処理がなされる。

ちなみに、WriteノードのPythonタブに書き加えた

import makeDir;makeDir.main()

は二行であらわさないといけないから ; (セミコロン)が入っているが、menu.pyに

import makeDir

の一行がはいっていれば、

makeDir.main()

だけでよい。

2014-09-08

NaN の話

いろいろ作業をしていると[NaN]という数値(?)を持ったピクセルに出くわすことがある。NaN → http://en.wikipedia.org/wiki/NaN



原因はいろいろだけど、expressionなどで 0÷0(0割る0) みたいな計算があるとなるし、一見expression等がなくてもGizmoに含まれていることもある。
これがナカナカ厄介で、これをblurなどのピクセル拡張系のノードで処理すると拡大する。




これを回避する方法がnukepediaにも載っている(http://www.nukepedia.com/expressions/dealing-with-nan-pixels)、この方法だと、広範囲だったり、数ピクセルの間で点在している場合は回避しづらいので、そういう場合はMergeExpressionなどを使って原因となるノードの一つ前のノードをAにコネクトして、



nukepediaでの手法と同じくisnan()関数をもちいて、

isnan(r)?Ar:r

とかやれば回避しやすいです。

2014-09-02

nuke.pluginAddPath

init.pyに


nuke.pluginAddPath(ディレクトリ)


を加えると、その指定したディレクトリのinit.py, menu.py, ToolSetsフォルダを読みにいく。
ちなみに、

nuke.pluginAddPath(./Gizmos)

とかにしておくと、そのinit.pyの存在するディレクトリにあるGizmosフォルダを追加する。

2014-09-01

viewing LUT に cineon を追加する。

init.py に


nuke.ViewerProcess.register("Cineon", nuke.createNode, ("ViewerProcess_1DLUT", "current Cineon"))


を追加。

2014-08-31

Grain

標準のGrainの式は・・・



max(minimum.r,r+(2*Ar-1)*(r*red_m+black.r))

となっており、noise(fBm)で作成されたほぼ0~1までの乱数がスクリーン上に配布されたモノ(Ar)に対して、



(2*Ar-1) とあるので、ここの値はほぼ -1 ~ 1 となる。



さらに、ソースのピクセル値(r)に対して (r*red_m + black.r) とあるので、これはソースのピクセル値(r)にGrainノードのintensityのところの値(red_m)を掛けて、同じくGrainノードのblackのところの値(black.r)を足している。つまり、(r*red_m + black.r)部分はソースのピクセル値(r)もしくはユーザー指定の強度係数(red_m)のどちらかが 0 であれば black.r のみを返す。
で、この(r*red_m + black.r)部分とさっきの画面全体にばらまかれた -1 ~ 1 の係数を掛けたものをソースのピクセル値に足したもの r+(2*Ar-1)*(r*red_m+black.r) とGrainノードのminmumのところで指定した値 (minmum.r)を比べて大きい方を採用するっての言うのがGrainノードのざっくりとした仕組み。



上記の面倒くさいともされる式を念頭において、もっとざっくりと見た目中心でいうと、intensityのところで入れた値を元に、ソースピクセル値から適当な数値を足したり引いたりしているが、ソースイメージの黒に近い暗い部分にはその影響は出ない、もしくはほとんど見えないので、black値でその暗い部分にも影響が出るようにする。



black値の用途を暗い部分への影響という風にとらえると、式から判断するににマイナス数値を入れようがプラス数値を入れようがあまり意味合いは変わらない。(2*Ar-1) が -1 ~ 1 なので。

black値 = 0.06
black値 = -0.06
ただし、



このように、black値 = -0.06 の方には不思議なバンドがでている。これは、

(r*red_m+black.r) の部分の値が 0 付近になるあたりことで、r+(2*Ar-1)*(r*red_m+black.r) が結局ほぼ r (元の値/ソースのピクセル値) になることで出てしまう。black値にマイナス値を入れるとこういうことが起こりうる可能性がある。ちなみに、(r*red_m+black.r) の部分の値が 0 というのは ソースのピクセル値(r)とintensity値(red_m)の乗算した結果がblack値のマイナスをとったものと同じ値にななる場合。

また、intensityに1とかをいれると、ソースピクセル値が0-1に収まっていても、場合によっては2近くの値が出る可能性もあるし、真っ白付近の値なのに、真っ黒付近の値のピクセルが出てしまう可能性もある。

curvetoolで右の白い部分のみで計測

さらに、プレートでの暗部がマイナス値の場合でこの暗部にノイズを入れたい場合にも注意が必要で、

この場合、暗い部分は -0.02

一見、これだけで見ると暗部にノイズが入っているが、オリジナルと比較すると・・・


こんな感じになり、大丈夫そうにも見えるが、viewing lut をcineonにしてみると・・・


明らかな差が見えてくる。
これを解消するために、minmum値に元の暗い部分の値、この場合 -0.02 をいれておくと



こんな感じで回避できる。