Edited at

Blender Compositor ノードエディタでの小ネタ・小技集

More than 1 year has passed since last update.


この記事は Blender Advent Calender 2017 12/16 日目の記事です。


主にBlenderでコンポジットノードを弄る時用の細かいTips集です。


いくつかのショートカット操作


Ctrl+Shift+クリック でビューワ設定

image

クリック先のノードにViewerノードを接続。Backdropのチェックを入れておくことで各ノードの結果を素早くチェックできて便利。複数Viewerノードがある場合は最後に選択されたViewerノードが使用されるようです。

ちなみに標準で同梱されているNode Wranglerアドオンを有効化しておくと、Cyclesマテリアルノードでも同等の機能が使えるようになって便利です。また他にもノード操作を行う上で便利なショートカットが有効になるので事実上必須。有効にしておきましょう。


参考:

Blender Addon Review: Node Wrangler(ノード使い必須) – CGrad Project

http://www.cgradproject.com/archives/3553



Ctrl+Shift+D でインプットへの接続を保ったままノード複製

image

Shift+Dで選択中のノードを複製できますが、更に Ctrl を加えてCtrl+Shift+Dを入力することでそのノードへの接続を保ったまま複製ができます。


接続の切断とRerouteの追加

image

それぞれCtrl+ドラッグ Shift+ドラッグでノード間の接続の接続やRerouteの追加ができます。


画像ドロップでImageノード追加

image

UV/Imageエディタでの画像名横のアイコンをノードエディタ上にドラッグ&ドロップすることでImageノードを追加できます。Windowsならエクスプローラからも同じ操作が可能。(MacやLinuxの場合は未確認)


ノード追加用のショートカットキーを用意しておくと地味に便利

ノードを追加する時はShift+Aキーから目的のタイプを選択するのが普通ですが、よく使うノードはそれを追加する用のショートカットキーを用意しておくと便利です。

Shift+Aキーからノードを追加した時にインフォパネルに表示される情報を参考にしつつショートカットキーを追加します。

hotkey1.jpg

Blender設定の Input > Node Editor > Node Editor (Global) で Add ボタンを押し新規キー設定を追加、割り当てたいキーを入力、実行するオペレータの項目はnode.add_nodeとし、Node Typeの項には例えば CompositorNodeValue を入れるとコンポジットノードでのValueノードの追加になります。

hotkey2.jpg

上記設定ではCtrl+Shift+VキーでValueノードが追加されるようになります。

ノードタイプはCompositorNodeValueの他にもNodeReroute (Reroute)、

CompositorNodeMixRGB (Mixノード)、CompositorNodeMath (Mathノード) 等もオススメ。

ただ上のノードタイプはReroute以外コンポジットノード専用のものなので、Cyclesノード上で設定したキーを押すとエラーが出るのが難点。共通のキーでコンポジット/Cyclesノードに対応するには他のアドオンに頼る他無さそうです。


ノードエディタ上でGrease Pencilが使える

ノードエディタ上でもDキー + マウスドラッグでグリースペンシルが使えます。

gp1.jpg

Dキーを押しながらTabキーでグリースペンシルの編集モードに移行。3Dビューでの操作と同じですね。

gp2.jpg


Maskノードを使ってノードの画像サイズを設定

Maskノード(とMixノード)を組み合わせることで画像バッファのサイズを指定することができます。典型的な利用法としてはTextureノードのサイズを自由に設定したい時など:

Textureノードそのままだとその画像サイズはレンダリングパネルでのサイズに依存する形で設定されてしまいますが

MaskノードをMixノードの上ソケットに繋ぎ、Textureノードを下ソケットに繋げると、結果としてMaskノードで設定された画像サイズのテクスチャ画像が出力されます。

上のMixノードのFactor値は1.0に設定されてるので本来上ソケットに繋がれてる画像は無視されるだけの無意味な処理となる筈ですが、画像サイズは上ソケットに繋がれているものが継承されるという仕様になっているようです。

またTextureノードは接続先ノードの画像サイズに応じて最終的なサイズを変化させるという分かりにくい仕様になっているため、結果として上の手続きでサイズを自由に指定できるという流れになっています。

他の利用例として、画像の拡縮を行う際にScaleノードを通すだけでは画像サイズは変わりませんが、上の方法を活用することできちんと画像サイズも変化させる拡縮を行うことが可能になります。


Imageノードのアルファチャンネルのタイプに注意

このように半透明な箇所を含むレンダリング結果を画像ファイルに出力したとします。

コンポジットでAlpha Overノードを使って単色の上に重ねる場合、Render Layerから直接繋いだ場合は問題がありませんが

先程出力した画像ファイルをImageノードとして読み込み、それをAlpha Overで重ねると妙な結果になります

