2017年5月11日木曜日

VrayMaterial(VrayMtl)の内部計算の検証

気が付けばほぼ一年ぶりの更新・・・。

昔に(2012年)個人で検証したVrayMaterial(VrayMtl)のShaderの計算に関するメモ。このままだとただ埋もれるだけだなと思い公開する事にしました。分かり辛く、大した情報でもないのであれですが、埋もれるくらいなら、どこかで誰かの役に立てば、、、と思い。

注意
エレメント出力して数値を確認して合っているかの確認を行ったものの、メーカーに問い合わせたりした訳ではないので、実際の所は不明。また、メモなので分かり辛いの申し訳ないです。5年ほど前の検証なので今はまた違ってるかも。

フレネルを考慮しない場合
reflection = x; // x = GUIから等から入力された値
refraction = (1.0 - reflection) * x; // x = GUIから等から入力された値
diffuse = 1.0 - refraction - reflection; // diffuse

反射 0.5、 屈折 0.5と入力した場合
reflection = 0.5 //反射率
refraction = (1.0 - 0.5) * 0.5 = 0.25 //屈折率
diffuse = 1.0 - 0.5 - 0.25 = 0.25 //拡散反射率
がレンダリングで用いられる値

各rawやfilterの値について










Vrayは、これらの計算をRGB各チャンネルで行っている。

エネルギー保存の計算による色の破綻
計算の性質上、以下のような設定をした際などに設定していない色が表れる事がある。
例:diffuseをグレー[0.5, 0.5, 0.5] reflectionを赤色[1.0, 0.0, 0.0]に設定した場合
入力していないシアンが表れる














先程のシェーダーの計算式に値を入力してみると、レンダリング時のdiffuseカラーが[0.0, 0.5, 0.5]とシアンの色になってしまっている。

このような現象を望まない場合は、以下の2つの方法などで対応が可能
①Energy preservation mode:を「Monochrome」とする
Energy preservation mode:Monochrome
このように設定する事で、割合の計算をしたのちにRGBの値を掛けるようになり
直感的な色味が反映される。一方で「エネルギー保存則」の精度が落ちるとも言われており、
メリットデミリットがあるようだ。ちなみに、Mentalrayのベーシックなシェーダーはこのタイプのようで
アーティスティックを配慮してそのようにしているのでしょうか?
(海外のどっかのメンタルレイのサイトでそのような解説を見たような気が)


















②VrayBlendMatrialを用いる
VrayBlendMaterialを用いてブレンド具合で調整。
この方法だと物理ベースが担保される。
Additive modeにチェックを入れると物理ベースが破綻する。
この辺りの判断はワークフローなどにもよるので自己責任で。
特別な理由がなければ、基本的にはチェックを入れずに作業をするのが良いとされている

2016年7月23日土曜日

VRayのRenderPassを用いたあれやこれや

気付けば今年最初の投稿となるようです。明けましておめでとうございます。
VRayのレンダーパスを用いたテクニックをいくつか。


①worldPositionパスを用いてある座標からの距離でマスクを作成。(右の上から2つめ)

②diffuseパス ÷ ライトセレクトで特定ライトの強度を取得(真ん中の上から2つ目)

③ ① x ②で特定ライトの特定エリアのマスクを作成し特定エリアのライトの強度を調整

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



Shadowパスはプラス(加算)する事で落ち影のない画像を取得出来る。

from(「影なし画像」引く「shadowパス」。引算。加算の逆なので元の影がある状態に戻る)で合成する際に、重ねる強さなどを変える事で影の濃さや色を調整出来る。

HDRI : Logoscope

2015年10月8日木曜日

ベクトル計算の基礎

ベクトルの計算が出来るようになると、CGのシェーダー、パーティクル制御、コンポジットの画像処理など、様々な分野での応用が期待出来る。しかし、ベクトルの計算はなんか難しい。そこで、次元を落として考えてみるのはどうだろうか?

一次元

