続・トゥーンシェーダ
前回上手くいかなかった輪郭線の抽出をフィルタを使ってやってみた。
t-potさんの本を参考にフィルタを作成。
一度レンダリングした画像の深度バッファからテクセルの差分をとって輪郭線を書く。
という方法で実装した。
元がアセンブラのコードだったので、HLSLで書いてみた。
fxファイルより一部抜粋
float du = 1.0f / 640.0f / 2; // テクセル幅 / 2 float dv = 1.0f / 480.0f / 2; sampler Samp0 : register(s0); // エッジを抽出する float4 EdgePS(float2 Tex : TEXCOORD0) : COLOR { float4 Out = (float4)0; float2 Tex1 = Tex; // 隣のテクセル Tex1.x -= du; Tex1.y -= dv; float2 Tex2 = Tex; Tex2.x -= du; Tex2.y += dv; float2 Tex3 = Tex; Tex3.x += du; Tex3.y += dv; float2 Tex4 = Tex; Tex4.x += du; Tex4.y -= dv; float4 Sub1 = tex2D(Samp0, Tex1) - tex2D(Samp0, Tex3); float4 Sub2 = tex2D(Samp0, Tex2) - tex2D(Samp0, Tex4); Out.rgba = 1.0f - 128*(Sub1.a*Sub1.a + Sub2.a*Sub2.a); return Out; }
もっとスマートな書き方がありそうだが、動いているので気にしない方向で。
今更だけど
トゥーンシェーダを実装してみた。
まず、色の階調化だけど、これは簡単にできた。
ライトのベクトルと法線の内積をとって、UV座標を計算。ベクトルは正規化しないとU値が1より大きくなるので注意。このままでは、値が0以下のこともありえるので、0から1の範囲になるように調整する。
次は、輪郭線の抽出。さっきと同じで、視線のベクトルと法線の内積をUV座標のV値に代入する。
後は計算したUVを元にトゥーン用のテクスチャから色を拾って描画。これで、おしまい。
描画してみた
何か輪郭線が汚い。上手く抽出できてないようだ。
以下、fxファイルの中身
float4x4 mWVP; float3 vLight = {0.557f, -0.557f, 0.557f}; // ライトの方向 float3 vEye; // 頂点シェーダー出力 struct VS_OUTPUT { float4 Pos : POSITION; // 座標 float2 Tex : TEXCOORD0; // テクスチャUV }; VS_OUTPUT ToonVS(float4 Pos : POSITION, float4 Normal : NORMAL) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.Pos = mul(Pos, mWVP); // 座標変換 float3 N = normalize(Normal.xyz); // 法線を正規化 Out.Tex.x = max(dot(vLight, N), 0) * 0.5f + 0.5f; // 0から1の範囲に修正 float3 E = normalize(vEye - Pos.xyz); // 視線のベクトル Out.Tex.y = dot(E, N); // vを計算、内積が小さいとき、輪郭抽出を行う return Out; } technique ToonShader { pass P0 { VertexShader = compile vs_2_0 ToonVS(); } }
PSPSDKのfreadはゆっくりしすぎ
PSPSDKのfreadを使って画像を読み込もうとしたのだが、512*512のビットマップをロードするのに軽く30秒以上かかる。遅すぎる。
いろいろと調べていたら、ファイルの読み込みは、sceIoRead関数で4byte境界で行ったほうが、早くなるらしいとのこと。とりあえず、そのように実装してみたら3秒位で読み込めた。
あと、14byteの構造体をsizeofでサイズ取得したら16byteになってた。こっちも4byte境界なのかもしれない。