DirectX 8.1:トーンシェーダー


~ツゥーンじゃないよ、スクリーントーンだよ~




■はじめに

CGワールドのVol.46に、 ガラクタスタジオさんのラクガキ王国の記事が載っていました。 その中にボツになった記事として、スクリーントーンを張った、トーンシェーダーの記事が載っていましたので、 挑戦してみました。
今回は、ツゥーンシェーダーを施すのと同時に、暗い部分にてんてんのスクリーントーンを張りました。


今回のソースは、次のものです(DirectX8.1用です)。

まぁ、いつものように適当にファイルが入っています。
前回と頂点シェーダプログラムと、draw.cpp が変化しています。

ps.shピクセルシェーダープログラム。今回、ここが複合技。
vs.vsh頂点シェーダープログラム。
draw.cppメインの描画部分。
draw.h描画の各関数の定義。特に意味無いので出番無し。
main.h基本的な定数など。今回も出番無し。
main.cpp描画に関係しないシステム的な部分。変更が無いので、出番無し。
tone.bmp (スクリーントーンテクスチャー) toon.bmp (ツゥーンテクスチャー)

あと、いつもの様に、モデルと、実行ファイル及び、プロジェクトファイルが入っています。

■やってること

トーン用のテクスチャーで特別なことは、スクリーン座標を使うことです。
透視変換した後の座標値を(いい感じに見えるように)適当にスケールをかけて、テクスチャー座標に使います。

また、ツゥーンシェーダーした光の強さを計算して、一定の(0.3)以下の光の強さ以下のときにトーンを張るようにピクセルシェーダープログラムを組んでいます。

■ソース解説

今回は、ツゥーンシェーダーの時と比較して、
・2つめのテクスチャーをトーン用のテクスチャーにしている
・シェーダープログラムを書き換えた
という違いしかないので、シェーダープログラムだけの解説に絞ります。

頂点シェーダープログラムは、次のようになります。

0001: vs.1.0
0002: 
0003: ; c0-3   -- world + ビュー + 透視変換行列
0006: ; c12    -- {0.0, 0.5, 1.0, 2.5}
0007: ; c13    -- ライトのベクトル
0009: ; c15    -- メッシュの色
0010: ;
0011: ; v0    頂点の座標値
0012: ; v3    法線ベクトル
0015: 
0016: ;座標変換
0017: dp4 oPos.x, v0, c0
0018: dp4 oPos.y, v0, c1
0019: dp4 oPos.z, v0, c2
0020: dp4 oPos.w, v0, c3
0021: 
0022: ; ((l,n)+1)/2 (平行光源のライティングを0~1に範囲変更)
0023: dp3 r0.x,   v3,   c13
0024: add r0.x,   r0,   c12.z
0025: mul oT0.x,  r0.x, c12.y
0026: 
0027: ; スクリーン座標をテクスチャー座標に設定
0028: dp4 r0.x,   v0, c0
0029: dp4 r0.y,   v0, c1
0030: dp4 r0.w,   v0, c3
0031: rcp r0.w,   r0.w
0032: mul r0.xy,  r0, r0.w
0033: mul oT1.xy, r0, c12.ww
0034: 
0035: ; メッシュの色
0036: mov oD0, c15

変わった部分は、2つめのテクスチャー座標 oT1 の設定です。
XY成分と、同次座標の計算用のW成分のスクリーン座標を求めます。
その後、w=1.0fの空間に変換し、適当なスケール c12.w = 2.5f を掛けて座標にしています。
c12.w をいろいろと変えると、スクリーントーンの点の大きさが変わります。

ピクセルシェーダープログラムは、いままでの組み合わせです。
下のソースの黄色の部分ですが、モノトーンフィルターでやった方法で、ツゥーンシェーダーの結果の色 t0 の色の強さを求めます。
次に、c0.w = 0.2 を減算して、トーンを掛ける部分と掛けない部分を切断する色の強さを調整します。
後は、比較命令 cnd を持ちいて、トーンを掛ける部分(明るさが0.3以下)と、掛けない部分(明るさが0.3以上)を決定します。

0004: ps.1.0
0005: 
0006: def c0, 0.0f,   0.0f,   0.0f,   0.2f
0007: def c1, 0.299f, 0.587f, 0.114f,1.000f  ; v = 0.299*R + 0.587*G + 0.114*G
0008: 
0009: ; テクスチャーの色を引っ張ってくる
0010: tex t0                              ; ツゥーンテクスチャー
0011: tex t1                              ; トーンテクスチャー
0012: 
0013: mul r1, t0, v0                      ; ツゥーンシェーディング
0014: 
0015: dp3 r0.rgba, t0, c1                 ; 色の強さを求める
0018: add r0.a, r0, -c0                   ; 切断色を調整する
0016: mov r0.rgb,  t1                     ; トーンテクスチャーを引っ張る
0020: cnd r0, r0.a, c1.aaa, r0            ; (0.5 < 色の強さ-0.2) ? 1 ? トーンテクスチャー
0021: 
0022: mul r0, r0, r1                      ; 最終色=トーン*ツゥーン*オブジェクトの色

■問題点

作っていて気が付いたのですが、ドアの部分などで、不自然にスクリーントーンが引き伸ばされる場合があります。

テクスチャーを繰り返しモード(D3DTADDRESS_WRAP)で貼り付けているのですが、その処理が正常になされないようです。 (テクスチャーの値が0~1にクリップされる?)
従って、テクスチャーのサイズを大きくするか、ポリゴンを細かく分割するかの対応が必要そうです。

■最後に

静止画で見ると、そこそこ見れるのですが、動くと「ブキミ」ですね。
うまくオブジェクトに貼り付けて、ツゥーン処理と絡めれば、そこそこ使えるかと思います。




もどる

imagire@gmail.com