BIO100% のページで、モノトーンへの変換の質問を見かけました。 なんか、Pixel Shader のいい例になりそうだったので、早速作ってみました。
次のソースを、ダウンロードしてください。 もともとは、フルカラーだったテクスチャーを、セピア調にレンダリングして表示します。
前回のプログラムと、ほとんど違いありません。 違いは、桜の絵『sakura.bmp』を読んで、ポリゴンのサイズを調整していることと、 SetPixelShaderConstant による、定数レジスタの設定をしていないことです (つまり、簡単になってんじゃん)。
といっても、難しいことはありません。
よく、色をRGBで表現しますが、それ以外にも表現方法があります。
そのひとつに、YCbCr があります(それ以外にも、ウィンドウズのウィンドウの色などをカスタマイズするときにESLが使われますね)。
PS2 を持っている人は、出力のオプションにあるので、知っているかもしれません。
YCbCr は、ビデオ出力に用いられている変換で、色を強さY と、色味Cb, Cr に分けます。
人間は、色の強さY に敏感で、色味にはそれほど敏感ではありません。
そこで、ビデオ出力では、色の強さYに関して情報量を多くし、Cb, Cr に関しては情報量を減らして
、効率よく(見た目をそれほど損なうことなく情報を間引きして)画像を送っています
(う~ん、きちんと調べたわけではないですけど、間違ってはいないでしょう)。
実は、色の強さY はモノクロ画像です。
これに、適当な(定数の)Cb,Cr をのせてから、RGB に逆変換すれば、モノトーン画像が得られます。
RGB から、YCbCr への変換は良く知られていて、
Y = 0.29900 * R + 0.58700 * G + 0.11400 * B Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B
になります。この逆変換は、3次元の行列として、求められて、
R = Y + 1.40200 * Cr G = Y - 0.34414 * Cb - 0.71414 * Cr B = Y + 1.77200 * Cb
となります(実は丸写しですので、違うかもしれません)。
以下のプログラムでは、Y を元のテクスチャーから求めて、Cb,Cr を定数 (Cb = -0.2, Cr = 0.1)にして、計算します。
今回のミソの、シェーダープログラムです。 次の様に組んで見ました。
ps.1.0 def c0, 0.299, 0.587, 0.114, 0.000 ; v = 0.299*R + 0.587*G + 0.114*G def c1, 0.000, 0.000, 0.000, -0.200 ; αはセピア色の Cb 成分 (-0.5<=Cb<=0.5) def c2, 0.000, 0.000, 0.000, 0.100 ; αはセピア色の Cr 成分 (-0.5<=Cr<=0.5) def c3, 0.000, -0.344, 1.722, 0.000 ; (R,G,B) の Cb 成分 = (0.000, -0.344, 1.722) def c4, 1.402, -0.714, 0.000, 0.000 ; (R,G,B) の Cb 成分 = (1.402, -0.714, 0.000) tex t0 ; テクスチャーの色 dp3_sat r0.rgba, c0, t0 ; テクスチャーの色と、c0 の内積を取って、色の強さを求める mad r0, c1.a, c3, r0 ;r0 = c1.a*c3 + r0 = c1.a*c3 + c0*t0 mad r0, c2.a, c4, r0 ;r0 = c2.a*c4 + r0 = c2.a*c4 + c1.a*c3 + (c0,t0) ;r0.r = 0.100*( 1.402) -0.200*( 0.000) + 0.299*R+0.587*G+0.144*B ;r0.g = 0.100*(-0.714) -0.200*(-0.344) + 0.299*R+0.587*G+0.144*B ;r0.b = 0.100*( 0.000) -0.200*( 1.722) + 0.299*R+0.587*G+0.144*B
def c0~c5 は、定数レジスタを設定しています。 前回は、外部から設定していましたが、シェーダープログラムからも設定できます。 今回は、それを試してみました。
テクスチャーの画像を tex t0 で、t0 に引っ張ります。
dp3_sat r0.rgba, c0, t0 は、飽和抑制つき内積です。c0 と t0 の積 (0.299*R+0.587*G+0.114*Gで、色の強さが求まります)を
計算した後、_sat で、0~1 の間にクリップします。
あと、マトリックス計算をどうしていいのか良くわから無かったので、とりあえず C1, C2 のα成分に Cb と Cr を格納しておいて、
定数レジスタ、C3, C4 と積和(0.1*c4-0.2*c3+(t0,c0))をとって、YCbCr から、RGB 成分を計算しました。
r0 がレンダリングの結果なので、以上の結果がレンダリング結果として得られます。
とりあえず、他では見かけないレンダリングをしてみました。
さらに、元のテクスチャーと上手く掛け合わせれば、『だんだんセピア化』などができるでしょう。
まだ、c1, c2がα成分しか使っていないなど、無駄がありますが、これからもいろいろ試したいと思います。