稀に良くコードを書き捨てるが毎回よくわからなくなるのでメモ。
整数のインデックスでアクセスする画像座標xyはピクセル中心が基準だがfloatで0.0~1.0のUV座標uvはピクセル端が基準となる(環境が多い、ほぼ100%)。
そこで横方向のピクセルだけを考え図示すると以下のようになる。wは画像の幅(x方向のピクセル数)である。xのインデックスの本来の最小値は0で最大値はw-1である。
ピクセル端はピクセル中心から0.5ピクセルだけずれている。
つまりx=-0.5がu=0.0に対応しx=w-0.5がu=1.0に対応する。
これをもとに考えるとxyからuvへの変換は以下の式になる(y方向のピクセル数=画像の高さはhとし、慣習に倣ってvはyに対して上下反転させている)。
u=\frac{x+0.5}{w}
\hspace{20pt}
v=1.0-\frac{y+0.5}{h}
同様にuvからxyへの変換は
x=u*w-0.5
\hspace{20pt}
y=(1.0-v)*h-0.5
となる。しかしこのままだとfloatでありピクセルにそのままアクセスできないので必要に応じてround()でNearest Neighborサンプリングしたり、あるいはbilinearサンプリングしたりすればよい。
やりがちな次の式、w-1といった整数インデックスの最大値で正規化するやり方は間違い。
u=\frac{x}{w-1}
\hspace{20pt}
v=1.0-\frac{y}{h-1}
気づかないことも多いが画像サイズが小さい時にずれが目立つ。