2001 8月25日に、X Fellows conference 2001 でお話をさせていただきました。
今回は、そのお話です。
パワーポイントファイルとプログラムのファイルを公開します。
使えるものは、使ってやってください。
シェーダーオプションは、
Lambert | : | ランバート並行光源 |
Phone | : | Phone スペキュラ-付き |
Kajiya | : | Kajiya の論文に載っていたらしい方法の光源計算 |
101 | : | 101 で使われた、Goldman による光源計算の簡易版 |
ms | : | Real-time fur over arbitrary surfaces で使われている光源 |
公開しなかったネタ:GeForce3 なら、mを押すと、モノトーンに切り替わるぞ! 前に紹介した pixel shader プログラムです。
CG で、毛を生やす方法には、
I | 髪の毛を柱状の物体として モデリング |
II | Particle Systemによるアプローチ (粒子を線で繋ぐ) |
III | 髪の毛の固まりをテクスチャマップ, バンプマップによって表現 |
IV | 異方性反射モデルを応用 |
が、考えられます。個人的な意見としては、
I | 重たすぎ |
II | 衝突判定がメンドクさ |
III | 本命かな? |
IV | IIIの方法と併用して、さらに質感アップできるんじゃない? |
て、感じです。
『III が現実的な解なのでしょうけれど、さらに質感を加えるために、
今回紹介する VI の方法を使うといいんじゃない』というのが基本的なスタンスです。
異方性反射とは、方向によって反射の仕方が変わる現象のことで、大雑把に言えば、素直でない光の反射です。
ちなみに、ネタ本は
I | Kajiya, James T. and Timothy L. Kay, “Rendering Fur with Three Dimensional Textures.” (SIGGRAPH 89 conference proceedings) |
II | Dan, B. Goldman, “Fake Fur Rendering.” (SIGGRAPH 97 conference proceedings) |
です。最初、自分で考えてプログラムを組んだのですが、後で調べると既に発表されていたので、
それらの考察をもとにプログラムを組みなおしました。
実は、Kajiya 等による論文は読んでいません。解説してあるホームページがあったので、そこからパクリました。
Goldman の論文は、ディズニーの映画の 101 に使われたものです。
101 の最後のシーンに犬が大勢座っているシーンがあるのですが、そのシーンでは、真中の2匹の犬を除いて、
今から紹介する方法(ここでは、手を抜いて、その一部しか紹介しませんが)を使っているそうです。
なお、大急ぎで呼んだので、間違っている可能性大です。
間違っていたら、教えてください。
実際のモデル化は、毛を表面に生えた円柱として捕らえ、その円柱に関する光源計算を行います。
今回は、簡略化のため、円柱は法線方向に伸びているとします。
粗視化のレベルとしては、レンダリングされたモデルのレベルでは、毛は見えないけれど、
光源計算を考慮する微小表面では、毛の先端が影響しないほどほぼ無限に伸びています。
ちなみに、『微視的に見る』のことろで、アップになっている髪の毛は、私の毛です。
これから後の比較対照として、ランバート並行光源と、視線と反射光のなす角の4乗に比例するフォンスペキュラ-光の画像を載せておきます。
まず、Kajiya 等の方法です。
下の画像が作れます。
特徴としては、光沢(天使の輪やねぇ)がでます。
さらに、全体的に光が回り込み、やわらかい光が当った感じになります。
光の効果としては、2つの効果を考慮します。diffuse 項と、スペキュラ-項です。
それらの光の成分(及びLabmert 平行光源)の合成として、光の強さが求まります。
光の成分の割合は、定数レジスタに入れて掛け合わせることで実現します。
今回は、円柱に当る光は白ですが、Labmert 平行光源の色は少しオレンジがけています(気分的なものです)。
diffuse 項
diffuse 項は、平面に対する Lambert diffuse を円柱に対して適応したもので、円柱に光が当って、明るくなった効果を表現します。
一番強く照らされるれるのは、側面から光が当った時です。
また、毛の方向に水平に光が当った時には、毛の表面には、光が当りません。
従って、毛の向きと光の方向の sin に比例した強さで光が強くなります。
Kajiya 等の論文では、円柱上の微小面に対する Lambert の法則を円筒面で積分することにより、この結果を導き出しています。
スペキュラ-項
スペキュラ-項は、反射成分で、円柱に関する Phone の反射(見た目で調整してcos^8γ)を用います。
Kajiya 等の方法は、円柱の長さ方向に対し角度θで入射した光は、入射角度と同じ角度θをもつ円錐の上を正反射すると仮定します。
光の反射成分は、反射する光がなす円錐と、反射ベクトルのなす角γに対して、Phone の反射を適応することによって導出します。
γは、視線ベクトルと円柱の長さ方向のなす角ψ及び、反射ベクトルと円柱の長さ方向のなす角θの差と等しいので、
cosγは、三角関数の合成の公式、
cosγ = cos(ψ-θ) = cosψ cosθ - sinψ sinθ
を使って求めることが出来ます。
では、以上の効果を計算した頂点シェーダープログラムです。
今回は、短くするためにマクロ命令を多用しています。
sin(a,b) は、2つのベクトル a, b のなす角度の sin (すなわち、外積 a×b の大きさ)です。
計算は、内積や外積を求めて、その組み合わせをすることになります。
vs.1.0 ; c0-3 -- world + ビュー + 透視変換行列 ; c4-7 -- world 行列 ; c8-11 -- world の逆転置行列 ; c12 -- {0.0, 0.5, 1.0, -1.0} ; c13 -- ライトのベクトル ; c14 -- カメラの位置 ; c15 -- メッシュの色 ; c16 -- diffuse ライトの色 ; c18 -- ファー diffuse の色 ; c19 -- ファー specular の色 ; ; v0 頂点の座標値 ; v3 法線ベクトル ; v7 テクスチャ座標0 m4x4 oPos, v0, c0 ; 座標変換 mov oT0, v7 ; メッシュのテクスチャー m3x3 r0, v3, c8 ;法線の変換 ;法線の正規化 dp3 r0.w, r0, r0 rsq r0.w, r0.w mul r0, r0, r0.w ; r0 = 法線 m4x3 r1, v0, c4 ; 頂点のワールド座標系での位置を調べる add r1, c14, -r1 ; 視点から頂点の方向e ; e の正規化 dp3 r1.w, r1, r1 rsq r1.w, r1.w mul r1, r1, r1.w ; r1 = e = 視点 - 頂点 ; Kajiya ファー mul r4, r0.zxyw, c13.yzxw ; r4 = n x l mad r4, r0.yzxw, c13.zxyw, -r4 dp3 r4.w, r4, r4 rsq r4.w, r4.w rcp r4.w, r4.w ; r4.w = |n x l| = sin(n,l) mul r5, r0.zxyw, r1.yzxw ; r5 = n x e mad r5, r0.yzxw, r1.zxyw, -r5 dp3 r5.w, r5, r5 rsq r5.w, r5.w rcp r5.w, r5.w ; r5.w = |n x e| = sin(n,e) dp3 r2.w, r0, c13 ; n dot l dp3 r2.y, r0, r1 ; n dot e mul r2.z, r2.w, -r2.y ; r2.z = ( l・n)(-n・e) mad r2.z, -r4.w, r5.w, r2.z ; r2.z = ( l・n)(-n・e) - sin(n,l)sin(n,e) mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^2 mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^4 mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^8 mul r6, c18, r4.w ; ファー diffuse mad r6, c19, r2.z, r6 ; ファー specular max r2.w, r2.w, c12.x ; 負の値をカット mul r3, c16, r2.w ; ランバート diffuse add r6, r3, r6 ; ファー + Lambert mul oD0, c15, r6 ; メッシュの色を反映
で、結果としては、先ほど出たクマのような光沢が、スペキュラ-の効果として現れます。
また、diffuse の結果として、法線が裏向いた側にも光が回り込みます。
これは、下の画像のように、裏から光を当ててやればよく分かって、裏からの光で、周りがぼんやりと光ります。
(いちばん外側の白い輪郭は、スペキュラ-の影響ですね)。
真中も少し光っていますが、これは良く分かりません(多分内積の負の値のクリッピングに関するバグ)。
さて、次に101で使われた、Goldman の手法です。
こちらは、かなり現象論的な手法を追加します。
見た目としては、陰が付き、全体的に落ち着いた感じに仕上がります。
では、一つ目の効果は、反射光に対する修正です。
Kajiya らの specular 反射は、円柱の長さ方向に対し角度θで入射した光は、入射角度と同じ角度θをもつ円錐の上を正反射すると仮定しました。
個人的にもこれは納得できません。
円柱の動径方向に関して、光の入射方向の反対向きに反射する光が多いはずです。
そこで、反射率を『ρ反射』、透過率を『ρ透過』として、その間は光と視線の角度の cos で補間します。
ここで、気にしたいことは、円柱の長さ方向に関して垂直な平面上での角度を計算するということです。
円柱の長さ方向(法線方向)に垂直なわけですから、視線と、光の方向のそれぞれに関する法線との外積を計算します。
外積の結果は、元にした視線や光の方向とも垂直なのですが、円柱の動径方向に関して同じ(90度)だけ回るので、
(n×e,n×l) と (e,l) の円柱の動径方向に関する角度は等しくなります。
ということで、以上の説明は、外積計算や内積計算だけで計算できるので、頂点シェーダーで計算できることが分かると思います。
もう一つは、強引な陰影です。滑らかな階段関数を導入して、法線が光と適当に反対を向いているときは、
だんだん暗くしていきます。
今回は、さらに計算の簡略化のため、線形近似しました。
また、環境(ambient)項に対応する完全に裏を向いているときの光の強さも0にしました。
これも計算の簡略化のためです(黒いほうがひきしまって、かっこよかったってのもあります)。
これは、下にある ppt の切抜きに関して、ρ=1の状態に対応します。
ここらへんの計算をきちんと行いたい場合は、減衰の式を濃度で表現したテクスチャーを使って、
光の向きと法線との内積の値をUV値にした環境マッピングをするといいと思います。
以上の説明をプログラムすると、次のようになります。
vs.1.0 ; c0-3 -- world + ビュー + 透視変換行列 ; c4-7 -- world 行列 ; c8-11 -- world の逆転置行列 ; c12 -- {0.0, 0.5, 1.0, -1.0} ; c13 -- ライトのベクトル ; c14 -- カメラの位置 ; c15 -- メッシュの色 ; c16 -- diffuse ライトの色 ; c18 -- ファー diffuse の色 ; c19 -- ファー specular の色 ; c20 -- ファー透過パラメータ ; ; v0 頂点の座標値 ; v3 法線ベクトル ; v7 テクスチャ座標0 m4x4 oPos, v0, c0 ; 座標変換 mov oT0, v7 ; メッシュのテクスチャー ;法線の変換 m3x3 r0, v3, c8 ;法線の正規化 dp3 r0.w, r0, r0 rsq r0.w, r0.w mul r0, r0, r0.w ; r0 = 法線 ; 頂点のワールド座標系での位置を調べる m4x3 r1, v0, c4 ; 視点から頂点の方向e add r1, c14, -r1 ; e の正規化 dp3 r1.w, r1, r1 rsq r1.w, r1.w mul r1, r1, r1.w ; r1 = e = 視点 - 頂点 ; Kajiya ファー ; r4 = n x l mul r4, r0.zxyw, c13.yzxw mad r4, r0.yzxw, c13.zxyw, -r4 dp3 r4.w, r4, r4 ; r4.w, = (n x l)^2 rsq r7.x, r4.w ; r4.w, = 1/|n x l| rcp r4.w, r7.x ; r4.w, = |n x l| = sin(n,l) ; r5 = n x e mul r5, r0.zxyw, r1.yzxw mad r5, r0.yzxw, r1.zxyw, -r5 dp3 r5.w, r5, r5 ; r5.w, = (n x e)^2 rsq r7.y, r5.w ; r5.w, = 1/|n x e| rcp r5.w, r7.y ; r5.w, = |n x e| = sin(n,e) dp3 r2.w, r0, c13 ; n dot l dp3 r2.y, r0, r1 ; n dot e mul r2.z, r2.w, -r2.y ; r2.z = (l・n)(-n・e) mad r2.z, -r4.w, r5.w, r2.z ; r2.z = (l・n)(-n・e) - sin(n,l)sin(n,e) mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^2 mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^4 mul r2.z, r2.z, r2.z ; r2.z = ((l・n)(-n・e) - sin(n,l)sin(n,e))^8 mul r6, c18, r4.w ; ファー diffuse mad r6, c19, r2.z, r6 ; ファー specular ; 101 Fur ; r7.w = cos(n x l, n x e) = (n x l)(n x e)/(|n x l||n x e|) = κ dp3 r7.w, r4, r5 mul r7.w, r7.w, r7.x mul r7.w, r7.w, r7.y ; r7.w = f_dir = (1+c20.w κ)/2 (directional attenuation factor) mul r7.w, r7.w, c20.w add r7.w, r7.w, c12.z mul r7.w, r7.w, c12.y ; f_suf = cramp(2.0f*(l・n)+1) rcp r7.z, c12.y mad r7.z, r7.z, r2.w, c12.z; r7.w = 2.0f*(l・n)+1 max r7.z, r7.z, c12.x ; 負の値をカット min r7.z, r7.z, c12.z ; 1.0以上の値をカット ; ファー = f_suf * f_dir mul r7.w, r7.w, r7.z ; ファー = f_suf * f_dir * (ファーdiffuse + ファーspecular) mul r6, r6, r7.w mul r3, c16, r2.w ; ランバート diffuse add r6, r3, r6 ; ファー + Lambert mul oD0, c15, r6 ; メッシュの色を反映
いかがでしょうか。
NM@BCさんのページで訳されていた、マイクロソフト研究所の Jerome Lengyel さん等の
Real-time fur over arbitrary surfaces. Symposium on Interactive 3D Graphics 2001, pages 227-232 (update: 2001.4.11)
のライティングを加えてみました。
この光源は、計算を軽くするために、スペキュラ-項を、half-vector (h=(l+e)/2:光源と視線ベクトルの中間のベクトル)と法線の
sin に比例した値にしています。
僕は、勉強不足なので、half vector の物理的意味がまだ良くわかっていません。
もちっと勉強したいと思います。