この記事はシェーダーアドベントカレンダー Advent Calendar 2019 14日目です!
https://qiita.com/advent-calendar/2019/shader-advent-calender-2019
13日目は@MachiaWorxさんのシェーダで音楽を作るmusic shaderの紹介
https://qiita.com/MachiaWorx/items/fe586e7ceefbf107254f
でした。
概要
魔法陣の飾りに、ルーン文字っぽいものをプロシージャルに生成したい
さやちゃんぐbotからルーン文字っぽくないという指摘を受けて調べたところ
https://coliss.com/articles/freebies/free-font-which-cannot-read.html
どちらかというと、アトランティック言語に似ていますね
完成図
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
#define saturate(x) clamp(x,0.,1.)
#define _tail2x(p,n) (mod(p,2.)-1.)
float Hash( vec2 p, in float s ){
return fract(sin(dot(vec3(p.xy,10.0 * abs(sin(s))),vec3(27.1,61.7, 12.4)))*273758.5453123);
}
float noise(in vec2 p, in float s){
vec2 i = floor(p);
vec2 f = fract(p);
return mix(
mix(Hash(i + vec2(0.,0.), s), Hash(i + vec2(1.,0.), s),f.x),
mix(Hash(i + vec2(0.,1.), s), Hash(i + vec2(1.,1.), s),f.x),f.y) * s;
}
float fbm(vec2 p){
float v = 0.0;
v += noise(p*34., .1);
v += noise(p*20., .04);
return v;
}
vec2 tailY2x(vec2 p,float n){p*=n;return vec2(p.x,_tail2x(p.y,n));}
// signed distance
float sd(float d,float r){return r-d;}
float dot2(vec2 p){return dot(p,p);}
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
float l = saturate(abs(1./((p.x + fbm(p)) * 80.0))*sd(dot2(tailY2x(p+vec2(.1,0.),12.)),.9));
gl_FragColor = vec4(l*vec3( 0.75, 0.5, .05 )*2., 1.0);
}
解説
実は単なるfbmです。
一般的なfbmの解説はしませんので、以下The Book of Shaderのfbmの解説の方を参考にしてください。
https://thebookofshaders.com/13/?lan=jp
ノイズ生成で補間しない
重要なのはノイズ発生部分
float noise(in vec2 p, in float s){
vec2 i = floor(p);
vec2 f = fract(p);
f *= f * (3.0-2.0*f);//ノイズの頂点を滑らかに補間してつなぐ
return mix(
mix(Hash(i + vec2(0.,0.), s), Hash(i + vec2(1.,0.), s),f.x),
mix(Hash(i + vec2(0.,1.), s), Hash(i + vec2(1.,1.), s),f.x),f.y) * s;
}
通常ノイズ生成時に補間関数を使って各ノイズの頂点を滑らかに補間してつなぎますが、文字のカクカクした感じを出したかったので、補間関数を取り除きます。何もしなければ線形補間になるのでカクカクした感じになります。fbmで良く雷のサンプルが上がってますが、イマイチなのはこのせいです。ノイズ生成時に滑らかに補間していては、どう頑張ってもイナズマのカクカクした感じは出せません。この補間関数部分に、イージング関数を使って変化を強調しても面白い効果が出せます。
float noise(in vec2 p, in float s){
vec2 i = floor(p);
vec2 f = fract(p);
//f *= f * (3.0-2.0*f);//補間しない
return mix(
mix(Hash(i + vec2(0.,0.), s), Hash(i + vec2(1.,0.), s),f.x),
mix(Hash(i + vec2(0.,1.), s), Hash(i + vec2(1.,1.), s),f.x),f.y) * s;
}
fbmの回数は2回まで
次にfbmの回数を2回にします。
fbmの回数が増えるとドンドン角が取れてしまいます。
float fbm(vec2 p){
float v = 0.0;
v += noise(p*34., .1);
v += noise(p*20., .04);
return v;
}
マスクを掛ける
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
float l = saturate(abs(1./((p.x + fbm(p)) * 80.0)));
gl_FragColor = vec4(l*vec3( 0.75, 0.5, .05 )*2., 1.0);
}
↓
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
float l = saturate(abs(1./((p.x + fbm(p)) * 80.0))*sd(dot2(tailY2x(p+vec2(.1,0.),12.)),.9));
gl_FragColor = vec4(l*vec3( 0.75, 0.5, .05 )*2., 1.0);
}
sd(dot2(tailY2x(p+vec2(.1,0.),12.)),.9)
最後に、文字の形に見えるように円のグラデーションをタイリングしたマスクを掛けて完成です!
明日は、さやちゃんぐbotさんの80年代ゲーセン筐体っぽいレトロ調の画面をUnityシェーダーで作る。です。