一次元のものを「ベクトル」と言っていいのかどうか謎ですが・・・。(数学や物理の世界では1つの値を持つものは「スカラー」)と言われている。)

ある点、AとBがあったとする。Aは原点から3の距離であり、Bは5の距離である。

AからB、BからAなどのベクトルを得る場合は、ベクトルの引き算を行う。

B-A=AからBへのベクトル
5 - 3 = 2

2というベクトル(?)が得られた。Aから+2の所にBがあるという意味合いですかね。
実際にはベクトルは「向き」と「長さ」を持っているため、得られたベクトルは「プラス方向」に「長さ2」のベクトルとなるのだろうか。これから分かるように、ベクトルと言った場合、その値はAから見て・・・といったような「相対的なもの」である事に注意したい。

A - B  = BからAへのベクトル


3 - 5 = -2
「マイナス方向」に長さ「2」というベクトルが得られた。

一次元で考えるととても分かりやすいですね。

長さの計算
一次元だと値=長さなので、計算する意味まったくないけど、
2乗してから√するって覚えておく。

sqrt(x^2)
pow( x^2 , 0.5 ) 

意味ねー!!次元が増えると役立つ。

※ルートって1/2乗の計算と同じなんだ。つまり0.5乗でも結果同じ。詳しくは分からないが、プログラムの世界ではこちらの記述の方が計算速度が速いというような話を聞いたことがある。確かによく見かける気がするが。




単位ベクトルの取得(ノーマライズ)

これは、ベクトルの計算などをシンプルに行うため等に用いられるもので、ベクトルの長さを1にしたベクトルの事を指す。

自分の長さで割ってあげる



ベクトル / ベクトルの長さ

上の場合だと 2/2 = 1.0
下の例だと -2/2 = - 1.0 (分母が正の値ですが、「長さ」なので基本的に正の値になります!)

ベクトルの計算 足し算
この得られたベクトルを使ってAを動かす場合、
得られたベクトルをAに足してあげれば、AはBの位置に移動する。

A = A + V

動かす度合を変えたい時は、単位ベクトルが便利で
Bの方に1だけ動かすとか0.5だけ動かすとか

A = A + NV(長さ1のベクトル)
A = A + NV*0.5

二次元


二次元であっても同じである。

B - A = AからBへのベクトル

V(x , y) = B ( x, y) - A ( x , y) 
V(x , y) = B (4 , 1) - A(1 , 3) = V (3 , -2)

x、y それぞれで引き算行えばいいす。

そうすると、AからX +3 、Y -2の方向にBがいると言った意味のベクトルが得られる。が、次元が増えた事によりベクトルの長さがですね・・・・下の図注視するとなんか直角三角形が見えてこないですか?



そして、ベクトルの引き算をした際に、aとbの辺の長さが既に取得出来ていたりしませんか?


ピタゴラスの定理きた----!となる。

ということで V(length) = sqrt( 2^2 + 3^2) = 3.60555

もしくは pow ( (2^2+3^2) , 0.5) ・・・ (2^2+3^2)^0.5 という意味です。


出ました。ベクトルの長さ = 3.60555

今回の場合、ベクトルはV(3,-2)とYの値に負の値を持ってますが、計算式の性質上、累乗の計算があり、かならず求められる長さは正の値となるため、そのまま使って問題ないです。

Length = sqrt (3^2 + -2^2)
Length = sqrt (9 + 4)
Length = sqrt (13)
Length = 3.60555


で、単位ベクトル(長さ1のベクトル)はこの得られた長さを使って自分のx , yの値を割ってあげる事で求める事が出来る。


単位ベクトル NV = V (x / length , y / length)

NV = ( 3/3.60555 , -2/3.60555)
NV = ( 0.832051 , -0.5547)

出ました。単位ベクトル。

このように、次元が増えても、各次元で計算すればよい事が多く、2次元がクリアー出来れば、後は単純に次元が増えていくだけなので、3次元のベクトルの計算は基本的には2次元で使った式の次元を増やすだけとなる。

たとえばピタゴラスの定理もこうすればいいらしい。


