CG・コンテンツ制作
  1. CG・コンテンツ制作トップ
  2. DAIKIN CG Channel
  3. UsersNotes
  4. ICE
  5. ICE による AmbientOcclusion;ICE AmbientOcclusion
SUITE USERS NOTES
ICE による AmbientOcclusion
ICE AmbientOcclusion
ICE_ac_00
ritaro_ml

プロローグ
これも、CEDEC2011にて公開したICE機能の1つです。
ICE_ac_01
ICEを使って頂点色をリアルタイムに変化させる 、というものです。
ゲーム用のデータ作成に使える機能かな、という感じです。
ついでにRaycastノードの使い方習得というかたちでまとめてみました。

表題にはAmbientOcclusionと書いていますが、
それらしい結果が得られた、くらいの認識でお願いします。
各自が欲しい効果になるようにカスタマズする際の参考になればと考えます。

公開にあたり、関係各者のご理解、誠にありがとうございます。感謝です。

 > NodeLocation
 > 頂点色設定の鍵

 > Raycast
 > 基本設定
 > 値の改善
 > コンパウンド化
 > インフィニット・ライトを使う

 > Ambient Occlusion
 > 3DVectorのArray対応
 > Fill Interpolated Array
 > 法線とインフィニットライト情報の追加
 > Array(配列)を0~1値にする
 > コンパウンド化
 > グループ対応のコンパウンド化
 > グループ対応のスクリプト
パソコンのスペックWindowsXP SP3、Intel Core2Duo 3G、 RAM; 3G、NVIDIA Quadro FX1700



NodeLocation
頂点色設定の鍵
ココでは、ポリゴンの頂点の色を変化させるという目的の為には、
『 各頂点に0~1の値を得て、そのデータを使って色を設定させる 』ってところがポイントです。
つまり、色々な方法を使って0~1の値を得られれば、それに応じて様々な色表現が得られる、ということなんです。

まず最初に、ポリゴンオブジェクトには頂点カラーマップのデータだけは付けておきます。
そして、そのポリゴンの色頂点データを取得する方法として
NodeLocationを使うのが、 ミソ です・・・。
Softimage ユーザガイドからその説明を見るとこう書いてあります。

NodeLocation
ポリゴンメッシュ上のポリゴンノード(または「ポリノード」や「テクスチャ サンプル ポイント」とも呼ばれる)のロケーション。
これは、頂点カラー プロパティの値を取得するときに役立ちます。

ということで、まずはそのあたりまでを作ってみましょう。
凹凸のあるポリゴンを作成します。(机です)
ICE_ac_02
取得 > プロパティー > 頂点カラーマップで頂点色の設定(UV_Cluster_AUTOの Vertex_Color)を付けておきます。
ICE_ac_03
ポリゴンに ICE_Tree ノードを設定し、SetDataself.colorself.cls.UV_Cluster_AUTO.Vertex_Color.Colors項目を設定します。
次に、GetDataself.NodeLocationを設定して己の頂点カラープロパティの値を取得して、
GetDatacolorを記入して、上で設定した色をここで拾い、
それをSetDataself.cls.UV_Cluster_AUTO.Vertex_Color.Colors項目に接続します。
これでもう、SetDataノードのself.color項目にて色を変化させると、その色が頂点色として反映されます。

更に、黒 > 白のGradientノードを取り出し、self.colorにつなぎます。すると全体が黒になります。
それは、今、各頂点に入っている位置データがで、その色が、だからです。

各頂点に0~1になるような値を設定することが出来れば、各頂点の色が黒から白の間の色になるはずです。

一旦ここまでをシーン保存して、次にRaycastの話に移ります。



Raycast
基本設定
まずはRaycastノードの使い方を身に付けちゃいましょう。
ICE_ray_01
ポリゴンの球を取り出し、0.5くらいのスケールにしてフレーズさせ、スケール値は1であることを確認します。
実は大きさは関係ないのですが、スケール値は厄介なことにならないようににしておいてください。
これをPolyLightと名前を変更しておいてここからレイが発せされる仮のライトとして扱います。
Raycastノードを取り出し、GeometryとしてGet Desk1を接続します。
Positionとしてself.PointPosition
DirectionとしてPolyLightの位置データから己のPointPositionSubtract(減算)した値を接続します。
その結果のLocationSetDataself.tmpと仮のデータに差し込んでICETreeに接続します。
これが最も基本的な接続例です。

