GLSL
ではさほど使う機会のない行列ですが画像処理で使うこともあります。
この行列について根本的な部分の勘違いと、勘違いに気づかない原因について覚書きしておきます。間違いに気づけないのは本当に厄介。
ついでに行列について1からおさらい。
※前回float比較について色々言ってますが結局==比較します。問題ないし楽だから。
行列の宣言
まずは定義とアクセスです。
確認用コード(グレーになることを確認)。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void main(){ vec2 v=vec2(1, 0); mat2 m=mat2( 1, 2, 3, 4 ); vec4 c=v.yyyx;//black if(m[0][0]==1.)c+=v.xyyy; if(m[0][1]==2.)c+=v.yxyy; if(m[1][0]==3.)c+=v.yyxy; if(m[1][1]==4.)c-=v.xxxy*.5; gl_FragColor=c; } |
ここまでは特にいうことはないですね。
matrix
は以下のようにvector
で定義することもできます。
1 2 3 4 5 |
//上のfloat定義と同じmat2 mat2 m=mat2( vec2(1, 2), vec2(3, 4) ); |
行列の計算
ここでベクトルと掛け合わせてみましょう。
GLSL
ではベクトルの転置なんかは考えなくていいです。
となるので新しいベクトルは[1, 2]
となりそうですが、そうはなりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void main(){ vec2 v=vec2(1, 0); mat2 m=mat2( 1, 2, 3, 4 ); vec2 test=v*m; vec4 c=v.yyyx;//black if(test[0]==1.&&test[1]==2.)c+=v.xyyy;//false if(test[0]==1.&&test[1]==3.)c+=v.yxyy;//true gl_FragColor=c; } |
逆の場合はこうなります。
1 2 3 4 5 |
vec2 test=m*v; vec4 c=v.yyyx;//black if(test[0]==1.&&test[1]==3.)c+=v.xyyy;//false if(test[0]==1.&&test[1]==2.)c+=v.yxyy;//true |
これも[1, 3]
になりそうなのに[1, 2]
になっています。
行列定義
前見た資料を見直すとちゃんと書いてました。
The floats are assigned to elements in column major order. (OpenGL ES1.0)
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements
in column-major order. (OpenGL ES3.0)
より分かりやすく書いてるのはここです。
つまり定義する上で行に見えるところはカラム(列)だったんですね。
分かりやすく考えると転置行列を定義している感じです。
実用例と勘違いの元
なんでこんな根本的なことに今まで気づかなかったかというと、あまり使わないのもありますが基本的に使うのが回転行列ぐらいだったからです。
回転行列は掛ける順序や転置、回転方向によって同じ結果になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void main(){ vec2 v=vec2(1, 0); mat2 r=mat2( cos(time), sin(time), -sin(time), cos(time) ); mat2 rt=mat2( cos(time), -sin(time), sin(time), cos(time) ); mat2 rm=mat2( cos(-time), sin(-time), -sin(-time), cos(-time) ); vec4 c=v.yyyx;//black if(all(equal(r*v , v*rt)))c+=v.xyyy; if(all(equal(r*v , v*rm)))c+=v.yxyy; if(all(equal(rt*v, rm*v)))c+=v.yyxy; gl_FragColor=c; } |
さらに言えば、gl_FragColor=texture2D(t, r*uv);
のような使い方では回転行列とは逆方向にテクスチャが回るように見えるため、勘違いしたままで正しく動くということになっていました。
色系のフィルタなどを使うとおかしいことに気づけます。
1 2 3 4 5 6 7 8 9 10 |
void main(){ mat3 sepia=mat3( 0.393, 0.769, 0.189, 0.349, 0.686, 0.168, 0.272, 0.534, 0.131 ); vec3 color=texture2D(t, uv).rgb; gl_FragColor=vec4(sepia*color, 1);//黄緑になる gl_FragColor=vec4(color*sepia, 1);//正しいセピア } |
このように通常の行列として書いてしまった場合は計算順序を変える。
行列の性質おまけ
float
1つで定義した場合、スカラー行列になる。
1 2 3 4 5 6 7 |
void main(){ mat2 m=mat2(2); vec4 c=v.yyyx;//black if(m[0][0]==2.&&m[1][1]==2.)c+=v.xyyy; if(m[0][1]==0.&&m[1][0]==0.)c+=v.yxyy; gl_FragColor=c; } |
matrix
定義順にvector
に入れることが出来る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void main(){ vec2 v=vec2(1, 0); mat2 m=mat2(1,2,3,4); vec2 test2=vec2(m); vec3 test3=vec3(m); vec4 test4=vec4(m); vec4 c=v.yyyx;//black if(test2[0]==1.&&test2[1]==2.)c+=v.xyyy; if(test3[0]==1.&&test3[1]==2.&&test3[2]==3.)c+=v.yxyy; if(test4[0]==1.&&test4[1]==2.&&test4[2]==3.&&test4[3]==4.)c+=v.yyxy; gl_FragColor=c; } |