GLSLの練習【Crop, Tiling】


GLSL(OpenGL Shading Language)を使って画像処理する練習。

今回は色々な切り抜き(Cropping)と敷き詰め(Tiling)。

最終的にreact nativeで使いたいのでgl-reactを前提として書きます。

ひし形切り抜き DiamondCrop

gl-reactのサンプルにあるシェーダー。

座標中心を(0.5, 0.5)から(0.0, 0.0)としてx, yの絶対値を加算、0.5を超えるかどうかで書き分ける。

if文(もしくは三項演算子)を使わずstep0.01.0に分けてmixしている。

ifは処理の負荷がかかるというのをよく見かけるからそのせいかな。

円形切り抜き CircleCrop

mixの条件を変えただけ。

単純に中心からの距離をstep0, 1に分けているだけです。

三角形切り抜き TriCrop

ある点が三角形内にあるかどうかを判定する方法は色々あります。

点同士のベクトルを順に外積をとって向き(符号)が同じであれば中にあるというのがわかりやすかったので、GLSLで書いてみます。

三角形の座標3つを渡して計算しようとしましたが、2次元は一般的な外積ではないのでcross2dと符号をboolで取る関数を用意しました。

if文を避ける方法は思いつかなかった。

coords = [[0.0, 0.0], [1.0, 0.0], [0.5, 1.0]]とするとこうなる。

多角形切り抜き PolygonCrop

多角形の内外判定はCrossing Number Algorithm(点と周囲との交差判定)とWinding Number Algorithm(点と周囲とのベクトル間角度の合計判定)が一般的のようです。

Winding Number AlgorithmがGLSL実装しやすそうだったので書いてみます。

配列のサイズを不定にできなかったので最大10にして実際のサイズも渡す。

acosで角度を計算しますが正負は得られないため外積の符号を使います。

 

ところでこのコードは多くの環境で動きません(gl-react-nativeでのみ動いた)。

配列のサイズが合わないことや配列のインデックスに変数を使えないことが原因。

配列で未定義の操作が起きうるとダメ見たいです。

そもそも配列も基本的にあまり使わないほうがよいとか。

配列サイズを固定して平書きすれば動きます。

これで6つの座標に基づいた切り抜きが出来ます。うーん。

こんなcoordsをわたすとこうなります。

敷き詰め Tiling

tileで1列に敷き詰めたい数を指定して元画像の大きさを変えています。

centerはどこを中心に大きさを変えるかですが、敷き詰めの原点でもあります。

重要なのはmod[0~tile]になった座標ではみでた部分を再描画することです。

交互に敷き詰め TilingAlt

レンガのように継ぎ目がずれる敷き詰め方を考えます。

変わったのはx座標の値にy座標の値を使うところです。

これで1段ごとに0.5ズレるようになっています。

もちろんx, yの処理を逆にすれば縦に半分ズレます。

間隔をあけて敷き詰め TilingStep

modで描画しない分まで含めた座標にして範囲外は無視しています。

範囲外はこれまでのように三項演算子でvec4(0.0)にしてもよかったんですが、描画しないならdiscardの方が正しい気がしたので使っています。どっちがいいんだろう。

tile=3.0, stepx=0.5, stepy=0.1とするとこんな感じ。

組み合わせ

単純なシェーディングですがこれらを組み合わせによっては面白い表現もできそう。

例えば多角形切り抜きしてずらして敷き詰め、円状に切り抜くとこうなります。

所感

これまでなんどかGLSLやHLSLに触れてきましたが、環境構築が面倒、環境によって動作が異なる、包括的なドキュメントがないなど理由を付けて敬遠してました。

改めて書いてみるとやっぱり面倒ではありますが、今のところは面白みの方が強い。

多角形の切り抜きで特に思ったことですが、シェーダーは目的に合わせてワンオフで作るべきでライブラリ的に便利に使えるものではなさそう。

何でもできそうでいて色々と制限があるけど工夫すればなんとかできる。

とりあえずそこら中にある理解不能なサンプルを目指して遊んでみたい。


コメントを残す

メールアドレスが公開されることはありません。