Raycast の左側PositionDirectionを視覚化して、何やってるのか見て理解を深めましょう。
ICE_ray_02
self.PointPositionベクトルを表示させるとそのオブジェクトの中心からの直線上のポリゴン頂点からベクトルが発せられます。
ICE_ray_03
今度は、PolyLightグローバル位置を表示させると、ワールドの中心点から今ある位置まで、一本のベクトルが見れます。
ICE_ray_04
すると、DirectionSubtractで接続しているベクトルとは、
机の頂点の位置から発してポリゴンライト位置の方向に到達しているっていうベクトルが得られます。
いつもこうして検証して見れるのがICEの有効利用ポイントですね。

さて、この真ん中のGetPointPositionなのですが、まさにポリゴンの頂点と同じ位置なので、
影の影響を出すには、頂点位置から法線方向に少し浮かした位置から発した方が良い結果を得られます。
それを以下のように設定して実現します。
ICE_ray_05
Get PointNormalを取り出して法線方向を導き出し、そこにMultiply by Scalor0.01をかけて、
その値をPoinPositionAdd(加算)してあげればほんの少し浮かすことが出来ます。
これで己のポリゴン表面にレイが当たりやすくなり、影が出やすいようになります。
ICE_ray_06
今度は Raycastノードの右側を見ます。
LocationHitしかありません。
前述では、Locationを仮のtmp値に接続したのですが、
これでは個々のレイが当たった位置情報を表示してしまいます。(0,0.0とは当たらなかったという意味)
そうではなくて、Hitの方を仮のtmp値につなげてあげると、レイが何かに当たるとをかえしてきます。
つまり、このになった部分が影になれば、陰影が作成できます。
ICE_ray_07
それを実現させるため、Ifノードを取り出し、
もし当たったら0そうじゃなたら1をというResult(結果)最初に作ったGradiantに接続すれば、
おう!!頂点色がPolygonLightの位置によって変化するが実現できました。



値の改善
仕組みはもう大体見えて来ました。後はより良い結果を得る工夫次第です。
ヒントは、たくさんのRaycast事例がありますので、そこから面白そうな仕組みを取り入れてみましょう。
ICE_ray_08
レイの方向と法線の角度差から、陰影をもたらすことを考えてみます。
Subtract(減算)から出ている値はRaycastDirectionに接続しているベクトルでした。
その値と法線データGet PointNormalGetAngleBetweenにつないで角度を求め、
Rescaleノードを使って0~1の間の値に範囲を変換しています。
さらにFCurveを使って値変化に強弱を増し、Ifにつながるを求める部分につなぎ変えます。
この結果に違いは以下図↓のようになります。

ICE_ray_09

結果として、平行投影っぽい色合いから、ポイントライトっぽい距離による減衰効果が得られています。
ICE_ray_10
更に、近隣(4頂点)の平均値を取って来るという方法があったので、それはココではこのようにつなげます。
Ifから出る結果を一旦tmpという値に集め、Get Neighborsから各頂点の近隣の値をtmpから取得し、
Get Array Averageで平均値を取ってGradientにつなげると、上図の絵上と絵下の違いになりました。

更に、レイ角度にランダムのノードを差し入れて、それを何回がリピートした平均値を作り出す、ってのも試したはみたものの、
あまり綺麗な結果を得られなかったので、それは見送りました。



コンパウンド化
では、一旦ここまでのものをコンパウンド化して、簡単に使えるようにしておきます。
ICE_ray_11
左に外部入力出来るポイントを作成しておきます。
頂点色を設定するポリゴンのジオメトリーライトの位置のポイントを作成します。
一番下のGradientはReferanceとしてつないでおいて、
コンパウンドのノードをダブルクリックした時の表示画面から頂点色が変更できるようにしておきます。
右側はExecuteを1つにまとめておくと見た目良くつなげられるようになります。
ICE_ray_12

コンパウンドのプロパティーに、名前カテゴリノード色バージョンを設定してOKを押します。
コンパウンドの書き出しをすれば記入したカテゴリに書き出されます。



インフィニット・ライトを使う
光の方向を、通常良く使っているインフィニット(平行投影)のライトの設定画面から変更できるようする、
という風に改造してみます。
ICE_ray_20
普段、何気なく使っているライトは、回転値が0,0,0の時、-Z方向に向いている↑、ということを再認識すれば、
ライトの回転に従ってベクトルも回転するようにするには、Rotate Vectorを使って、
3DVectorZ値-1のベクトルを用意して、ライトの回転値をRotationにつなげれば、
同じ方向を追従する、と解ります。便利!!
Raycastに使う時はライトの方向に向くベクトルが欲しいので、3DVectorZ値は逆方向のになります。
ICE_ray_21
このようなつながりになりました。
色は、各R・G・B色に分けてからMultiplyで掛け合わせます。
これで、ライトの回転で明るさの方向が変わり、ライトの色で頂点色も変化します。

