テラByteの時代にキロByte

shader又はdemosceneに関係する事

--shader(GLSL)の小技 03-- カメラの使い方

今回はカメラの使い方についてやってみます。これについては、色々を書き方があって人それぞれですが、ここではmat3()を使うやり方を使います。ここをブラックボックスで使うのもアリだけど、理解して使った方が、スッキリしてshaderが書けるし、3D空間のコントロールのスキルも上がるので、少し詳しく説明していきます。ただ、図を描くのが苦手なので、言葉だけで説明になります。なのて頭でイメージしてください。
これを理解するには、cross()で導かれるベクトルの性質をイメージできないと始まりません。イメージで説明します。アナログ時計の時針と分針を2つのベクトルとすれば、2つのベクトルのcross()は軸棒になります。軸棒と時針は直交してます。軸棒と分針は直交してます。これを頭に入れながら話を進めます。

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

カメラ部分だけを書きだしました。
まず、視点の位置をro,目標をtaにした場合のrayの方向の単位ベクトルを求めます。wとおきます。これがカメラの方向ベクトルになります。 このベクトルと直角のベクトルを取得する為に、補助ベクトルを用意します。通常は重力方向のvec3(0,1,0)。ここでcross()の出番です。wとvec3(0,1,0)を入力するとwに対して直角のベクトルが取得できます。normalize()をして、uとおきます。wとvec3(0,1,0)は直交しているとは限らないので、uとwをcross()に入力してベクトルを取得。これは直交している単位ベクトル同士なのでnormalize()の必要はありません。これでu,v,wのそれぞれが直交する単位ベクトルが手に入りました。これらをmat3(u,v,w)としてlookAt行列して使用できます。
mat3(u,v,w)*rdu*rd.x+v*rd.y+w*rd.zvec3(dot(rd,u), dot(rd,v), dot(rd,w)) はと同等です。uはrdのxの成分、vはrdのyの成分、wはrdのwの成分に対応してる感じが、こいつの正体(この1文に関しては、ニュアンスで言ってます。上手く表現できないです)。
補助ベクトルは、必ずvec3(0,1,0)でなくても良いです。wと同じでなければ構いません。真下を見たいなら補助ベクトルのvec3(0,1,0)と重なってしまうのでvec3(0,0,1)とかにしないとなりません。視界を斜めにしたければ、補助ベクトルをnormalize(vec3(0.2,1,0))とかにしても良いと思います。
今回はtaをわざわざ書いたけどta=vec3(0) なら必要ないので、vec3 w=normalize(-ro); で十分でしょう。

今回のshader (shadertoy rule)

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

float deBox(vec3 p)
{
    return length(max(abs(p)-vec3(1),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,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+=i*i;
            break;
        }
        p+=rd*d;
    }
    fragColor=vec4(col,1.0);
}