ちなみに3dsMAXのTPのDistanceノードはこれらの計算を一発でやってくれる素晴らしい、「神、いわゆるゴッドなノード」だと、エフェクトスペシャリストの米岡さんに教えて頂いた。「Distance」という名前から「距離」をイメージしがちだが、本質的にはベクトルの長さと、単位ベクトルの取得になります。

また、代表的なベクトルの計算はMathノードで可能となっている。
計算式を一々打ち込んだり、ノード組まなくてもいいのでとてもありがたいですね。
神、いわばゴッド。

Maxscriptによるベクトルの操作はこちらを参照 Maxscript その9 「座標とベクトル point3値」


以前、「Nukeで3DCGのノーマルマップを使ったLambert Shaderの表現」の言ったような記事を書いたのですが、ノーマル(法線情報)も面の向きのベクトルなので、これも初歩的なベクトルの計算だけで出来る事になります。基本的なベクトルの計算を知るだけでも非常に沢山の表現が手に入るのではないかと思います。

距離を色に変えてみたりとか。

重力でものを落としたいとか
これも、ニュートンの法則が・・・とか考えずに、毎フレーム、自分のベクトルに重力のベクトルを足すだけでよいとか(重力による加速度といった考え)Maxだと一々スペースワープで重力を作る必要はないわけで・・・。

CGの「Velocity」などもベクトルである。「RealSmartが・・・」などの単語をよく耳にするが、その根本はベクトルであるため、この辺りを理解する事でVelocityチャンネルなどを適切に扱えるようになってくるのではないかと思う。

「ベクトルの内積 / Dot Product」、「ベクトルの外積 / Cross Product」 などが加わるとパーティクル制御などの幅がめちゃくちゃ広がると思います。

ベクトルの計算でよく分からなくなったらx軸だけとか1次元に落として考えてみる。

取り急ぎ失礼します。

2015年5月31日日曜日

カラーマネジメントについて思う事

左:適切に見れていない 右:適切に見れている

素材を確認する環境や設定によって、同じデータでも見え方が大きく異なる事例をいくつも確認出来ている。撮影現場の見え方と作業現場の見え方の違いetc。この事に気づいていない人も多いのではないだろうか?

カラーマネジメントはVFX制作環境の根本に関わる事だと僕は考えている。この部分が土台としてしっかりあって、はじめて見え方や方法論など議論が可能になるのではないのかな?と。

現在、日本の環境の多くは左の環境である事が多いように思う。特にカラーマネジメントをしていない環境。その環境で見え方や方法論を語る事に意味があるのか?「こうすると光り方が綺麗に見えるよ!」などなど。

僕は、まだそれら議論が可能な段階にすらなっていないのでは?と感じている。

カラーマネジメントされていない環境でチャートを撮影する事に何の意味があるのだろうか?

リニアワークフローやモニターキャリブレーション以前の問題だと思う。それは細部の話であって、まずは、全体のカラーマネジメントを行って素材の管理や制作環境が整って初めてVFX制作が行えるのではないのかな?と。今、語られている多くの方法論は「カラーマネジメントされていない環境」「出力デバイスや画像の色に依存した方法論」なのではないかな・・・・と。

カラーマネジメントがあって、はじめて骨太で確かな技術を張り巡らせれるのではないかな?と。ヴィジュアルエフェクト。名前の通り、見た目、非常に重要だと思うんだ。カメラの色や、異なる規格、例えばBT.2020などが来ても揺るがない環境。

そのような環境になるといいな。など、最近考えている次第。

僕自身がまだ、プロジェクト等で色々思考錯誤している段階でズバリな記事を書けない事があれなんだけど・・・。今、個人的に一番ホットなのは、上の画像のような高輝度の表現。エフェクトなどの見え方にとても直結している部分。エフェクトのルックデブ環境について。特にRGBの原色に近い部分に顕著に表れる。

「そもそも、そのレンダリング画像、適切に見れてないんじゃ?」って結構確信に近い思いがある。適切に見る事が出来て、初めてパラメーターとかの話が出来るんじゃないか?