コンパウンドにまとめると、こんな風になりました。
ICE_ray_22



Ambient Occlusion
3DVectorのArray対応
さて、ここからは、少し今までの方法と違う事を考えます。

前述の通り、少しランダムな値を入れたベクトルを作り出して、それをリピート処理して平均値を求める方法では綺麗な結果を得られませんでした。
そこで、複数のベクトルをArray(配列)処理できる仕組みに変えてみたいと思います。
Gradientにつながる所は大幅に変更が必要ですが、その仕組みを一旦理解できたら、それを利用すれば良いだけです。

まずは、複数ベクトルの対応からで、つないだ結果を先に見せます。
一番左下はコンパウンドになっていてFill Interpolated Arrayって書かれています。
ここに今、というカウント数(レイ数になる)を入れていると、次のノードがPop From Attayなので、つArray(配列)が削除されてとなり、
その数のベクトルが各頂点位置から出ている、っていう図になっています。(ここのカウント数は最終的には10とか16とかなります)
これを、もう少し詳しく説明します。
ICE_AO_01



Fill Interpolated Array
最初のコンパウンドが何やってるの?ですね。
ICE_AO_02
仮に数字が設定されていて入って来たとします。
黄色の接続点、上(ValueA)がY=390、下(ValueB)がY=30、つまり、360度で1回転に30度足した数字にあえて入っています。
つまり、目的としてはY軸360度を、入って来た数字で割った回転値分のArray(配列)を作り出している代物です。
の次のMaxmumには3が入っていて、3より下の数値にならないようになってます。
Build Array from ConstantにはSizeValueにも最大値が入るので4で、4つの4のArray(配列)を作ります。
Get Array Sub IndicesはArray(配列)の順番数を列に入れるので、0,1,2,3となります。
下に行って、Subtract(減算)が入っていて最大値から1減った数3になります。
上も下もInteger to Scalarはスカラー数字に変換し、そのままの数字が移行して、
Divide by Scalar0,1,2,3で割った数のArray(配列)、0、0.333、0.667、1となります。
・・・つまり、どんな数字が入って来ても、その数字から引いた数で割った0~1の数値のArray(配列)を作り出します。
最後のLinear Interpolateはその割った数に相当する角度を出すので、Y=30、150、270、390というArray(配列)を作り出します。

この部分のコンパウンドが作製できたら、Fill Interpolated Arrayという名前でコンパウンドを書き出しておいてください。

で、次も説明してしまうと、30と390って同じベクトルのことなので、Pop From Attayで一番下の列が削除され、
Y=150、270、390というArray(配列)を出します。
ICE_AO_03
数字を増やしていっても、きれいに360度を入って来た数値から1引いた数で割った数分のベクトルを作り出す、となっています。



法線とインフィニットライト情報の追加
次は、Rotate Vectorノードを使い、各頂点の法線情報で求めたArray(配列)ベクトルを配置します。
ICE_AO_04
最後、インフィニットのライトですが、あ、と気が付きますね、1つ前のRotateVectorがZ=1でした。
ICE_AO_05
さーこれで、と思いきゃ、今度はHIT側の工夫が必要です。



Array(配列)を0~1値にする
Array(配列)を使った数値(マルチのデータ)が入って来るので、このようにしないとつながりません。
意味が解ってくれれば、後は使うだけなので・・・・。
ICE_AO_06
では、RaycastHIT出力から出る値の意味を説明します。

GetArraySizeは必ず0以上になるはずなので、=0に絶対ならないので、
Rescaleの前のIfはいつも下のif Falseの値が使われます。
GetArraySumはHitした総数を計算しているので、直前のIfがHitしたら1を足していって0~2の値を作り出します。
そのArrayをDived by scalarでいつも1少ない数値(ここでは2)で割るので、
かならず0~1の値を作り出します。
GradientにつながるRescaleはグラデーションの0が黒なので、
逆になるようにしていて、Clamp にチェックを入れて負数にならないようにしています。

ここも前のところと同じで、ある値に1少ない数値で割ると0~1の範囲の値を得られる
っていうのを使うのがミソのようです。
ICE_AO_07
前のポリコンのライトの時の設定と同じく、GetNeighborsを使うため、
一旦tmpデータとしてSetDataしてGet Averageで平均した値をGradientにつなぎます。



