tangramでWEB GLSL地図入門 その２

こんにちは
本日は12月17日のはずですが、サンタクロースの影を感じられますね。。。

さて、前回の記事で、tangramで、まずは地図を表示させてみましたが、今回はもう少しいじっていきます

https://qiita.com/northprint/items/00eee5ee48007f794de7

まずはどんなものが出来たのかお見せします

## 今回の成果
![1224.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2788/e1162327-7ffb-79d2-3885-87a60df41921.gif)
センスはさておき、、クリスマスっぽい配色にしてみました

## ネタ元のフラグメントシェーダー
ビルの表面は以下の私が過去にNEORTに投稿したシェーダーを使っています

https://neort.io/art/bice73s3p9f3vpbp9ft0?index=447

## yamlをどのように編集するのか

スタイルを定義するyamlですが、「cameras」、「lights」、「sources」、「layers」、「styles」の大項目があります
かなり大雑把ですが

- cameras にカメラの数とタイプの定義
- lights　にライティングの定義
- sources に地図のタイルの定義
- layers に表示するレイヤーの定義
- styles にレイヤーに適用するスタイルの定義

上記を理解すれば、あとはドキュメントとサンプルを見て理解出来るのではと思います

今回は以下のマトリックス風のサンプルをベースに

- 回転のさせかたを変更
- 色の変更
- ビルの表面を変更

しました

https://github.com/tangrams/tangram-sandbox/blob/gh-pages/styles/tilt-matrix.yaml

## 利点を考える
地図でアート作品を作るのにはとても良いのではないでしょうか
というのはさておき、基本2Dなので、そこそこ用途が限られてくるかもなあという印象です
（今回3D風に見せていますが、スタイルでシェーダーを書いてそういう風に見せているだけ）

ただ、スタイルのシェーダーを書けば、世界全部どこでも同じスタイルで出せるのはおもしろいなと思いました

成果GIFは都庁を中心とした絵にしていますが、
以下の絵は我らがサッポロファクトリーです
![スクリーンショット 2021-12-25 3.57.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2788/f0ce4e98-cbe4-ba5e-77c5-7e8d261bf113.png)

---

デモはこちら

https://northprint.net/tangram-test/

触ったらわかるのですが、GISとして任意にブラウザ上で動かそうとすると、かなり難易度が高いです（無理やり3Dかつ自動回転させてるので）

最後にstyleのyamlのソースコードを貼っておきます