(VFX制作のカラーマネジメントは難しいですよね。「正しい」だけではなく「好ましさ」も考慮しなくてならない。間違っていても、それが「好ましい」と判断されれば、それに柔軟に対応していかなければいけない。それはとても難しい事だと思う。)

これはカラーマネジメントに関わらず、全ての事に言える事だと思う。確かな基礎があって、初めて応用を張り巡らす事が出来るといいますか。僕の中にどこか色んな基礎的な事がすっぽり抜けている感覚が沢山あって・・・。

僕の理想とする環境は、「それは間違っている!」「正しくない!」とかではなくて、必要な環境を構築し、アーティストにはそこで自由に作品を作ってもらって「こうするといいよ!」とか「こうしてみた」みたいな議論がされていったらいいなぁ・・・。となんとなく思い描きつつ・・・。

2015年2月3日火曜日

Maxscript その18 「for式 ループ処理その1(+whileループ)」

ループ処理の基本となるforループ、for式。「for文」などで検索すると、説明しているサイトが沢山出てくると思う。
---------
■for式1

【構文】
for i = 0 to 10 do print i

for <変数> = <初期値> to <終了値> [by <変化値>] do <式>

使える値はinteger値、float値、time値

byで変化値を変える。 byを記述しない場合の変化値は1。

for i = 0 to 10 by 2 do print i
for i = 10 to 0 by -2 do print i

複数の処理を繰り返し実行したい場合は do の後の<式>にブロック式を用いる

for i = 1 to 10 do (
    obj = Sphere()
    obj.radius = 5
    obj.pos = [i*10,0,0]
    c = (255/10)*i
    obj.wirecolor = [c,c,c]
)

上のスクリプトは多分下の書き方でも同じだと思う・・・。
for i = 1 to 10 do (
    obj = Sphere radius:5 pos:[i*10,0,0] wirecolor:(random [0,0,0][255,255,255])
    --色の記述ちょっと変えた
)

【for式で使われる変数のスコープ】

for式で定義される変数(下の場合「i」)は、for式の中だけで有効なローカル変数となるようだ。

(
    for i = 1 to 10 do (
        print i
    )
    print i
)

この場合、最後のprint iの実行結果は「undfined」 となる

【戻り値】
for式も式なので処理が終わると「OK」という答えを返す。
a = for i = 1 to 10 do ()



OK値(値>特殊データ値)

【多重ループ

どのような順番で処理がされるているか注意して確認してみる。

num = 3
for x = 1 to num do (
  for y = 1 to num do (
  for z = 1 to num do (
  format "x = %\ty = %\tz = %\n" x y z
  )
  )
)





























---------
■for式2
for <変数名> = <初期値> to <終了値> [by <変化値>] collect <変数名>

x = for i = 1 to 10 collect i
print x

この場合のfor式の戻り値は「OK」ではなく、iに代入された全ての値の配列となる。

x = for i = 1 to 10 (
    collect i*2
)
print x
---------
tips:強制ループ終了 exit
一般的にループを途中で中断させるための命令が用意されている。maxscriptにもそのためのexitという関数があるが・・・。


どういう事だ?と調べようとしたらリファレンスが「ループ終了のページ→forループのページ→ループ終了のページ」とループしていた!流石ループの説明だ。exitしたい。

exit使うとパフォーマンスが落ちるのでwhileかwhereを使ってね。という事らしい。


【例】 -- 0~100からランダムで値を取得し、50より大きい場合はforループを終了させる。
(※説明用にfor式を使っているが、この場合は下で説明するwhileループの方が適しているかもしれない)

x = 0
for i = 1 to 100 while x <50 do (
    x = random 0 100
    print x
)

while<式>の答えは(True/False)となっており、答えがFalseとなると処理が中断される

意味はないが、処理がされないfor式は以下のようになる。

for i = 1 to 100 while false do ()
---------
tips:for式とfloat値

