title: "円" description: "distance関数とlength関数を使って円を描きます。smoothstepによるアンチエイリアスも学びます。" chapter: "04-shapes" order: 2 challenge: description: "画面上に3つの異なるサイズの円を描いてください。それぞれ異なる色にし、円同士が一部重なるように配置しましょう。" hints:
- "円のSDF関数を使い回し、中心位置と半径を変えて3回呼び出します。"
- "smoothstep(0.01, 0.0, d) で各円の内側を判定できます。"
- "色を重ねるには加算(+)が簡単です。重なり部分は色が混ざって明るくなります。"
円
distance関数とlength関数
GLSLには距離を計算するための便利な関数が用意されています:
// length: ベクトルの長さ(原点からの距離)
float len = length(vec2(3.0, 4.0)); // = 5.0
// distance: 2点間の距離
float dist = distance(vec2(1.0, 2.0), vec2(4.0, 6.0)); // = 5.0
実は distance(a, b) は length(a - b) と同じです。
円のSDF
前回学んだ円のSDFをおさらいします:
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
任意の位置に円を描くには、座標をずらします:
vec2 center = vec2(0.5, 0.5);
float d = sdCircle(uv - center, 0.25);
smoothstepでアンチエイリアス
step を使うとくっきりした境界になりますが、ピクセルのジャギーが目立ちます。smoothstep を使うと滑らかな境界が得られます:
// step:ジャギーが出る
float shape = step(0.0, -d);
// smoothstep:滑らかな境界
float shape = smoothstep(0.01, 0.0, d);
smoothstep(edge0, edge1, x) は:
x <= edge0のとき0.0x >= edge1のとき1.0(edge1 < edge0 の場合は逆)- その間は滑らかに補間
ピクセルサイズに応じた幅で smoothstep を使うと、解像度に依存しないアンチエイリアスが得られます:
float px = 1.0 / u_resolution.y; // 1ピクセルの大きさ
float shape = smoothstep(px, 0.0, d);
実践:円を描く
右のプレビューでは、画面中央に白い円が描かれています。コードを見てみましょう:
vec2 uv = gl_FragCoord.xy / u_resolution;
vec2 p = uv - vec2(0.5);
p.x *= u_resolution.x / u_resolution.y;
float d = length(p) - 0.3;
float circle = smoothstep(0.005, 0.0, d);
vec3 color = vec3(circle);
まとめ
length()でベクトルの長さ、distance()で2点間の距離を計算- 円のSDF:
length(p - center) - radius smoothstepを使うとアンチエイリアスの効いた滑らかな描画ができる