Shader(HLSL), 手続き的にテクスチャ生成など行うとき使用頻度の高い関数

Shaderを使ってプロシージャルにモデルやテクスチャを作成する時, 把握しておくと便利な関数があります.

基本的には, TEXTURING&MODELING A Procedural Approach 3rd Edition (https://www.amazon.co.jp/Texturing-Modeling-Procedural-Approach-Third/dp/4862460860) のCHAPTER2冒頭の内容です.詳しくはこちらの書籍を参照されてください.

# 補間関数

## lerp
線形補間を行う関数

```.c
float lerp(float a, float b, float x)
{
  return a + x * (b - a);
}
```

xが0のとき, 結果はa, xが1のとき, 結果はbとなります. xが0.5のとき, 結果は0.5 * a + 0.5 * b, xが0.7の時, 0.3 * a + 0.7 * bとなり, xの値を0→1すると, 結果は線形にa→bと変化します.

# 条件関数

## step
2つの値を比較して, どちらの値が大きいかに基づいて 0 または 1 を返します.

```.c
float step(float a, float x)
{
  return (float) (x >= a) ? 1 : 0;
}
```

xがa以上の場合は1, それ以外は0を返します.

![bandicam 2017-02-11 19-04-32-232.png](https://qiita-image-store.s3.amazonaws.com/0/35267/544f49a4-9bf1-2fe2-cf72-545bb96563ff.png)


#### ifの書き換え
GPUアーキテクチャの特性上, if文の多用はパフォーマンスを落とす要因となり得ます.
lerp関数とstep関数を用いて if文は以下のように書き換えることができます.

```.c
if (u < 0.5)
{
  color = fixed4(1.0, 1.0, 1.0, 1.0);
}
else
{
  color = fixed4(0.0, 0.0, 0.0, 0.0);
}
```

↓↓↓

```.c
color = lerp(fixed4(1.0, 1.0, 1.0, 1.0), fixed4(0.0, 0.0, 0.0, 0.0), step(0.5, u));
```

#### step関数で作られる矩形パルス関数
2つのstep関数を組み合わせて, 次のような矩形パルスが作成できます.

```.c
#define PULSE(a, b, x) (step((a),(x)) - step((b),(x)))
```

グラフは以下のようになり, aで始まりbで終わるパルスを生成します.
![bandicam 2017-02-11 19-04-40-152.png](https://qiita-image-store.s3.amazonaws.com/0/35267/47016c26-fc02-978f-a1f1-22e09e64fa48.png)


## clamp
指定された値を指定された最大値と最小値の範囲に留めます

```.c
float clamp(float x, float a, float b)
{
  return (x < a ? a: (x > b ? b : x));
}
```

![bandicam 2017-02-11 19-04-10-072.png](https://qiita-image-store.s3.amazonaws.com/0/35267/177526a4-22e0-50bb-5c77-555c1f08573d.png)

## abs
指定された値の絶対値を返します.

```.c
float abs(float x)
{
  return (x < 0 ? -x : x);
}
```

グラフは以下のようになります.
![bandicam 2017-02-11 19-04-00-884.png](https://qiita-image-store.s3.amazonaws.com/0/35267/52ffb2ee-84b3-c78b-38d5-79cf51125666.png)


## smoothstep
閾値aで始まり, 閾値bで終わる緩やかな遷移を作ります.
smoothstepは, aとbのときに傾きが0, aのとき値が0, bのとき値が1となる特性を持つ3次関数 $3x^2-2x^3$ で実装されます.

```.c
float smoothstep(float a, float b, float x)
{
  float t = saturate((x - a)/(b - a));
  return t * t * (3.0 - (2.0 * t));
}
```

グラフは以下のようになります.
![bandicam 2017-02-11 19-04-24-414.png](https://qiita-image-store.s3.amazonaws.com/0/35267/151cb072-43c7-ad76-e37e-41a6041c04d9.png)


# 周期関数

## sin
正弦波を返します. 0で始まり, xを[0, 2π]と変化させると[-1, 1]の範囲で値が変化します.
グラフは以下のようになります.
![bandicam 2017-02-11 19-08-12-300.png](https://qiita-image-store.s3.amazonaws.com/0/35267/06e86f39-31e9-8ade-ee7f-cb990399440d.png)

## cos
余弦波を返します. 1で始まり, xを[0, 2π]と変化させると[-1, 1]の範囲で値が変化します.
グラフは以下のようになります.
![bandicam 2017-02-11 19-08-17-267.png](https://qiita-image-store.s3.amazonaws.com/0/35267/e8ffecef-bec8-9219-e4ae-8fe902edd608.png)



## fmod
aをbで除算したときに得られる正の剰余が得られます.

```.c
float fmod(float a, float b)
{
  int n = (int)(a / b);
  a -= n * b;
  if (a < 0)
  {
    a += b;
  }
  return a;
}

```

以下のようなグラフが得られます.
![bandicam 2017-02-11 19-04-16-650.png](https://qiita-image-store.s3.amazonaws.com/0/35267/65242303-8fc9-1043-5855-77c8ed7ca538.png)


`fmod(x, a) / a`とすると,振幅1, 周期がaののこぎり波が得られます.

これと
`#define PULSE(a, b, x) (step((a),(x)) - step((b),(x)))`
を組み合わせることによって, 周期cのパルス関数が得られます

```.c
PULSE(a, b, fmod(x, c) / c)
```
![bandicam 2017-02-11 19-05-36-576.png](https://qiita-image-store.s3.amazonaws.com/0/35267/653dea00-5c1f-f258-4f63-480a419b5ebe.png)


# 補正系関数（非組み込み関数）
単位区間で単調増加する関数を区間内での値のゆがみを変化させます.

## ガンマ補正関数

```.c
float gammacorrect(float gamma, float x)
{
  return pow(x, 1.0 / gamma);
}
```

gammaが1より大きければ, 1に向かって上方向にシフトし, gammaが0と1の間であれば0に向かって下方向にシフトします.

![bandicam 2017-02-11 19-33-49-394.png](https://qiita-image-store.s3.amazonaws.com/0/35267/b0c43837-249b-7ae5-3c07-34e264a60c2c.png)

![bandicam 2017-02-11 19-34-01-013.png](https://qiita-image-store.s3.amazonaws.com/0/35267/271d7f85-2ee6-b8c8-0f59-1df4b0b40946.png)


## bias関数
bias関数はガンマ補正関数のバージョンの一つで, `bias(b, 0.5) = b`となるように定義されたパラメータ b で, gammaパラメータを置き換えます.

```.c
float bias(float b, float x)
{
  return pow(x, log(b) / log(0.5));
}
```

![bandicam 2017-02-11 19-34-13-567.png](https://qiita-image-store.s3.amazonaws.com/0/35267/f2d9b1a0-5b00-d32b-8100-9b81d3e150b7.png)

![bandicam 2017-02-11 19-34-25-059.png](https://qiita-image-store.s3.amazonaws.com/0/35267/cdd2ee60-b99a-ffb3-b1e0-4ffd9ea0cb80.png)



## gain関数
gの値によらずxが0.5のとき0.5を返す, bias曲線で形成されたS字曲線を返します.

```.c
float gain(float g, float x)
{
  if(x < 0.5)
  {
    return bias(1.0 - g, 2.0 * x) / 2.0;
  }
  else
  {
    return 1.0 - bias(1.0 - g, 2.0 - 2.0 * x) / 2.0;
  }
}
```

![bandicam 2017-02-11 19-34-53-596.png](https://qiita-image-store.s3.amazonaws.com/0/35267/eb73c7b9-9e8b-32e8-2455-31576522ce90.png)

![bandicam 2017-02-11 19-35-06-528.png](https://qiita-image-store.s3.amazonaws.com/0/35267/8ec27df0-06e1-8587-e05e-1b8cbfe70580.png)