コンピューターでのfloat値の扱いには色々あるらしい。以下は、maxの側の問題なのかコンピュータで演算する際の問題なのかは不明だが、for式の値にfloatの値を使うと以下のような事が起きる場合がある。(検索「浮動小数点 丸め誤差」)

for i = 0.0 to 10.0 by 0.1 do print i


for式に使う値は出来るだけintegerを使うようにした方が良いかもしれない。

for i = 0 to 100 do (
    print (i/10.0)
)

---------
tips:whileループとDoループ

本来、Tipsではなく、別で扱った方がいいくらい代表的なループ処理の一つ。無限ループとも言われたりしますか?Maxscriptでは2つのループが準備されている。条件の記述を間違うとループ処理が終わらず、永遠とループ処理がされてしまう。「ESC」キーを長押しすることで強制的にループ処理を止める事が出来るようだ。たまに、Maxの強制終了により強制的にループ処理を抜ける事もある。ワイルドだぜ。

do <式> while <条件>
while <条件> do <式>

例:Xに10を代入し、Xを1つづ引いていく。Xが1より小さくなるとループ処理が終わる。
x = 10
while x>0 do print (x-=1)

doとwhileの違いはdoはwhileの前に処理があるため、最低1回は処理が行われる

x =10
do (print (x-=1)) while (x>10)

x = 10
while (x>10) do (print (x-=1))
---------
ここまで来るとスクリプトで出来ることがとても増えてくる。特に大量の情報を扱うCG制作において、このループ処理がもたらす効果は本当に大きい。実際どのように使うか等の具体例は後ほどまとめて掲載したいと思う。

2014年12月31日水曜日

Maxscript その17 「3d変換行列(3D Transform Matrix) matrix3」

ここでは3D変換行列(3d Transform Matrix)の基本的な操作について少しだけ、ザックリと触れたいと思う。

3D変換マトリックスは何かというと、3Dで扱うオブジェクトの変換=移動・回転・スケールの情報らしい。それが、どのようなものなのか理解しようとすると、それはそれは(僕にとって非常に)高度な数学の知識を必要とし、理解するのが難しい。


ここでは3D変換マトリックスをシステムと捉え、それを使った基本的なトランスフォームの操作を見ていってみたいと思う。誰か知らないけど、3Dの変換を管理する素晴らしいシステムを作ってくれた。その基本的な使い方と概念。


maxscriptでは <ノード>.transformとすることで3D変換マトリックスの数値を取得/設定できるようだ。





取得した値はmatrix3という値になっており、ワールド座標基準の4つのベクトル情報を持つ。



matrix3 X軸 Y軸 Z軸 位置(オフセット)



となっている。何も操作していない状態だと以下のようになる。






X軸方向のベクトルの長さを2倍にすると次のようになる。




x,y,z,positionをコントロールするポイントを作って、各位置情報をteapotのトランスフォームに値を渡してあげるような仕組みを作ってみた。変化が分かりやすい。




4つのベクトルを使って、移動回転スケール(スキュー)が表現出来るようだ。凄いっすね・・・。

数値の説明は終わり。水色の数値から直感的に色々イメージするのは非常に難しいため、基本的にはmatrix3の数値は見ない。考えない。



matrix3値を操作するための様々な関数が用意されているが、ここでは割愛。詳しく知りたい人はscriptリファレンスの「matrix3」の項目を見て下さい。



<3D変換行列の演算 変換の継承>

リファレンスによると演算子は



演算子

+

-

*
as


とあるが、ここでは乗算(掛け算)のみ見てみようと思う。3D変換行列の基本的な演算は乗算になるようだ。


原点にあるAの変換(移動回転スケールがデフォルト値)をBに持って行く計算



Bの変換行列を取得してAの変換行列に掛け算してあげる事で、Aの変換がBの変換と等しくなる。

何も変換していないマトリックスは

matrix3 1

(matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0])

として取得する事も可能なようだ。

$.transform = matrix3 1

で、変換をリセット出来ると思う。

