テラByteの時代にキロByte

shader又はdemosceneに関係する事

--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);
}