モノトーンフィルター


~BIO100%のページからひらめきました~




■今回のファイルは

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.psh)

今回のミソの、シェーダープログラムです。 次の様に組んで見ました。

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がα成分しか使っていないなど、無駄がありますが、これからもいろいろ試したいと思います。





もどる

imagire@gmail.com