擬似ファーライティングの頂点シェーダーによる実装


~XFC2001での講演の内容です~




■はじめに

2001 8月25日に、X Fellows conference 2001 でお話をさせていただきました。
今回は、そのお話です。

パワーポイントファイルとプログラムのファイルを公開します。
使えるものは、使ってやってください。

シェーダーオプションは、
Lambertランバート並行光源
PhonePhone スペキュラ-付き
KajiyaKajiya の論文に載っていたらしい方法の光源計算
101101 で使われた、Goldman による光源計算の簡易版
msReal-time fur over arbitrary surfaces で使われている光源
です。

公開しなかったネタ:GeForce3 なら、mを押すと、モノトーンに切り替わるぞ! 前に紹介した pixel shader プログラムです。

■アプローチ

CG で、毛を生やす方法には、

I髪の毛を柱状の物体として モデリング
IIParticle Systemによるアプローチ (粒子を線で繋ぐ)
III髪の毛の固まりをテクスチャマップ, バンプマップによって表現
IV異方性反射モデルを応用

が、考えられます。個人的な意見としては、

I重たすぎ
II衝突判定がメンドクさ
III本命かな?
IVIIIの方法と併用して、さらに質感アップできるんじゃない?

て、感じです。
『III が現実的な解なのでしょうけれど、さらに質感を加えるために、 今回紹介する VI の方法を使うといいんじゃない』というのが基本的なスタンスです。
異方性反射とは、方向によって反射の仕方が変わる現象のことで、大雑把に言えば、素直でない光の反射です。
ちなみに、ネタ本は

IKajiya, James T. and Timothy L. Kay, “Rendering Fur with Three Dimensional Textures.” (SIGGRAPH 89 conference proceedings)
IIDan, B. Goldman, “Fake Fur Rendering.” (SIGGRAPH 97 conference proceedings)

です。最初、自分で考えてプログラムを組んだのですが、後で調べると既に発表されていたので、 それらの考察をもとにプログラムを組みなおしました。
実は、Kajiya 等による論文は読んでいません。解説してあるホームページがあったので、そこからパクリました。
Goldman の論文は、ディズニーの映画の 101 に使われたものです。
101 の最後のシーンに犬が大勢座っているシーンがあるのですが、そのシーンでは、真中の2匹の犬を除いて、 今から紹介する方法(ここでは、手を抜いて、その一部しか紹介しませんが)を使っているそうです。
なお、大急ぎで呼んだので、間違っている可能性です。
間違っていたら、教えてください。

実際のモデル化は、毛を表面に生えた円柱として捕らえ、その円柱に関する光源計算を行います。
今回は、簡略化のため、円柱は法線方向に伸びているとします。
粗視化のレベルとしては、レンダリングされたモデルのレベルでは、毛は見えないけれど、 光源計算を考慮する微小表面では、毛の先端が影響しないほどほぼ無限に伸びています。


ちなみに、『微視的に見る』のことろで、アップになっている髪の毛は、私の毛です。

これから後の比較対照として、ランバート並行光源と、視線と反射光のなす角の4乗に比例するフォンスペキュラ-光の画像を載せておきます。

Lambert diffuse Phone specular

■Kajiya 等の提唱した方法

まず、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 の結果として、法線が裏向いた側にも光が回り込みます。 これは、下の画像のように、裏から光を当ててやればよく分かって、裏からの光で、周りがぼんやりと光ります。 (いちばん外側の白い輪郭は、スペキュラ-の影響ですね)。


真中も少し光っていますが、これは良く分かりません(多分内積の負の値のクリッピングに関するバグ)。

■Goldman の手法

さて、次に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 の物理的意味がまだ良くわかっていません。
もちっと勉強したいと思います。





もどる

imagire@gmail.com