シャープなリニアフィルタ


cubicやlanczosは他にも資料や解説があるので、
動作が軽くて、ドット感が失われない、シャープなリニアフィルタについて、書き留めておこうと思います。


元々、このフィルタは「整数倍の拡大だと補間無し(point)がいいよね」って会話から「なら2.25倍はx2を補間無しで拡大して、残りのx1.25倍を補完したらどうなんだろうね」というアイデアから発生しています。
よりドット感を保ったまま綺麗に拡大することが本フィルタの主題であり、bicubicの亜種として作成していました。
初期のアルゴリズムを掻い摘んで言えば、倍率がx2なら、サンプリングするテクセル座標の範囲をx0.5に縮めることで、1パスで擬似的に表現していました。(実際に補間無しで2倍する行程を、UV座標を逆計算することでマルチパスを回避していました)


さて、よくよく考えれば補間が入る場所が狭いので、Cubicを使う必要がありません。正直linearでも十分というのが分かります。
また、linearの場合は縦横2x2のサンプリングですから、読み込むUV座標を逆計算する必要もなく、結局の所、素直に補間の度合に強弱を付けるだけで同じ効果が得られるので大きな最適化が望めます。

強度パラメータにしてしまえば、アルゴリズムが簡単になる他にも、強度に小数値が指定でき、細かい調整が出来るのも魅力的です。
以下、PixelShader(HLSL)。

//PS 2.0

//float2 vSize...TextureSize(width,height)
float2 fUV = frac (IN.texUV0 * vSize);

//強度適用
//float2 SharpN...強度(1.0〜)
fUV = (fUV-0.5)*SharpN + 0.5;
fUV = min(fUV,1.0);
fUV = max(fUV,0.0);
	
float2 uu,vv;
uu.x = 1.0-fUV.x;
uu.y = fUV.x;
vv.x = 1.0-fUV.y;
vv.y = fUV.y;	

//TexelRead
//UV0 UV1  (Position)
//UV2 UV3
float4 tmp0,tmp1,tmp2,tmp3;
tmp0 =  tex2D(ColorSampler,IN.texUV0);
tmp1 =  tex2D(ColorSampler,IN.texUV1);
tmp2 =  tex2D(ColorSampler,IN.texUV2);
tmp3 =  tex2D(ColorSampler,IN.texUV3);

//Linear  
tmp0 = tmp0*uu.x + tmp1*uu.y;
tmp2 = tmp2*uu.x + tmp3*uu.y;
return tmp0*vv.x + tmp2*vv.y;

アスペクト比を守って拡大することが前提なので、強度が縦横一緒になっています。必要なら縦横で分けるべきかも知れませんね。


コレで終わりかというもう少し続きます。
この方式では倍率に関係なく一律の強度パラメータが適用されます。
つまり、x1.25倍の場合に強度4.0をした場合、結果は補間無しのようなジャギジャギしたものになってしまいます。
よって、倍率から適切な強度を求める必要があります。
とりあえず、x1.0倍(又はそれ以下)では普通のLinear相当にするのが良いでしょうから、強度は1.0(1.0+0.0)が適切でしょう。
次にx2.0倍の場合ですが、試行錯誤と、キリのいい数値的な意味で、強度は1.5(1.0+0.5)ぐらいが妥当だと思われます。
これらの関係を式にすると

[強度]=([倍率]-1.0)/2 + 1.0
if ([強度]<1.0) [強度]=1.0

な感じでしょうか。
人それぞれだとは思いますので、適当に。


2倍程度ではイマイチ実感が出ませんが、3〜4倍になるとそれなりにドット絵に対して有効なフィルタではないかと思われます。
昔のゲームでドット感を残したい場合に使えるかも知れませんね。
個人的には、ドットを無くして滑らかに補完するよりも、ドット感が残る方が好きなので、結構好きだったりします。
3Dモデルに使用する場合は、3ないし4頂点変形のため倍率が得られないので使えないかと思います。
なにか良い方法はないかな?