--shader(GLSL)の小技 07-- 立面と側面の距離関数を使って3次元の距離関数
立面と側面の距離関数を使って3次元の距離関数作る方法。
2次元の距離関数取得 -> 2次元の距離関数取得 で3次元の距離関数になる。
2次元の距離関数を用意します。
float lengthN(vec2 v, float n) { vec2 tmp = pow(abs(v), vec2(n)); return pow(tmp.x+tmp.y, 1.0/n); }
これは、今は繋がらなくなった http://www.demoscene.jp/?p=1147 にあった2Dの距離関数です。
私が、一番使っている奴です。円を変形させて4角っぽくします。これを平面として使います。
これでシリンダーを作ります。今は
Inigo Quilez :: fractals, computer graphics, mathematics, shaders, demoscene and more
にやり方載ってるけど、1つ書いておきます。
//http://www.demoscene.jp/?p=1147 float lengthN(vec2 v, float n) { vec2 tmp = pow(abs(v), vec2(n)); return pow(tmp.x+tmp.y, 1.0/n); } float de1(vec3 p) { p.x-=2.0; // wは、xz平面における距離関数 float w= abs(lengthN(p.xz,3.0)-1.5)-0.2; vec2 d = vec2(w, abs(p.y)-3.0); // 末尾の0.1は角のアールです。 return length(max(d,0.0))-0.1; }
もう一つのやり方。
xz平面における距離関数を作ります。
float a = atan(p.z, p.x); float r = 0.5 * ((sin(5.0 * a)) + 3.0); float d = r * length(p.xz);
dがxz平面における距離関数。これとp.yでvec2をつくります。vec2(d, p.y)
こうです。それを座標として扱い2Dの距離関数に入れます。
vec2 q = vec2(d, p.y); float de = lengthN(q,3.0) - 2.0;
こんな感じで作ります。
バリエーションが多いので、今回は2例だけです。
以前、これについて書いたshaderがあるので、それを参考にしてください。
https://www.shadertoy.com/view/4tsGD4
昔、書いた奴なので、ちょっとヒドイですが、又書き返す根性ないので、これで勘弁してください。
かなり、レイのオーバーシュート気味になる確率が高い。
レイの進行を遅らせる定数を掛けて、誤魔化して使う事がほとんどです。
もう少し、ちゃんとやりたい人は
https://www.shadertoy.com/view/Xlf3zf
https://www.shadertoy.com/view/4llGWM
ここを参考にしてください。
rayのオーバーシュート対策が、しっかりとられています。
ここのロジックの解説を誰かやってくれないかな?
2番目のshaderのSuperFormulaを使ってる形状は圧巻です。
今回のshader (shadertoy rule)
mat2 rotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } //http://www.demoscene.jp/?p=1147 float lengthN(vec2 v, float n) { vec2 tmp = pow(abs(v), vec2(n)); return pow(tmp.x+tmp.y, 1.0/n); } float de1(vec3 p) { p.x-=2.5; // wは、xz平面における距離関数 float w= abs(lengthN(p.xz,3.0)-1.5)-0.2; vec2 d = vec2(w, abs(p.y)-1.0); // 末尾の0.1は角のアールです。 return length(max(d,0.0))-0.1; } // XZ平面の距離関数とYでvec2を作り、違う距離関数の引数に使う。 // 2次元の距離関数取得 -> 2次元の距離関数取得 で3次元の距離関数になる。 float de2(vec3 p) { p.x-=-2.5; float a = atan(p.z, p.x); float r = 0.5 * ((sin(5.0 * a)) + 3.0); float d = r * length(p.xz); vec2 q = vec2(d, p.y); float de = lengthN(q,3.0) - 2.0; // ちょっとレイがオーバーシュート気味なので、レイの進行を遅らせる定数を掛けてます。 return 0.4 * de; } float map(vec3 p) { return min(de1(p),de2(p)); } 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,5,-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); }