画像のアルファチャンネルにはStraightPremultipliedの2種類の取り扱い方があり、どうやらBlenderは標準ではPremultipliedで画像を扱うものの、Imageノードの出力はStraightとして読み込まれるみたいです。なので透明部を含むImageノードはAlpha ConvertノードのStraight to Premulを通して変換してやることで、きちんと正しい結果が得られるようになります。

またAlpha Convertノードを使わずともAlpha OverノードのConvert Premulチェックを入れれば、下ソケットの画像についてStraight to Premulと同等の変換を施してくれるようです。

ただConvert Premulチェックは若干仕様が妙なところがある(個人的な感想)のでなるべくAlpha Convertノードを通すようにしたほうが安全かもしれません。


参考:

文化ヒナゲシ制作所雑記帳 【Blender】アルファオーバーの扱い方について(ストレートアルファ・プレマルチプライドアルファ)

http://matosus304.blog106.fc2.com/blog-entry-286.html



Levelsノードで画像全体の平均値を取得

Add > Output > Levelsノードのインプットに画像を繋げると、その画像の平均値を取得できます。

ave1.png

Meanは平均値でStd Devは標準偏差を出力してくれます。

これを活用すれば、画面上に何かが写った時をトリガーとして何らかの効果を与える、といったことが可能になります。

例えば下のように元画像Aとオブジェクトの一部のみを表示したマスク画像Bとを用意したとして、

ave2.gif

画像Bの平均値が少しでも0より大きくなったら1として見なすようにしつつMixノード(ここではAddノード)のFactorに繋ぎます。

Mixノードの下ソケットには適当な画像を繋いでおきます。すると――

ave4.gif

マスク画像の白い箇所が表示されたタイミングで別の画像が表示されるようになりました。画像内の一部の値を全体の効果に波及できるので色々使いみちがありそうです。


Pixelateノードでモザイク

Filter > Pixelateノードはモザイク処理を施してくれます。

適当な値nとして、画像をScaleノードで一旦1/n倍に縮小してからPixelateノードを通し再びScaleノードでn倍にすると、全体にモザイク効果が出ます。

使いやすいようにグループ化しておきます。

Levelsノードの項でも使用した画像A,Bをまた用意

ave2.gif

両方の画像にフィルタを通しつつ、画像Bの方はMathノードのGreater Thanで2値化しておきます。

そして画像Bの結果をMixノードのFactorとして利用しつつ画像Aの処理前と処理後とを振り分けると――

mosaic4.gif

マークした箇所に自動でモザイク処理を施してくれるようになりました。

今回の簡単な例のままではまだ若干見切れている箇所があるので、フィルタのかかる範囲をより広めにしたい場合は画像Bにブラーをかける等の前処理を入れると良いと思います。


直前のフレームのコンポジット出力を利用する

現在よりも前の出力結果を利用することで漸次的な画像フィルタ・エフェクトを実現できます。


下準備

File Outputノードで適当な場所にコンポジットの結果をファイルとして出力するようにしておきます

一旦レンダリングをし、当該箇所に画像ファイルを生成しておきます。次にImageノードとしてそのファイルを読み込み、タイプをImage Sequenceにします。Framesは適当に大きな値に設定し、Start Frame2に設定します。これで準備完了です。


基本形

まず各フレームを普通にレンダリングした結果は次のようにしてみます。実際には背景は透過しています。

grad3.gif

ただスザンヌが動いているだけです。

ここでコンポジットノードを次のように組んでみます

結果は下のようになります

grad5.gif

上のノードで何をしているのかというと、まずImage Sequenceノードは指定場所に存在する複数の連番画像を現在のフレームに対応して出力を変えてくれます (1フレーム目なら0001.png, 5フレーム目なら0005.png等) が、上でStart Frameをデフォルトの1から2に変えておいたため、現在のフレームに対して1つ前の画像が返されるようになっています(5フレーム目で0004.png等)。1フレーム目では対応する画像が無いので出力は空になっています。

またFile Outputノードはレンダリングしたフレーム毎に連番画像を指定した場所に生成するので、例えば1フレーム目なら0001.pngを、フレームを進める度に0002.png, 0003.png, ... といった具合にファイルを出力していきます。

上のImage SequenceノードはFile Outputで出力される場所を指定したのでフレームを進めてレンダリングする度に現在のフレームより1つ前の、つまり直前にFile Outputで出力したばかりの画像を参照していくことになります。

今回のメインの処理は「Image Sequenceで読み込んだ画像の上に現在のレンダリング結果をAlpha Overで重ねる」という風にしているため、フレームを進める度にそのフレームのスザンヌが重なっていき、最終的に無数に連なった出力になりました。



応用例

残像

grad6_1.gif

基本形のノードのImage Sequenceノードのアルファ値を毎フレーム減衰させているだけです

疑似流体

grad7_1.gif