コンパウンド化
そしたら、ここまでのノードのつながりを、たった1つのコンパウンドにまとめてしまいます。
Rayの数、表面からの距離、減衰を考慮する距離、
インフィニットのライトを使用するかどうか(色も含む)グラデーションの色、ライトの選択
はコンパウンドをダブルクリックして表示される設定画面で見えるように設定します。
ICE_AO_08
その中身はこんな感じになっています。
インフィニットのライトを使うかどうかはIfによって選べるようになっています。
GradientGetLightにはReferenceで接続して変更できるようにしておきます。
ICE_AO_09
Ambient Occlusion with Lightという名前でカテコリColorとして書き出せば、皆で使えるコンパウンドになります。
ICE_AO_10
改良は、まだまだ出来そう・・・なんですね。
ICE_AO_11



グループ対応のコンパウンド化
さて、背景とかに使用するとしたら、複数オブジェクト対応も必要なんだろうなと考えました。
まずは、複数のポリゴンオブジェクトをマージした場合は簡単ですよね。
ICE_ray_
グループ対応のコンパウンドの作成に伴い、もう少しコンパウンドに機能を追加しました。
Geometryとして左側の紫色の接続点がつなぐと増えるタイプにしました。
Geometry を複数接続する時は、Group Geometryノードを使用します。
グループノードを追加したり複数接続したりすることができるようにです。
ICE_ray_31
それは、コンパウンドで左側の外部入力の名前から、
プロパティーにて[マルチ-新規のポートを既存のノードに接続]とすると
接続すると、どんどん増えるタイプになりまうす。Geometry接続点がどんどん増やすことが出来ます↓。
ICE_ray_32
明るい部分と暗い部分をRescale ノードのTargetのスタート値とエンド値で変更できるので
それがコンパウンドから操作出来るようにしました。
ICE_ray_33
Multiply Color by Scalarをself.Color へつながる途中に挟み、右側と接続しておけば、コンパウンドから操作できるようになります。



グループ対応のスクリプト
最後は、1つのコンパウンドにまとまったら、
スクリプトを使って、Group登録されているオブジェクトに
ICETreeノードの設定、コンパウンドノードの接続まで、一気にしてしまいます。

Groupに入れた複数のポリゴンオブジェクトに、まずは頂点カラーの設定だけはしておきます。
そして、Group >メンバの選択をしておいて、以下のスクリプトを走らせます。

sub BindAOICE2OBJ( oObj )
set compound = Dictionary.GetObject( oObj &".ICETree.Ambient_Occlusion_with_Light", false )
if typename(compound) = "Nothing" then
set iceOp = ApplyOp("ICETree", oObj, siNode, null, null, 0)
AddICECompoundNode "Ambient Occlusion with Light", iceOp
ConnectICENodes iceOp & ".port1", iceOp &".Ambient_Occlusion_with_Light.Execute"
end if
end sub

sub BindGroupToAOICE2OBJ( obj )
if typename( obj ) <> "Group" then
logmessage "You must select Group."
exit sub
end if

set oGrp = obj
for i=0 to oGrp.Members.Count - 1
set oObj = oGrp.Members.Item( i )
BindAOICE2OBJ oObj
next
end sub

sub BindSelectionAOICE2OBJ( )
for i=0 to Selection.Count - 1
set oObj = Selection.Item( i )
BindAOICE2OBJ oObj
next
end sub

' BindGroupToAOICE2OBJ( Selection(0) )
BindSelectionAOICE2OBJ

このスクリプトのダウンロード>>ApplyAO.txt(本当の拡張子はvbsで)

良く使うスクリプトなどはこのようにボタン形式にすることもできますね。↓
ICE_ray_34
すると、全部のオブジェクトに同じICEノードが接続された状態になります。
このスクリプトもあらかじめ接続するノードが同じである範囲を考慮してスクリプトを改良し、
一気に設定してしまえば作業がどんどん楽になることでしょう。

PS,
Groupを使った複数オブジェクトの状態での頂点色設定は、1つのオブジェクトにマージしたオブジェクトに設定した場合に比べて
結果がはっきりしない、というかあまいというか、差がある場合がありました。特に真平らな面など。
マージしたポリゴンに一旦頂点色を設定して、個別へはGATORなどを使って色を移し変える、
なんてことも考えられると思います。
ICE_ray_35

という訳で、次もICEですかね。
乞う、ご期待!!
戻る次へ

お気軽にお問い合わせください

Daikin CG News お申し込み

CGクリエイター向けのセミナー・イベントやキャンペーン、製品情報をメールマガジンでお届けします。(登録無料)

Twitter

ページの先頭へ