この記事は Blender Advent Calender 2017 12/16 日目の記事です。
主にBlenderでコンポジットノードを弄る時用の細かいTips集です。
いくつかのショートカット操作
Ctrl+Shift+クリック でビューワ設定
クリック先のノードにViewerノードを接続。Backdrop
のチェックを入れておくことで各ノードの結果を素早くチェックできて便利。複数Viewerノードがある場合は最後に選択されたViewerノードが使用されるようです。
ちなみに標準で同梱されているNode Wrangler
アドオンを有効化しておくと、Cyclesマテリアルノードでも同等の機能が使えるようになって便利です。また他にもノード操作を行う上で便利なショートカットが有効になるので事実上必須。有効にしておきましょう。
参考:
Blender Addon Review: Node Wrangler(ノード使い必須) – CGrad Project
http://www.cgradproject.com/archives/3553
Ctrl+Shift+D でインプットへの接続を保ったままノード複製
Shift+D
で選択中のノードを複製できますが、更に Ctrl を加えてCtrl+Shift+D
を入力することでそのノードへの接続を保ったまま複製ができます。
接続の切断とRerouteの追加
それぞれCtrl+ドラッグ
Shift+ドラッグ
でノード間の接続の接続やRerouteの追加ができます。
画像ドロップでImageノード追加
UV/Imageエディタでの画像名横のアイコンをノードエディタ上にドラッグ&ドロップすることでImageノードを追加できます。Windowsならエクスプローラからも同じ操作が可能。(MacやLinuxの場合は未確認)
ノード追加用のショートカットキーを用意しておくと地味に便利
ノードを追加する時はShift+A
キーから目的のタイプを選択するのが普通ですが、よく使うノードはそれを追加する用のショートカットキーを用意しておくと便利です。
Shift+Aキーからノードを追加した時にインフォパネルに表示される情報を参考にしつつショートカットキーを追加します。
Blender設定の Input > Node Editor > Node Editor (Global) で Add ボタンを押し新規キー設定を追加、割り当てたいキーを入力、実行するオペレータの項目はnode.add_node
とし、Node Typeの項には例えば CompositorNodeValue
を入れるとコンポジットノードでのValue
ノードの追加になります。
上記設定ではCtrl+Shift+V
キーでValue
ノードが追加されるようになります。
ノードタイプはCompositorNodeValue
の他にもNodeReroute
(Reroute)、
CompositorNodeMixRGB
(Mixノード)、CompositorNodeMath
(Mathノード) 等もオススメ。
ただ上のノードタイプはReroute以外コンポジットノード専用のものなので、Cyclesノード上で設定したキーを押すとエラーが出るのが難点。共通のキーでコンポジット/Cyclesノードに対応するには他のアドオンに頼る他無さそうです。
ノードエディタ上でGrease Pencilが使える
ノードエディタ上でもDキー + マウスドラッグ
でグリースペンシルが使えます。
Dキーを押しながらTabキー
でグリースペンシルの編集モードに移行。3Dビューでの操作と同じですね。
Maskノードを使ってノードの画像サイズを設定
Maskノード(とMixノード)を組み合わせることで画像バッファのサイズを指定することができます。典型的な利用法としてはTextureノードのサイズを自由に設定したい時など:
Textureノードそのままだとその画像サイズはレンダリングパネルでのサイズに依存する形で設定されてしまいますが
MaskノードをMixノードの上ソケットに繋ぎ、Textureノードを下ソケットに繋げると、結果としてMaskノードで設定された画像サイズのテクスチャ画像が出力されます。
上のMixノードのFactor値は1.0
に設定されてるので本来上ソケットに繋がれてる画像は無視されるだけの無意味な処理となる筈ですが、画像サイズは上ソケットに繋がれているものが継承されるという仕様になっているようです。
またTextureノードは接続先ノードの画像サイズに応じて最終的なサイズを変化させるという分かりにくい仕様になっているため、結果として上の手続きでサイズを自由に指定できるという流れになっています。
他の利用例として、画像の拡縮を行う際にScaleノードを通すだけでは画像サイズは変わりませんが、上の方法を活用することできちんと画像サイズも変化させる拡縮を行うことが可能になります。
Imageノードのアルファチャンネルのタイプに注意
このように半透明な箇所を含むレンダリング結果を画像ファイルに出力したとします。
コンポジットでAlpha Overノードを使って単色の上に重ねる場合、Render Layerから直接繋いだ場合は問題がありませんが
先程出力した画像ファイルをImageノードとして読み込み、それをAlpha Overで重ねると妙な結果になります
画像のアルファチャンネルにはStraight
とPremultiplied
の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
ノードのインプットに画像を繋げると、その画像の平均値を取得できます。
Mean
は平均値でStd Dev
は標準偏差を出力してくれます。
これを活用すれば、画面上に何かが写った時をトリガーとして何らかの効果を与える、といったことが可能になります。
例えば下のように元画像A
とオブジェクトの一部のみを表示したマスク画像B
とを用意したとして、
画像Bの平均値が少しでも0より大きくなったら1として見なすようにしつつMixノード(ここではAddノード)のFactorに繋ぎます。
Mixノードの下ソケットには適当な画像を繋いでおきます。すると――
マスク画像の白い箇所が表示されたタイミングで別の画像が表示されるようになりました。画像内の一部の値を全体の効果に波及できるので色々使いみちがありそうです。
Pixelateノードでモザイク
Filter > Pixelate
ノードはモザイク処理を施してくれます。
適当な値n
として、画像をScaleノードで一旦1/n
倍に縮小してからPixelateノードを通し再びScaleノードでn
倍にすると、全体にモザイク効果が出ます。
使いやすいようにグループ化しておきます。
Levelsノードの項でも使用した画像A,Bをまた用意
両方の画像にフィルタを通しつつ、画像Bの方はMathノードのGreater Than
で2値化しておきます。
そして画像Bの結果をMixノードのFactorとして利用しつつ画像Aの処理前と処理後とを振り分けると――
マークした箇所に自動でモザイク処理を施してくれるようになりました。
今回の簡単な例のままではまだ若干見切れている箇所があるので、フィルタのかかる範囲をより広めにしたい場合は画像Bにブラーをかける等の前処理を入れると良いと思います。
直前のフレームのコンポジット出力を利用する
現在よりも前の出力結果を利用することで漸次的な画像フィルタ・エフェクトを実現できます。
下準備
File Output
ノードで適当な場所にコンポジットの結果をファイルとして出力するようにしておきます
一旦レンダリングをし、当該箇所に画像ファイルを生成しておきます。次にImageノードとしてそのファイルを読み込み、タイプをImage Sequence
にします。Frames
は適当に大きな値に設定し、Start Frame
を2
に設定します。これで準備完了です。
基本形
まず各フレームを普通にレンダリングした結果は次のようにしてみます。実際には背景は透過しています。
ただスザンヌが動いているだけです。
ここでコンポジットノードを次のように組んでみます
結果は下のようになります
上のノードで何をしているのかというと、まず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で重ねる」という風にしているため、フレームを進める度にそのフレームのスザンヌが重なっていき、最終的に無数に連なった出力になりました。
応用例
残像 | |
---|---|
基本形のノードのImage Sequenceノードのアルファ値を毎フレーム減衰させているだけです
疑似流体 | |
---|---|
Displace
ノードと組み合わせると流体のような表現になります。上のノードでは場所によってランダムな方向に変形させるためにClouds
テクスチャを活用しています。
疑似流体2 | |
---|---|
Texture
ノードのOffsetのベクトルを時間経過と共に変化させています。(ベクトルのXYZを一本化するノードはCombine RGBAで代用)
また残像の項目でやったものと同じくアルファ値の減衰もさせています。
クリッピング | |
---|---|
File Outputで出力するのはオブジェクトのアルファ値のみにし、それをフレームごとに加算しています。加算していった値をMixノードのFactorに繋げれば2つの画像をオブジェクトの軌跡でクリッピングできます。 |
Image SequenceのStart Frame
の値を変えれば直前のフレームだけでなく2つ前やそれ以上過去のフレームも参照できます。またFile Outputを複数用意すれば複数の異なる過去の出力結果を参照できるので、やりようによってはより複雑なフィルタ・エフェクト処理が可能になると思います。
簡易ドライバ
コンポジットだけでなくBlender全般で使える話ですがメモがてら…。
ドライバの追加可能な数値入力欄で例えば#frame
と入力すると、その項目にドライバが設定され、現在のフレームが自動的に入力されるようになります。
(Blender 設定で File > Auto Run Python Scripts
を有効にしておく必要あり)
#frame
のように簡易的な形でドライバの設定に使えるものとしては、frame
の他にもpi
やe
といった定数、sin
やcos
等の関数が用意されてます。数値入力欄で#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
が設定されるようになります。
これでユーザ独自の簡易ドライバ用変数を用意することができましたが、このままだとファイルを読み込む度にリセットされてしまうので、ファイルを読み込むタイミングでスクリプトを実行するようにします。
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)
上記コード中では定数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のコンポジットノードでライフゲームする」です。