```yaml:scene.yml
cameras:
    camera1:
        type: perspective

lights:
    light1:
        type: directional
        direction: [0, 1, -.5]
        diffuse: .3
        ambient: 1

sources:
    nextzen:
        type: MVT
        url: https://tile.nextzen.org/tilezen/vector/v1/512/all/{z}/{x}/{y}.mvt
        url_params:
            api_key: ********************* ←　取得したAPIキーを入れる
        tile_size: 512
        max_zoom: 16

layers:
    earth:
        data: { source: nextzen }
        draw:
            tiledCross:
                order: 0
                color: [0,0,0]

    landuse:
        data: { source: nextzen }
        draw:
            tiledCross:
                style: tilt
                order: 1
                color: [0.2,0.45,0.87]

    water:
        data: { source: nextzen }
        draw:
            polygons:
                style: tilt
                order: 2
                color: '#88bbee'

    roads:
        data: { source: nextzen }
        filter:
            not: { kind: [path, rail, ferry] }
        draw:
            roads:
                order: 4
                color: gray
                width: 8
                cap: round
        highway:
            filter:
                kind: highway
            draw:
                roads:
                    order: 5
                    color: '#cc6666'
                    width: 12
                    outline:
                        color: grey
                        width: 1.5
        minor_road:
            filter:
                kind: minor_road
            draw:
                roads:
                    order: 4
                    color: lightgrey
                    width: 5

    buildings:
        data: { source: nextzen }
        draw:
            buildings:
                order: 7
                color: [0, 0, 0]
        3d-buildings:
            filter: { $zoom: { min: 13 } }
            draw:
                buildings:
                    extrude: true
                buildingsLines:
                    order: 8
                    color: [0.890,0.0,0.0]
                    width: [[12, .1px], [14, 0.5px], [15, 1.5px], [17, 1.5px], [18, 2px]]
                    extrude: true
styles:
    tilt:
        animated: true
        base: polygons
        shaders:
            blocks:
                global: |
                    mat3 rotateZ3D(float psi){
                        return mat3(
                            vec3(cos(psi),-sin(psi),0.),
                            vec3(sin(psi),cos(psi),0.),
                            vec3(0.,0.,1.));
                    }
                position: |
                    float t = u_time*0.05;
                    position.xyz = mat3(vec3(1.,0.,0.),vec3(0.,.5,-.5),vec3(0.,.5,.5)) * rotateZ3D(t) * position.xyz;
    roads:
        base: lines
        mix: base
        animated: true
        shaders:
            blocks:
                color: |
                    vec2 st = v_texcoord.xy;
                    vec2 rows = vec2(1.,20.);
                    float t = u_time*12.;
                    vec2 ipos = vec2(0.);
                    if ( v_color.r < 0.5) {
                        rows.x = 2.;
                        if (fract(st.x*rows.x * 0.5) < 0.5){
                            t *= -1.0;
                        }
                    }
                    ipos = floor(st*rows);
                    ipos.y -= floor(t);
                    vec2 fpos = fract(st*rows);
                    vec2 center = (.5-fpos);
                    float pct = random(ipos);
                    color.rgb = vec3(0.89,0.2,0.2) * ( pct * 1.5 );
    base:
        mix: tilt
        texcoords: true
        shaders:
            blocks:
                global: |
                    // Variant to be add to both vertex and fragments shaders
                    varying vec3 v_pos;

                    vec2 getTileCoords() {
                        return fract(v_pos.xy);
                    }

                    bool gridLine(vec2 st, float res, float press){
                        vec2 grid = fract(st*res);
                        return grid.x < res*press || grid.y < res*press;
                    }
                    float TileGrid(float res){
                        vec2 st = getTileCoords()*100.*res;
                        float pct = 0.0;
                        float press = 0.4+(1.0-fract(u_map_position.z))*0.1;
                        if (gridLine(st,0.01,press)) pct += 0.5;
                        if (gridLine(st,0.1,press)) pct += 0.1;
                        return pct;
                    }
                    float TileGrid(){ return mix(TileGrid(1.),TileGrid(2.),fract(u_map_position.z)); }

                    float random(float x){ return fract(sin(x)*43758.5453);}
                    float random(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }

                    float grid (in vec2 _pos, in float _zoom, in float _lineWidth){
                        _pos = fract(_pos*_zoom);
                        vec2 g = smoothstep(vec2(0.5-_lineWidth),vec2(0.5),_pos) -
                                 smoothstep(vec2(0.5),vec2(0.5+_lineWidth),_pos);
                        return clamp(g.x+g.y,0.0,1.0);
                    }

                    float box(in vec2 _st, in vec2 _size){
                        _size = vec2(0.5) - _size*0.5;
                        vec2 uv = smoothstep(_size,
                                            _size+vec2(0.001),
                                            _st);
                        uv *= smoothstep(_size,
                                        _size+vec2(0.001),
                                        vec2(1.0)-_st);
                        return uv.x*uv.y;
                    }
                    float cross(in vec2 _st, float _size){
                        return  box(_st, vec2(_size*0.5,_size*0.125)) +
                                box(_st, vec2(_size*0.125,_size*0.5));
                    }
                    float circle(vec2 p, float radius) {
                        return length(p) - radius;
                    }
                    float pattern(vec2 st, float n){
                        vec2 grid = vec2(3.,5.);

                        vec2 ipos = floor(st*grid);
                        vec2 fpos = fract(st*grid);

                        n = floor(mod(n,10.));
                        float pct = 0.0;
                        if (n < 1. ) { pct = st.y-st.x+0.5;; }
                        else if (n < 2. ) { pct = st.x; }
                        else if (n < 3. ) { pct = st.y; }
                        else if (n < 4. ) {  pct = max(st.x,st.y); }
                        else if (n < 5. ) {  pct = circle(st-1.0,0.5); }
                        else if (n < 6. ) {  pct = circle(st,0.5); }
                        else if (n < 7. ) {  pct = min(st.x,st.y); }
                        else if (n < 8. ) {  pct = st.x-st.y+0.5; }
                        else if (n < 9. ) {  pct = circle(vec2(st.x-0.5,st.y-1.0),0.01); }
                        else if (n < 10. ) { pct = circle(vec2(st.x-0.5,st.y),0.01); }

                        return step(.5,1.0-pct);
                    }
                position: |
                    // Normalize the attribute position of a vertex
                    v_pos = modelPosition().xyz;
    tiledCross:
        base: polygons
        mix: base
        animated: false
        shaders:
            blocks:
                color: |
                    vec2 pos = (getTileCoords()+vec2(.5));
                    float pct = clamp(cross(fract(pos),0.1),0.0,1.0);
                    color.rgb *= pct;
                    pos = (getTileCoords()*10.+vec2(.5));
                    pct = clamp(cross(fract(pos),0.3),0.0,0.8);
                    color.rgb = clamp(color.rgb+vec3(0.173,0.235,0.200)*pct,vec3(0.0),vec3(1.0));
    buildings:
        base: polygons
        mix: base
        blend: add
        animated: true
        texcoords: true
        shaders:
            blocks:
                color: |
                    vec3 pos = worldPosition().xyz*0.01;
                    vec2 uv = v_texcoord.xy;

                    float rows = 10.0;
                    vec2 ipos = floor(uv*rows);
                    vec2 fpos = fract(uv*rows);

                    ipos += vec2(0.5,floor(u_time*5.0*random(ipos.x+3.0)));
                    float pct = random(ipos);
                    vec3 c = vec3(pattern(fpos,100.*pct));
                    color.rgb = mix(c,vec3(c.r*0.1,c.g,c.b*0.3),step(.1,pct));

    buildingsLines:
        base: lines
        mix: base
        lighting: false
        shaders:
            blocks:
                width: |
                    width *= 0.2+min(pow(position.z*0.006,2.),.6);

```