MetalとSwiftで遊ぶ「エフェクトとKoara」

この記事は[NCC Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ncc)の5日目の記事です.

アドベントカレンダー内で勝手にやってるMetalの連載も5日目です．

# 勝手にやってるMetal連載

最近Metalを初めて楽しくなっちゃったのでたくさんoutputしてみる会です．

1. [Rendererを作る](https://qiita.com/micky_dtl/items/7177c667e5a5d6bb777a)
2. [プロシージャルモデリング的な何か](https://qiita.com/micky_dtl/items/711f3f6c86abfe0bea89)
3. [フラグメントシェーダで遊ぶ](https://qiita.com/micky_dtl/items/8926294af4ed81f69f76)
4. [テクスチャで遊ぶ](https://qiita.com/micky_dtl/items/f4f8b09e7478187c9ea5)
5. エフェクトで遊ぶ ←　イマココ

(そろそろしんどくなってきた)

# Koaraにエフェクトをかけて遊ぶぞ！

フラグメントシェーダしか使いません．
他のソースコードは[githubのリポジトリ](https://github.com/math-miki/MetalAdventCalendar)や[前回のTextureの記事](https://qiita.com/micky_dtl/items/f4f8b09e7478187c9ea5)を参考にしてください

## RGBディストーション

やること

* 赤色成分をx方向にずらす
* 緑色成分をy方向にずらす
* ずらす方向はそれぞれ，スクリーンの中心からの距離に比例する

出来上がり
<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/76493/8e1e1eee-049b-b9d3-d9c4-29e63464d4c7.jpeg">

コード

``` shades.cpp
fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
    // 座標変換とそのまま用いたテクスチャ座標
    float4 p = (vertexIn.screenCoord+1.0)/2.0;
    p.y = 1.0-p.y;
    // uniforms
    float2 touch = vertexIn.touch;
    float time = vertexIn.time;
    constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);
    // return half4(koara) // ここからスタート
    
    // RGBディストーション用のコード
    // x方向とy方向にずらした座標でサンプリング
    float2 d = vertexIn.screenCoord.xy*0.03;
    float2 distortion_x = float2(p.x+d.x, p.y);
    float2 distortion_y = float2(p.x, p.y+d.y);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);
    float4 Koara_distortion_x = koaraTexture.sample(nearestSampler, distortion_x);
    float4 Koara_distortion_y = koaraTexture.sample(nearestSampler, distortion_y);
    // 合わせて描画
    return half4(Koara_distortion_x.x ,Koara_distortion_y.y, koara.z, 1.0);
}
```

# ホワイトノイズ

やること

ノイズを生成して，サンプルした色と掛け合わせます.
使用するノイズは，コピペ伝承のfract-sinノイズです．

出来上がり

<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/76493/f5973209-6bd2-da38-5c87-5a455d4a129e.jpeg">

コード

``` shaders.cpp
float rnd(float2 n) {
    return fract(sin(dot(n, float2(12.9898,4.1414)))*43758.5453);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
    // 座標変換とそのまま用いたテクスチャ座標
    float4 p = (vertexIn.screenCoord+1.0)/2.0;
    p.y = 1.0-p.y;
    // uniforms
    float2 touch = vertexIn.touch;
    float time = vertexIn.time;
    constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);

    // ホワイトノイズ
//    return half4(half3(koara.xyz*rnd(p.xy)),1.0);
    
    // ホワイトノイズで2値化
    return half4(half3(koara.xyz*step(0.5, rnd(p.xy))),1.0);
    
    // 砂嵐化
//    return half4(half3(koara.xyz*step(0.5, rnd(p.xy+time))),1.0);
}

```
# 横にずらす(エフェクトの名前がわからない)

ホワイトノイズで使ったrnd関数を利用して，各textureのrowを同じだけ横にずらします．
説明分かりづらいので，画像を見て

出来上がり
<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/76493/dec465b2-7411-9ec4-34c9-ad416657033f.jpeg">

``` shaders.cpp

float rnd(float2 n) {
    return fract(sin(dot(n, float2(12.9898,4.1414)))*43758.5453);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
    // 座標変換とそのまま用いたテクスチャ座標
    float4 p = (vertexIn.screenCoord+1.0)/2.0;
    p.y = 1.0-p.y;
    // uniforms
    float2 touch = vertexIn.touch;
    float time = vertexIn.time;
    constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);
    float2 dx_p = float2(p.x+0.03*(rnd(float2(p.y))*2-1), p.y);
    float4 dx_Koara = koaraTexture.sample(nearestSampler, dx_p);
    
    return half4(dx_Koara);
}

```

# ポスタリゼーション

やること

それぞれの画素で赤色成分，緑色成分，青色成分をn段階に分けます．
まあ見ればわかるはず

出来上がり

<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/76493/269aade7-437b-4197-4bc1-9edf0c5c15e7.jpeg">

コード

``` shaders.cpp
half3 posterization(float3 c, int n) {
    return half3(ceil(c.x / (1.0/n)) / n,
                 ceil(c.y / (1.0/n)) / n,
                 ceil(c.z / (1.0/n)) / n);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
    // 座標変換とそのまま用いたテクスチャ座標
    float4 p = (vertexIn.screenCoord+1.0)/2.0;
    p.y = 1.0-p.y;
    // uniforms
    float2 touch = vertexIn.touch;
    float time = vertexIn.time;
    constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);

    return half4(posterization(koara.xyz,4),1.0);
}

```

# ソラリゼーション

やること

ソラリゼーションは，トーンカーブを色々したものです．
白黒画像ではなく，赤色成分，緑色成分，青色成分それぞれ独立にソラリゼーションしました，
こんな感じに，部分的に反転します．
<img width=300 src="https://gyazo.com/ab9b5453de06a26d1170e449070ddf8e.raw">

出来上がり
<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/76493/b5d4bad6-b4ab-64a1-712f-62692510262f.jpeg">
[こちら](http://satoh.cs.uec.ac.jp/ja/lecture/ComputerGraphics/13.pdf)からスクショしました．
画像処理検定の参考書にもこのような図がありましたね．


コード

``` shaders.cpp
half3 solarisation(float3 c) {
    return half3((1-cos(3*3.14*c.x))/2,
                 (1-cos(3*3.14*c.y))/2,
                 (1-cos(3*3.14*c.z))/2);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
    // 座標変換とそのまま用いたテクスチャ座標
    float4 p = (vertexIn.screenCoord+1.0)/2.0;
    p.y = 1.0-p.y;
    // uniforms
    float2 touch = vertexIn.touch;
    float time = vertexIn.time;
    constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
    float4 koara = koaraTexture.sample(nearestSampler, p.xy);

    // ソラリゼーション
    return half4(solarisation(koara.xyz),1.0);
}


```

# コードはこちら

[github](https://github.com/math-miki/MetalAdventCalendar/tree/master/MetalDay5)にあります．