ひとことで
0 から 1 への変化を、両端で傾きが 0 になる S 字カーブでつなぐ関数です。境界を柔らかくぼかしたり、動きの開始と停止を穏やかにしたりする、シェーダーとゲームプログラミングの定番部品です。さらに滑らかな 5 次版を smootherstep と呼びます。
ゲームでの使いどころ
- 柔らかい境界:スポットライトの縁、ディゾルブの焼けた縁、霧の開始距離、選択範囲のハイライト。境界を
smoothstep(edge0, edge1, x)でぼかすのはシェーダーの常套句です。 - フェードイン・アウト:UI、音量、画面遷移。線形だと始まりと終わりが唐突に感じられます。
- 移動の加減速:エレベーター、ドア、カメラの移動。デモ左の昇降機のように、端でふわっと止まります。
- ノイズの補間:値ノイズやパーリンノイズで、格子の間をつなぐ重みとして使います。
しくみ
まず を範囲 で 0〜1 に写し(Inverse Lerp + clamp)、それを 3 次式に通します。この 3 次式は、次の 4 条件を満たす唯一の 3 次多項式です。
両端で傾き(速度)が 0 なので、動き出しと停止が穏やかになります。ただし 2 階微分(加速度)は両端で と 0 にならないため、動き出しの瞬間に加速度が跳ねます。これも 0 にしたのが Ken Perlin の smootherstep です。
| 関数 | 端での速度 | 端での加速度 | 主な用途 |
|---|---|---|---|
| 線形(clamp) | 1(急に動き出す) | — | 単純な割合 |
| smoothstep | 0 | ±6(跳ねる) | 境界のぼかし、一般的なフェード |
| smootherstep | 0 | 0 | 法線を使うノイズ補間、非常に滑らかな動き |
実装のポイント
- 幅を画面サイズに合わせる:シェーダーで境界をアンチエイリアスしたいときは、ぼかし幅に
fwidth(x)を使うと、どの距離から見ても 1〜2 ピクセルの柔らかさになります。 - 逆向きの範囲:GLSL 仕様では
edge0 >= edge1の結果は未定義です。逆にしたいときは1.0 - smoothstep(...)と書きます。
つまずきやすい点
- 境界の位置がずれる:
smoothstep(e0, e1, x)の 0.5 の位置は です。「半径 r の円を幅 w でぼかす」ならsmoothstep(r - w, r + w, d)のように中心をそろえます。 - 動きの時間が延びて見える:同じ所要時間でも、両端が遅いぶん中央は線形の 1.5 倍の速さになります。移動量が大きいと中央が速すぎると感じることがあります。
- 繰り返し適用:
smoothstep(smoothstep(t))のように重ねると、さらにメリハリの強い S 字になります。意図的な効果として使うこともあります。
デモで試すこと
- 左の 3 台の昇降機を見比べてください。線形(赤)は上下端でガクッと止まり、smoothstep 系はふわっと止まります。
- 右の柱は、動く光の輪からの距離に関数をかけて高さを決めています。「右の柱に使う関数」を線形にすると、盛り上がりの縁が角ばった円錐になります。
- edge0 と edge1 を近づけると境界が鋭く、離すとなだらかになります。