テラByteの時代にキロByte

shader又はdemosceneに関係する事

--shader(GLSL)の小技 10-- raymarchingでmod()を使わないで大量モデル描写

raymarchingでmod()を使わないで大量モデル描写する。TDFのデモで使った手法だけど、誰もとってくれないので、自分で解説する事にしました。
一個一個をバウンディングして範囲に入っていたら、z値を比較し、手前なら描写という手法。GPUによるがGeForce GTX 960で,500個くらいなら問題無く動いてます。 説明はソースの方に書いておいたので、そちらを見てください。
この手法のポイントは

if(length(cross(rd,q-ro))<1.0)

これです。length(cross(v,p-a)) は、a点を通る単位ベクトルvの直線の距離関数になります。
ベクトルがrdで通過点がroならば、映像的には円になります。実際にはシリンダーですが、機能的にはバインディング球になる感じです。

今回のshader (shadertoy rule)

// https://www.shadertoy.com/view/4lXyWN
vec3 hash(uint i)
{
    uvec3 x = uvec3(123,456,789)*(i+55u);
    uint k = 1103515245U;
    x*=k;
    x = ((x>>2u)^(x.yzx>>1u)^x.zxy)*k;
    return vec3(x)*(1.0/float(~0U));
}

float deBox(vec3 p)
{
    return length(max(abs(p)-vec3(0.5),0.0));   
}

float map(vec3 p)
{
    return deBox(p);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y;
    vec3 ro=vec3(0,0,-20);
    vec3 rd=normalize(vec3(uv,2));
    vec3 col=vec3(0);
    
    float z=99.0;
    for(uint j=1u;j<200u;j++){
        // オブジェクトの座標を取得
        // 乱数でベクトルを取得し時間を乗算
        vec3 q=(hash(j)*2.0-1.0) *(iTime+20.0)*0.2;
        // この式で-1~1の間を行き来する
        // イメージで言えば3Dビリヤード
        q = (abs(fract(q)*2.0-1.0)*2.0-1.0)*15.0;
        
        // 座標からmaymarchingをするか決定する。
        if(length(cross(rd,q-ro))<1.0)
        {
            float d,i,t=0.0;
            vec3 p=ro;
            for(i=1.0;i>0.0;i-=1./50.0)
            {
                t+=d=map(p-q);
                if(d<0.001)
                {
                    // 視点と座標の距離を取得
                    float s = dot(rd,p-ro);
                    // z値を比べて描写するか決定
                    if(s<z)
                    {
                        col=vec3(1);
                        col*=i*i;
                        col*=exp(-t*t*0.001);
                        z=s;
                    }
                    break;
                }
                p+=rd*d;
            }
        }
    }
    fragColor=vec4(col,1.0);
}