トランスフォームの継承/親子関係(ペアレント)
3Dのオブジェクトのリンク(ペアレント=親子関係)の計算は基本的にはこの原理になっているようだ。

ザックリな感じであれですが、AとBのトランスフォームを掛け算した値をCに渡すと、CのトランスフォームはAとBのトランスフォームを継承した値になるようだ。いわゆる親子関係(リンク、ペアレント、コンストレイント)


だたし、掛け算する順番を変えると結果が異なるので注意が必要なようだ。

Bのトランスフォーム * Aのトランスフォーム → BがAの子の時のトランスフォーム
Aのトランスフォーム * Bのトランスフォーム → AがBの子の時のトランスフォーム

複数のペアレント構造を計算する場合も、同じ計算手順で。


Bから原点に戻す変換行列の求め方

考え方が非常に良く似ているので、ベクトルの操作の基本を見てみる。
教えて頂いたMark代表取締役 犬童さんに感謝

BからAのベクトルを求めたい時、AのベクトルからBのベクトルを引き算する事で、BからAのベクトルが取得出来る。

V = A.pos - B.posのような感じで。

B.pos = B.pos + V

とすると、当たり前だけど、ティーポットは原点に移動する。

ベクトルの計算(位置)だけだとシンプルだが、回転、スケールも考慮する場合はどうだろうか?そのような際に3D変換マトリックスを使った演算がとても強力だと思う。

原点からBの変換行列は、B.transform の値そのもの


Bから原点の変換行列(逆行列)はinverseを使って求める事が出来るようだ。


そして、自分の変換行列に、自分の逆行列を掛け算してあげると・・・
ティーポットが原点に移動し、回転、スケールもリセットされる。



inverseは反転。つまり「-」と考える事が出来るので

原点の変換行列 = 元の行列 - 元の行列

と考えると覚えやすいか。


相対的な変換行列の取得

AからBへのベクトル = B.pos - A.pos
AからBへの変換行列 = B.transform * (inverse A.transform)


BからAへのベクトル = A.pos - B.pos
BからAへの変換行列 = A.transform * (inverse B.transform)

---
TM = A.transform * (inverse B.transform)
B.transform = TM * B.transform

とすると、Bの変換がAの変換と同じになるのが確認出来るかと思う。

【実用例】動いているオブジェクト(Teapot001)に追従(リンク)するカメラ(Camera001)をオブジェクトを止めて同じように見えるようにする

新しく動いていないTeapot002と動いていないCamera002を作成して
動いていないカメラ、Camera002のTransformにScriptコントローラーを適用して以下のように記述。

トラッキングした情報をカメラに適用するか、オブジェクトに適用するかなども同じ仕組みだと思われる。オブジェクトを止めた状態でカメラの動きをつけて動いているカメラにその変換情報を渡してあげるとか。


<解説>
1.$Camera001.transform * (inverse $Teapot001.transform)とし、Teapot001からCamera001への相対的な変換マトリックスを取得

この変換をワールド座標で表すと以下の感じだと思われるので、ワールド原点を基準にしたい場合はこれだけで終わり(なはず・・・)。

2. 1で得た変換マトリックスをTeapot002の所に持ってくる。(Teapot002の子にする)
この計算をした場合、Teapot002の変換が考慮されているので、Teapot002を動かしたり、アニメーションを付けたとしても、Cameraとの相対関係は維持されたままになる(はず)。


カメラのトランスフォームをワールドX軸で対象にする
$Camera002.transform = $Camera001.transform * (scaleMatrix [-1,1,1])
とするとシンプルなのだが、この場合スケールの値がマイナスとなり、カメラやライトの場合は反転してしまって、上手くいかなかったので、xformMat関数を使い変換を変換した。オブジェクトなどでも、レンダリング時などエラーが起きそうですし。変換を変換。変換を・・・。

tiraokaさんのブログでカメラの変換トランスフォームを使って、素材の座標を変換する方法を説明してますが、考え方は同じになりますか。勉強になります。

ColorMatrix