--shader(GLSL)の小技 15-- 極座標で折りたたむ。
折りたたみとはabs()を使うだけの事。ただ、バリエーションが色々と有り過ぎる。フラクタルまで、当てはまる。ちょっと説明しようもない事に気づいた。
ここでは、極座標を折って行く方法をやってみます。
p-=2.0*min(0.0,dot(p,v))*v;
これは任意方向の折りたたみになります。これを使います。
これを角度を変えて折って行くと、極座標を2等分、4等分、8等分・・・としていけます。
次のような関数になります。v=normalize(vec2(v.x-1.0,v.y));
と、謎の式がありますが、これ偶然見つけた式です。色々とやってたら、上手く収まちゃいました。動きとしては、vのx軸に対しての角度が-90°、-45°、-22.5°・・・と半分になっていく式です。
vec2 fold(vec2 p, int n) { p.x=abs(p.x); vec2 v=vec2(0,1); for(int i=0;i<n;i++) { p-=2.0*min(0.0,dot(p,v))*v; v=normalize(vec2(v.x-1.0,v.y)); } return p; }
座標をp、反復回数をnにしています。
2のn乗が分割数になります。
#version 300 es
を使ってないなら、for文にnは使えないので、ダイレクトに
vec2 fold(vec2 p) { p.x=abs(p.x); vec2 v=vec2(0,1); for(int i=0;i<3;i++) { p-=2.0*min(0.0,dot(p,v))*v; v=normalize(vec2(v.x-1.0,v.y)); } return p; }
上みたいに、nのところに反復回数を入れてください。
使い方は、サンプルを参考にしてください。使い方は無限にある。たぶん。
今回のshader (shadertoy rule)
mat2 rotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } float udRoundBox( vec3 p, vec3 b, float r ) { return length(max(abs(p)-b,0.0))-r; } vec2 fold(vec2 p, int n) { p.x=abs(p.x); vec2 v=vec2(0,1); for(int i=0;i<n;i++) { p-=2.0*min(0.0,dot(p,v))*v; v=normalize(vec2(v.x-1.0,v.y)); } return p; } float map(vec3 p) { p.xz=fold(p.xz,3); p.z-=3.0; return min(udRoundBox(p,vec3(0.5),0.1),length(p.yz)-0.2); } vec3 doColor(vec3 p) { return vec3(0.3,0.5,0.8); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y; vec3 ro=vec3(0,3,-7); ro.zx*=rotate(iTime*0.5); vec3 rd=normalize(vec3(uv,2)); vec3 w=normalize(-ro); vec3 u=normalize(cross(w,vec3(0,1,0))); rd=mat3(u,cross(u,w),w)*rd; float d,i,t=0.0; vec3 p=ro; vec3 col=vec3(0); for(i=1.0;i>0.0;i-=1./50.0) { t+=d=map(p); if(d<0.001) { col=doColor(p); col*=i*i; break; } p+=rd*d; } fragColor=vec4(col,1.0); }
追記
極座標で折りたたみの関数をロジックの解るシンプルなものにしました。
version 300 es 用です。
vec2 fold(vec2 p, float n) { for(float i=0.0;i<n;i++) { float a = PI/exp2(i); vec2 v = vec2(cos(a),sin(a)); p-=2.0*min(0.0,dot(p,v))*v; } return p; }