レイマーチング-modで複製した物体に乱数のパラメータを与える-

#はじめに

今回レイマーチングで作りたいのはこういうやつ
![ 2017-10-22 12.08.54.png](https://qiita-image-store.s3.amazonaws.com/0/199455/a44b0c49-4213-44b8-06e5-aab990f9ed1b.png)
ランダムな高さを持った直方体を複製してやるぞ〜といった感じです
コードはGLSLです

#作り方
まず思いついた方法が，物体をmod()を使って複製してやる
-> 個々の物体の高さに座標を引数とした乱数を設定してやる

みたいな感じで作ろうと思いました，いけそう

##やってみた
```glsl:
    float boxDistFunc(vec3 p,vec3 b,vec3 c){
        return length(max(abs(p-c)-b,0.0));
    }
    float floorDistFunc(vec3 p){
        vec3 q=p;
        q.xz=mod(p.xz,1.0)-0.5;
        return boxDistFunc(q,vec3(0.4,0.4,0.4),vec3(0.0,0.0,0.0));
    }
    float distFunc(vec3 p){
        return floorDistFunc(p);
    }
```
**まずこれが単純に物体を複製した状態**

![ 2017-10-21 1.44.26.png](https://qiita-image-store.s3.amazonaws.com/0/199455/f34a869d-b605-1b45-8011-81505553ff79.png)
要はmodをレイの先端の座標に噛ませてやることでxz座標に関して-0.5~0.5までが延々繰り返されるっていう感じですね，なので原点に置いた一片が0.4*2=0.8の立方体がいっぱい出てる感じです

でこの中で最終的に使ってる距離関数がfloorDistFuncで，その中で呼び出してるboxDistFuncの第二引数が立方体の一片になるので，ここを弄ればいけるだろうと

```glsl
    float random(vec2 p){
        return fract(sin(dot(p.xy,vec2(12.9898,78.233)))*43758.5453123)*2.0-1.0;
    }
    float boxDistFunc(vec3 p,vec3 b,vec3 c){
        return length(max(abs(p-c)-b,0.0));
    }
    float floorDistFunc(vec3 p){
        vec3 q=p;
        q.xz=mod(p.xz,1.0)-0.5;
        return boxDistFunc(q,vec3(0.4,abs(random(floor(p.xz))),0.4),vec3(0.0,0.0,0.0));
    }
    float distFunc(vec3 p){
        return floorDistFunc(p);
    }
```
![ 2017-10-21 1.45.45.png](https://qiita-image-store.s3.amazonaws.com/0/199455/6b84f2c4-2932-ee7f-f68c-40fefb2b1781.png)

**あれれ〜〜〜おかしいぞ〜〜〜〜〜??**

ぐちゃぐちゃやないか，原型とどめてない
ということでこれを解決してみましたっていうお話です

#原因
要はレイが貫通しちゃってる状態なんですねあれは
当たるべき場所で当たってなくて，そのまま突き進んじゃうのであんな感じになっちゃうわけです

でその原因なんですが，図にしてみるとわかりやすかったです

**普通になんの乱数も与えず複製した場合**

![ 2017-10-22 12.56.19.png](https://qiita-image-store.s3.amazonaws.com/0/199455/750b675c-9bdd-7320-6fd3-8369a5aade16.png)

こういう感じでレイが進んでいます
ここで気をつけなければいけないのは，modを使って物体を複製しているのですが，あくまでmodはレイの先端座標に使っているということです
物体自体を予めいっぱい作っているわけではないので，レイの先端座標を進める際に現在レイが位置している格子中の物体以外の影響を受けていません

**問題の場合**

![ 2017-10-22 12.56.29.png](https://qiita-image-store.s3.amazonaws.com/0/199455/da8a80ca-2942-4ca3-895f-728f3c694763.png)

こういうことが起きちゃうってことですね

#解決策
上の様な場合はレイを距離関数から返された値そのまま進めるのではなく，次の格子に突入するまでだけ進めるっていう方法をとりました
![ 2017-10-22 12.56.38.png](https://qiita-image-store.s3.amazonaws.com/0/199455/21f149cf-3777-0c74-ce3d-1cef6089131a.png)

以下コードです

```glsl
        float distance=0.0;
        float rLen=0.0;
        vec3 rPos=origin;
        vec3 color=vec3(0.1);
        for(int i=0;i<100;i++){
            distance=distFunc(rPos);
            if(abs(distance)<0.01){
                if(distance==floorDistFunc(rPos)){
                    color=vec3(0.3,0.3,0.7);
                  }
                 break;
            }

            float t1=(1.0-fract(rPos.z))/ray.z;
            vec3 d1=rPos+t1*ray;
            float t2=(1.0-fract(rPos.x))/ray.x;
            vec3 d2=rPos+t2*ray;
            float t3=(-fract(rPos.x))/ray.x;
            vec3 d3=rPos+t3*ray;
            float t4=(-fract(rPos.z))/ray.z;
            vec3 d4=rPos+t4*ray;
            if(ray.z>0.0 && d1.x>=floor(rPos.x) && d1.x<=floor(rPos.x)+1.0 && abs(distance)>=length(t1*ray)){
                rLen+=length(t1*ray)+0.01;
            }else if(ray.x>0.0 && d2.z>=floor(rPos.z) && d2.z<=floor(rPos.z)+1.0 && abs(distance)>=length(t2*ray)){
                rLen+=length(t2*ray)+0.01;
            }else if(ray.x<0.0 && d3.z>=floor(rPos.z) && d3.z<=floor(rPos.z)+1.0 && abs(distance)>=length(t3*ray)){
                rLen+=length(t3*ray)+0.01;
            }else if(ray.z<0.0 && d4.x>=floor(rPos.x) && d4.x<=floor(rPos.x)+1.0 && abs(distance)>=length(t4*ray)){
                rLen+=length(t4*ray)+0.01;
            }else{
                rLen+=distance;
            }
            rPos=origin+rLen*ray;
        }
```
**追記**
コメント欄にありますが，[gaziya5](https://qiita.com/gaziya5)さんがもっとスッキリした書き方にまとめてくださっています
詳しくはコメント欄を見ましょう！

#まとめ
物体をmodを使って複製した後に個々の高さを乱数で設定してみました
なんかゴリ押し感強くてもっといい方法とかいくらでもありそうですが初心者なりに色々考えたので記事にしてみました
もっとこうしたほうがいいぞ！ってご意見あればお気軽にお願いします

今回のコードの全貌はこちらにあります
https://github.com/ukeyshima/randomHeight