Displaceノードと組み合わせると流体のような表現になります。上のノードでは場所によってランダムな方向に変形させるためにCloudsテクスチャを活用しています。

疑似流体2

grad8_1.gif

TextureノードのOffsetのベクトルを時間経過と共に変化させています。(ベクトルのXYZを一本化するノードはCombine RGBAで代用)

また残像の項目でやったものと同じくアルファ値の減衰もさせています。

クリッピング

grad9_1.gif

File Outputで出力するのはオブジェクトのアルファ値のみにし、それをフレームごとに加算しています。加算していった値をMixノードのFactorに繋げれば2つの画像をオブジェクトの軌跡でクリッピングできます。


Image SequenceのStart Frameの値を変えれば直前のフレームだけでなく2つ前やそれ以上過去のフレームも参照できます。またFile Outputを複数用意すれば複数の異なる過去の出力結果を参照できるので、やりようによってはより複雑なフィルタ・エフェクト処理が可能になると思います。


簡易ドライバ

コンポジットだけでなくBlender全般で使える話ですがメモがてら…。

ドライバの追加可能な数値入力欄で例えば#frameと入力すると、その項目にドライバが設定され、現在のフレームが自動的に入力されるようになります。

(Blender 設定で File > Auto Run Python Scripts を有効にしておく必要あり)

driver1.png

#frameのように簡易的な形でドライバの設定に使えるものとしては、frameの他にもpieといった定数、sincos等の関数が用意されてます。数値入力欄で#cos(2*pi)のように入力してみると確かに計算結果が返ってきます。

どのような変数や関数がこのような簡易的なドライバ設定の中で使えるのかといえば、どうやらbpy.app.driver_namespaceの中に展開されているメンバがそれに該当するようです。スクリプトコンソールでfor i in bpy.app.driver_namespace: print(i)と入力してみるとそのリストを見ることができます。


またユーザが独自に上のようなメンバを追加することもできます。コンソールでbpy.app.driver_namespace["a"] = 123を実行してからどこかの数値欄で#aと入れてみると、ドライバの返り値として123が設定されます。bpy.app.driver_namespace["f"] = lambda x:x*2 のように関数を代入すれば #f(100) で計算結果の 200 が設定されるようになります。

driver3.png

これでユーザ独自の簡易ドライバ用変数を用意することができましたが、このままだとファイルを読み込む度にリセットされてしまうので、ファイルを読み込むタイミングでスクリプトを実行するようにします。

BlenderのText Editorを開き下記コードを入力します。テキスト名を.pyで終わるものにするとRegisterのチェックボックスが有効になるので、それをオンにするとファイル読み込み時にそのコードを実行してくれるようになります。

import bpy

bpy.app.driver_namespace["a"] = 123
def f(x):
return x*2
bpy.app.driver_namespace["f"] = f
bpy.app.driver_namespace["g"] = lambda x:x**2
bpy.app.driver_namespace["get_current_frame"] = lambda: bpy.context.scene.frame_current

def on_frame_change(scene):
bpy.app.driver_namespace["time2"] = (scene.frame_current-scene.frame_start)/(scene.frame_end-scene.frame_start)
bpy.app.driver_namespace["render_scale"] = scene.render.resolution_percentage / 100

bpy.app.handlers.frame_change_pre.append(on_frame_change)
on_frame_change(bpy.context.scene)

driver2.png

上記コード中では定数a、関数f g get_current_frameと、シーンのフレームを移動するごとに値を更新するtime2 render_scale を宣言しています。

それぞれ



  • f g -- 関数の設定例( #f(100) #g(f(100)) のようにして使用可 )


  • #get_current_frame() -- #frameと同じ値


  • #time2 -- Timeノードにシーンの開始フレーム・終了フレームを入れた時と同じ挙動


  • #render_scale -- レンダリング設定の解像度のパーセンテージを正規化した値

のようになっています。

time2 render_scaleに関してはフレーム移動のタイミングで値を更新するためにbpy.app.handlers.frame_change_preへのハンドラ登録を行っています。ともかくこれでユーザ独自の簡易ドライバ用変数・関数が定義できるようになりました。

ちなみに



  • #bpy.context.scene.frame_current のようにして bpy.~~ から始まるコードも簡易ドライバとして使えます。
    ただ記述が冗長になりがちで何をしているか分かりにくいのとメンテナンス性が落ちることから、上のように独自のメンバを定義して使うのが賢明だと思います。

  • ドライバのScripted Expressionとして設定されるので、例えば#1 if frame<10 else 0のようにPythonの三項演算子等も利用できますね。


参考:

python - Other possible expressions like #frame - Blender Stack Exchange

https://blender.stackexchange.com/questions/2893/other-possible-expressions-like-frame



この記事は Blender Advent Calender 2017 12/16 日目の記事でした。

次回は「Blenderのコンポジットノードでライフゲームする」です。