範囲の変換

正規化

正規化は、一定のルールに基づいて、データを都合の良い形に整えること。

GLSL の場合、色は[0, 1]区間、座標は[0, 1]や[-1, 1]などの区間内の数値として扱うことが多い。

そのため、ここでいう正規化とは、ある値を[0, 1]範囲に収まる値(割合)に変換することだとする。

[a, b]範囲内に A という値がある。

このとき、[a, b]区間全体に対する A の割合は、

より、(Aa)(ba)\frac{(A - a)}{(b - a)}となるので、これを関数化すると、

float norm(float v, float min, float max) {
  return (v - min) / (max - min);
}

線形補間

範囲と割合を渡すと、その割合の位置にある値を返す機能を線形補間という。

範囲に割合をかければ、その割合の「位置」は求まる。

範囲の下限が 0 でないならば、その位置の「値」は下限を足したものになるはずだ。

float lerp(float min, float max, float t) {
  return min + (max - min) * t;
}

ちょっと式変形すると、t : (1 - t)という比が見えてくる。

  min + max * t - min * t
= min * 1 - min * t + max * t
= (1 - t) * min + t * max

この lerp 関数にあたるのが、GLSL の mix 関数なので、自前で実装する必要はない。

マップ

GLSL を触っていると、ある範囲内での位置(値)が、別の範囲内ではどの位置(値)に対応するのか、を知りたくなる機会がある。

例えば、[0, 100]区間内の 50 の位置は、[0, 10]区間内での 5 の位置に対応する。

[a, b]範囲内の A の位置が、[x, y]範囲内の X という位置に対応するとする。

まず、[a, b]区間に対する A の割合 M は、norm(A, a, b)で求まる。

そして、[x, y]範囲内で、割合が M となる値は、lerp(x, y, M)(GLSL ではmix(x, y, M))で求まる。

float map(float v, float min1, float max1, float min2, float max2) {
  return mix(min2, max2, norm(v, min1, max1));
}