GLSLレイマーチング研究_距離関数について勉強してみた14(Capped cylinderの関数をいじる)

[modeling with distance functions](http://iquilezles.org/www/articles/distfunctions/distfunctions.htm)の距離関数の一覧に沿って記事を書いています.

# Capped cylinderつまり円柱である.
## 円柱の距離関数

```c++
vec2 h = vec2(【半径】, 【高さ】);
vec2 d = abs(vec2(length(p.xz),p.y)) - h;
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
```

## lenghtを使用しない表記

```c++
/* length抜きの表記 */
float r      = 1.0;
float height = 1.0;
return sqrt((max(sqrt(p.x*p.x+p.z*p.z)-r, 0.0))*(max(sqrt(p.x*p.x+p.z*p.z)-r, 0.0))+(max(abs(p.y)-height,0.0)*(max(abs(p.y)-height,0.0))));
```

## 数式の説明
基本的に、[box](http://qiita.com/muripo_life/items/cbb81371bea70bfc2cd7)と[cupsule](http://qiita.com/muripo_life/items/f071dbac6e6125eea2f2)の応用です.

```c++
vec2 d = abs(vec2(length(p.xz),p.y)) - h;
```
の式で、ほぼ式を決めているので、lengthは,統一感を出すために挟んでいるようです。

なので、

```c++
vec2 h = vec2(【半径】, 【高さ】);
vec2 d = abs(vec2(length(p.xz),p.y)) - h;
return min(max(d.x,d.y),0.0) + max(max(d.x,0.0),max(d.y,0.0));
```

## `min(max(d.x,d.y),0.0)`ってなに?

なくても、円柱は描画されます.

```c++
min(max(d.x,d.y),【ここをいじってみよう】)
```

```c++
return min(max(d.x,d.y), sin(time)) + length(max(d,0.0));
```

![motion04.gif](https://qiita-image-store.s3.amazonaws.com/0/102634/0350f5e8-1dae-ca7c-c7b5-410d2dbfbafb.gif)

はい、これ境界をきっちり決めているみたいですね.

なので、`sin(time)` を挟むと、膨張したり凹んだりします.

# いじって遊ぶ
## 円錐二つ

```c++
/* 円錐二つ */
vec2 h = vec2(1.0, 1.0);
vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
return min(min(d.x,d.y),0.0) + length(max(d,0.0));
```

![01.PNG](https://qiita-image-store.s3.amazonaws.com/0/102634/55ac583f-4b32-a8af-c593-0c1d43dacf0f.png)

##　コマ
![02.PNG](https://qiita-image-store.s3.amazonaws.com/0/102634/82afb942-fd34-8655-a74a-de171ba549a1.png)

```c++
vec2 h = vec2(1.0, 1.0);
vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
return min(min(d.x,d.y),0.0) + length(max(d,0.3));
```

## [コマアニメーション](http://glslsandbox.com/e#39242.0)

![motion02.gif](https://qiita-image-store.s3.amazonaws.com/0/102634/2b3d2815-418e-8558-ad64-750d24f8875a.gif)


# [ソース](https://github.com/muripoLife/RayMarchingDistanceFunction/blob/master/14/fsp.frag)

```c++
// ============================================================================
// Capped cylinder
// ============================================================================

precision mediump float;
uniform vec2  resolution;    // resolution (512.0, 512.0)
uniform vec2  mouse;         // mouse      (-1.0 ~ 1.0)
uniform float time;          // time       (1second == 1.0)
uniform sampler2D prevScene; // previous scene texture


// Capped cylinderの距離関数
float sdCappedCylinder(vec3 p)

{
    // 回転
    // mat3 m_x = mat3(1,0,0,0,cos(time),-sin(time),0,sin(time),cos(time));
    // mat3 m_x = mat3(1,0,0,0,cos(0.3),-sin(0.3),0,sin(0.3),cos(0.3));
    // p = m_x * p;
    // mat3 m_y = mat3(cos(time),0,-sin(time),0,1,0,sin(time),0,cos(time));
    // p = m_y * p;
    // mat3 m_z = mat3(cos(time),-sin(time),0,sin(time),cos(time),0,0,0,1);
    // p = m_z * p;

    /* 標準の距離関数 */
    vec2 h = vec2(1.0, 1.0);
    vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    return min(max(d.x,d.y),0.0) + length(max(d,0.0));

    /* length抜きの表記 */
    // float r      = 1.0;
    // float height = 1.0;
    // return sqrt((max(sqrt(p.x*p.x+p.z*p.z)-r, 0.0))*(max(sqrt(p.x*p.x+p.z*p.z)-r, 0.0))+(max(abs(p.y)-height,0.0)*(max(abs(p.y)-height,0.0))));

    /* lengthを使用しない抜きの表記 */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    // return min(max(d.x,d.y),0.0) + max(max(d.x,0.0),max(d.y,0.0));


    /* 三角錐二つ */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
    // return min(min(d.x,d.y),0.0) + length(max(d,0.0));

    /* コマ */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
    // return min(min(d.x,d.y),0.0) + length(max(d,0.3));

    /* コマ(変形) */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
    // return min(min(d.x,d.y),0.0) + length(max(d,0.5*abs(sin(time))));

    /* コマ(コマ回しアニメーション) */
    // mat3 m_y = mat3(cos(time),0,-sin(time),0,1,0,sin(time),0,cos(time));
    // p = m_y * vec3(p.x+1.5*cos(time), p.y, p.z+1.5*sin(time));
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
    // return min(min(d.x,d.y),0.0) + length(max(d,0.3));

    /* メタリックぽい? */
    // mat3 m_x = mat3(1,0,0,0,cos(time),-sin(time),0,sin(time),cos(time));
    // p = m_x * p;
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    // return min(max(d.x,d.y)-0.03*abs(sin(time)),0.0) + length(max(d,0.0));

    /* ノイズっぽい? */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    // return min(max(d.x,d.y),0.0)*-1.0*abs(sin(time)) + length(max(d,0.0));
    
    /* 膨張 */
    // vec2 h = vec2(1.0, 1.0);
    // vec2 d = abs(vec2(sqrt(p.x*p.x+p.z*p.z),p.y)) - h;
    // return min(max(d.x,d.y),-1.0+sin(time)) + length(max(d,0.0));
}

// 距離関数を呼び出すハブ関数
float distanceHub(vec3 p){
    return sdCappedCylinder(p);
}

// 法線を生成する
vec3 genNormal(vec3 p){
    float d = 0.001;
    return normalize(vec3(
        distanceHub(p + vec3(  d, 0.0, 0.0)) - distanceHub(p + vec3( -d, 0.0, 0.0)),
        distanceHub(p + vec3(0.0,   d, 0.0)) - distanceHub(p + vec3(0.0,  -d, 0.0)),
        distanceHub(p + vec3(0.0, 0.0,   d)) - distanceHub(p + vec3(0.0, 0.0,  -d))
    ));
}

void main(){
    // スクリーンスペースを考慮して座標を正規化する
    vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
    // カメラを定義する
    vec3 cPos = vec3(0.0,  0.0,  5.0); // カメラの位置
    vec3 cDir = vec3(0.0,  0.0, -1.0); // カメラの向き(視線)
    vec3 cUp  = vec3(0.0,  1.0,  0.0); // カメラの上方向
    vec3 cSide = cross(cDir, cUp);     // 外積を使って横方向を算出
    float targetDepth = 1.0;           // フォーカスする深度
    // カメラの情報からレイを定義する
    vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);
    // マーチングループを組む
    float dist = 0.0;  // レイとオブジェクト間の最短距離
    float rLen = 0.0;  // レイに継ぎ足す長さ
    vec3  rPos = cPos; // レイの先端位置(初期位置)
    for(int i = 0; i < 32; ++i){
        dist = distanceHub(rPos);
        rLen += dist;
        rPos = cPos + ray * rLen;
    }
    // レイとオブジェクトの距離を確認
    if(abs(dist) < 0.001){
        // 法線を算出
        vec3 normal = genNormal(rPos);
        // ライトベクトルの定義
        vec3 light = normalize(vec3(1.0, 1.0, 1.0));
        // ライトベクトルとの内積を取る
        float diff = max(dot(normal, light), 0.1);
        // gl_FragColor = vec4(vec3(diff, diff, diff), 1.0);
        gl_FragColor = vec4(vec3(diff*177.0/255.0, diff*120.0/255.0, diff*68.0/255.0), 1.0);
    }else{
        // 衝突しなかった場合はそのまま黒
        gl_FragColor = vec4(vec3(0.0, 0.0, 0.0), 1.0);
    }
}
```