テラByteの時代にキロByte

shader又はdemosceneに関係する事

--shader(GLSL)の小技 04-- 距離関数に色情報を含めないで立ち回る方法。

raymarchingにおいて色を取得する部分というのは、その仕組み上、一番やっかいなところ。みんな手を変え品を変えてとやっている。map(距離関数を格納する部分)は一番アクセスするので、なるべくコンパクトにしたい。なので、ほぼ無駄な処理を繰り返す色の情報とかは入れたくない。でも、入れないと同じ事を別関数に書かなくてメンテナンスが悪い。そこで1つのアイデアです。色の情報が欲しい時は、既にrayがオブジェクトに衝突した後、なので、各オブジェクトの距離関数に座標を入力して返り値が衝突判定に使われている距離より小さければ、そのオブジェクトだと断定して色を返す方法。

vec3 doColor(vec3 p)
{
    const float precis = 0.001;
    if (de1(p)<precis) return vec3(0.3,0.5,0.8);
    if (de2(p)<precis) return vec3(0.8,0.5,0.3);
    return vec3(1);
}

こんな感じです。
他にも座標のエリアで色付けするとか、 if(length(p-vec3(1,2,3))<2.0) return vec3(0.3,0.5,0.8); みたいにある点から距離を使って色付けするとか、が考えられます。

今回の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;
}

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

float de1(vec3 p)
{
    p.x += 2.0;
    return udRoundBox(p,vec3(0.8),0.1);
}
    
float de2(vec3 p)
{
    p.x += -2.0;
    return sdTorus(p, vec2(1.0,0.3));
}

float map(vec3 p)
{
    return min(de1(p),de2(p));
}

vec3 doColor(vec3 p)
{
    const float precis = 0.001;
    if (de1(p)<precis) return vec3(0.3,0.5,0.8);
    if (de2(p)<precis) return vec3(0.8,0.5,0.3);
    return vec3(1);
}

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