テラByteの時代にキロByte

shader又はdemosceneに関係する事

--shader(GLSL)の小技 08-- Capsuleの距離関数からオペレーション部分を取り出した

Inigo Quilez :: fractals, computer graphics, mathematics, shaders, demoscene and more ここにある sdCapsule の距離関数

float sdCapsule( vec3 p, vec3 a, vec3 b, float r )
{
    vec3 pa = p - a, ba = b - a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h ) - r;
}

より、オペレーション部分を取り出し、他の距離関数にも使えるようにした。

vec4 opSlide(vec3 p, vec3 a, vec3 b, vec3 up){
  vec3 ba = b - a;
  float h = clamp(dot(p-a, ba) / dot(ba, ba), 0., 1.);
  p -= a + ba * h;
  vec3 w = normalize(ba), u = normalize(cross(up, w)), v = cross(w, u);
  return vec4(p * mat3(u, v, w), h);
}

使い方は sdCapsuleと同じだが、upという引数が増えた。lookat行列のupと同じだ。
返り値がvec4になっているが、返り値を d とした場合、d.xyz が変換された座標、 d.w が伸びてる空間のt値。このt値を使って色々と細工が出来る。 今回は少しバリエーションがあるので、スクリプトの使い方を見てください。 中身の方だか、lookat行列と同じ手法でmat3()を取得したが、今回は、逆行列として使ってます。

今回のshader (shadertoy rule)

mat2 rotate(float a)
{
    return mat2(cos(a),sin(a),-sin(a),cos(a));
}

// これは、sdCapsuleの距離関数のロジックをオペレーションに転嫁して球以外のモノにも対応できるようにしました。
vec4 opSlide(vec3 p, vec3 a, vec3 b, vec3 up){
  vec3 ba = b - a;
  float h = clamp(dot(p-a, ba) / dot(ba, ba), 0., 1.);
  p -= a + ba * h;
  vec3 w = normalize(ba), u = normalize(cross(up, w)), v = cross(w, u);
  return vec4(p * mat3(u, v, w), h);
}
    
float sdSphere(in vec3 p, in float s )
{
  return length(p)-s;
}

float sdTorus(in vec3 p, in vec2 t )
{
  vec2 q = vec2(length(p.xz)-t.x,p.y);
  return length(q)-t.y;
}

float sdHexPrism(in vec3 p, in vec2 h )
{
    vec3 q = abs(p);
    return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x);
}

float udRoundBox(in vec3 p, in vec3 b, in float r )
{
  return length(max(abs(p)-b,0.0))-r;
}

float deJyabara(in vec3 p, in float t)
{
    float de2 = abs(length(p.xz) - 0.5 + 0.05 * sin(t * 30.0));
    vec2 d = abs(vec2(de2, p.y)) - vec2(0.08, 0.0);
    return min(max(d.x, d.y), -0.03) + length(max(d, 0.0));  
}

float map(in vec3 p)
{
    vec3 a = vec3(1);
    vec3 b = vec3(-1);
    vec3 up = normalize(vec3(1,2,3));   
    vec4 op = opSlide(p, a, b, up);
    p = op.xyz; 
    switch (int(floor(mod(iTime * 0.3, 5.0)))) {
    case 0:
        return length(max(abs(p) - vec3(0.5, 0.2, 0.0), 0.0)) - 0.05;
    case 1:
        return sdTorus(p, vec2(0.5, 0.2));
    case 2:
        return sdHexPrism(p.xzy, vec2(0.3, 0.2));
    case 3:
        return sdSphere(p, 0.35);
    case 4:
        return deJyabara(p.xzy, op.w);
    }
}

vec3 doColor(vec3 p)
{
    return vec3(0.8,0.5,0.2);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y;
    vec3 ro=vec3(0,2,-5);
    ro.zx*=rotate(iTime*0.5);
    vec3 rd=normalize(vec3(uv,2));
    vec3 ta=vec3(0);
    vec3 w=normalize(ta-ro);
    vec3 u=normalize(cross(w,vec3(0,1,0)));
    vec3 v=cross(u,w);
    mat3 lookat=mat3(u,v,w);
    rd=lookat*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);
}