見出し画像

GLSL Tips #02 輪郭のジャギーをなめらかにする

こんにちは、AQUARING かに です。

今回はGLSLでテクスチャをマスクする際によくある輪郭のジャギーをなめらかにする方法について書いていきます。

GLSL側で円形のマスクを作成するコードを例に説明していきます。

マスクがジャギる例

まず普通に円形のマスク関数を書くと以下のようになります。

precision mediump float;

varying vec2 vTexCoord;

/**
 * 円形マスク
 * @param {vec2} uv UV座標
 * @param {vec2} pos 円の中心
 * @param {float} r 円の半径
 */
float circleMask(vec2 uv, vec2 pos, float r) {
  return 1. - step(r, length(uv - pos));
}

void main() {
  vec2 uv = vTexCoord;
  
  vec3 color = vec3(1, 1, 1);
  float mask = circleMask(uv, vec2(.5, .5), .3);// 中心に反映 0.3 の円形マスクを作成
  color *= mask;
  
  gl_FragColor = vec4(color, 1.0);
}

ダウンロード (22)

ぱっと見ではわかりづらいですが、輪郭部分を拡大するとジャギーが発生しています。

ダウンロード (22)のコピー2

これは、以下のcircleMask関数の処理の中でstep関数を使っているのが原因です。

float circleMask(vec2 uv, vec2 pos, float r) {
  return 1. - step(r, length(uv - pos));
}

step関数を使うことで任意の閾値の前後で 0.0, 1.0 を振り分けることができるのですが、関数の特性上急に値が変化してしまうため値をそのままマスクに使うと輪郭部分にジャギーが発生します。

グラフにすると以下のようなかんじになります。
x が 0.3 を超えた瞬間に返り値が 1.0 になります。

スクリーンショット_2022-02-28_18_33_21

smoothstep関数でなめらかにする

step関数をsmoothstep関数に置き換えるだけで、輪郭のジャギーを解消することができます。

precision mediump float;

varying vec2 vTexCoord;

/**
 * 円形マスク
 * @param {vec2} uv UV座標
 * @param {vec2} pos 円の中心
 * @param {float} r 円の半径
 * @param {float} edge 輪郭部分の幅
 */
float circleMask(vec2 uv, vec2 pos, float r, float edge) {
  return 1. - smoothstep(r - edge * .5, r + edge * .5, length(uv - pos));
}

void main() {
  vec2 uv = vTexCoord;
  
  vec3 color = vec3(1, 1, 1);
  float mask = circleMask(uv, vec2(.5, .5), .3, .002);// 中心に半径0.3、輪郭0.002の円形マスクを作成
  color *= mask;
  
  gl_FragColor = vec4(color, 1.0);
}

ダウンロード (23)

拡大してもアンチエイリアスが効いていてジャギっていません。

ダウンロード (23)のコピー

circleMask関数の第四引数に輪郭の太さを受け取る変数 edge を追加し、step関数をsmoothstep関数に置き換えています。
smoothstep関数は任意の数値間を 0.0 ~ 1.0 になめらかに変化する値として返してくれるため、値の変化に勾配をつけることができます。

float circleMask(vec2 uv, vec2 pos, float r, float edge) {
  // (半径 - 輪郭の太さの半分) ~ (半径 + 輪郭の太さの半分) → 0.0 ~ 1.0 に変換
  return 1. - smoothstep(r - edge * .5, r + edge * .5, length(uv - pos));
}

このコードでは edge に 0.002 という小さな値をいれることで、ごく短い距離で値を 0.0 ~ 1.0 に滑らかに変化させ、アンチエイリアスの働きをさせています。

float mask = circleMask(uv, vec2(.5, .5), .3, .002);// 中心に半径0.3、輪郭0.002の円形マスクを作成

グラフにすると以下のようなかんじになります。
(みやすくするために edge = 0.2 の状態にしています)
0.3 を挟んで ± 0.1 の範囲で滑らかに値が変化しているのがわかります。

スクリーンショット_2022-02-28_18_58_22

まとめ

step でジャギったら smoothstep に書き換えましょう!

この記事が気に入ったらサポートをしてみませんか?