抽象シェーダ模索

で、こう shader とかって基本的にはリアル絵出すためのものだけど、まぁそいうの興味無いっていうか絵かけないしモデリングとかよくわかんないしーってこと(興味はあるけどいじけてるというか)でそっち方向でもシェーダって使えないのかなとかそんなことを思って勉強してみることに。

こっちもまったく似たようなことしてます。まあXNAがシェーダ必須なのでしかたなく考えているわけ(id:ABA:20061214#p3)だが。

で、現状何に使っているかというと、まずバーテックスシェーダを擬似3Dっぽいいんちき奥行き計算に使っている。

以前、某携帯Javaアプリで擬似3Dレースゲームを作った時、スピード感を出すために路面の描画においてのみ、Z軸の値を2乗や3乗に(実際には係数で調整します)してみたことがあります。効果抜群でした。

まあここからの発想なわけだが。とりあえずZ座標を2乗すると妙なスピード感が出る。いや空間がめためたに歪むから結構変な感じになるけど。

VS_OUTPUT DistortedTransform(VS_INPUT Input)
{
    VS_OUTPUT Output;
    Output.Position = mul(Input.Position, World);
    float z = Output.Position.z;
    z += 10;
    z = z * z * sign(z) - 100;
    Output.Position.z = z;
    Output.DiffuseDepth.y = Output.Position.z;
    Output.Position = mul(Output.Position, ViewProj);
    Output.Texcoord = Input.Texcoord;
    float4 normal = mul(Input.Normal, WorldIT);
    normal.w = 0.0;
    normal = normalize(normal);
    Output.DiffuseDepth.x = dot(normal, LightVec);
    return Output;
}

HLSLのスーパーpre記法ないのね。そりゃそうか。ここにあるようにZ座標をごにょごにょしてピクセルシェーダに渡す。

で、ピクセルシェーダは縁ありの四角を書いて、あとソフトでないシャドウをつけるために使っている。

PS_OUTPUT AddEdgeAndLighting(PS_INPUT Input)
{
    PS_OUTPUT Output;
    float onLight = (Input.DiffuseDepth.x < 0.5)? 0.0:1.0;
    float btx = frac(Input.Texcoord.x);
    float bty = frac(Input.Texcoord.y);
    float hasBoard = (btx < 0.1 || btx > 0.9 || bty < 0.1 || bty > 0.9)? 0.0:1.0;
    float edgeThreshold = 0.001 * (100 + Input.DiffuseDepth.y);
    float cr = hasBoard * onLight * 0.5;
    cr = (btx < (0.1 + edgeThreshold))? cr * 1.3:cr;
    cr = (bty < (0.1 + edgeThreshold))? cr * 1.5:cr;
    cr = (btx > (0.9 - edgeThreshold))? cr * 0.7:cr;
    cr = (bty > (0.9 - edgeThreshold))? cr * 0.5:cr;
    Output.Color = Color * cr;
    Output.Color.a = Color.a;
    return Output;
}

これあきらかに重いよね……まだ360実機上で試してないから分からんのだが。あと距離にかかわらず一定の太さに見える縁を書くいい方法が分からん。今はInput.DiffuseDepth.yにZ座標をつっこんでおいて、それをいんちきな数式で変換して閾値を求めている。

なんつうか、素直にテクスチャ使えとか、素直にラインで書けとか、そういった声が聞こえてきそうな感じ。もうちょっとまともな使い道はないもんか。

ARB_*_program でコードを書く場合問題になるのはアセンブリで書かにゃならんのでめんどい&保守しにくい、ってこと。特にエラー出た時にどこでエラー出てるかさっぱりわからないのは結構きびしいかも。

ちなみにXNA上のHLSLは、エラー箇所を行番号で的確にポイントしてくれるので、この辺の手間は恐ろしく少ない。シェーダ入門用としてはかなりいい環境だと思う。動的にfxファイル書き換えってのはもちろんできないが。