こんにちは
本日は12月17日のはずですが、サンタクロースの影を感じられますね。。。
さて、前回の記事で、tangramで、まずは地図を表示させてみましたが、今回はもう少しいじっていきます
まずはどんなものが出来たのかお見せします
今回の成果
ネタ元のフラグメントシェーダー
ビルの表面は以下の私が過去にNEORTに投稿したシェーダーを使っています
yamlをどのように編集するのか
スタイルを定義するyamlですが、「cameras」、「lights」、「sources」、「layers」、「styles」の大項目があります
かなり大雑把ですが
- cameras にカメラの数とタイプの定義
- lights にライティングの定義
- sources に地図のタイルの定義
- layers に表示するレイヤーの定義
- styles にレイヤーに適用するスタイルの定義
上記を理解すれば、あとはドキュメントとサンプルを見て理解出来るのではと思います
今回は以下のマトリックス風のサンプルをベースに
- 回転のさせかたを変更
- 色の変更
- ビルの表面を変更
しました
利点を考える
地図でアート作品を作るのにはとても良いのではないでしょうか
というのはさておき、基本2Dなので、そこそこ用途が限られてくるかもなあという印象です
(今回3D風に見せていますが、スタイルでシェーダーを書いてそういう風に見せているだけ)
ただ、スタイルのシェーダーを書けば、世界全部どこでも同じスタイルで出せるのはおもしろいなと思いました
成果GIFは都庁を中心とした絵にしていますが、
以下の絵は我らがサッポロファクトリーです
デモはこちら
触ったらわかるのですが、GISとして任意にブラウザ上で動かそうとすると、かなり難易度が高いです(無理やり3Dかつ自動回転させてるので)
最後にstyleのyamlのソースコードを貼っておきます
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);