Cofactor Matrix Log

WebGL Lib 2019/07/24

## 2D スプライト描画の検討継続中 [実行](sample/index.html) とりあえず2Dシェーダ周りは一段落、と、思いたい。 ### 以下試行錯誤の記録 ポリゴンではなく、ピクセルの塗分けでアンチエイリアスを掛ける場合、 一般的には距離フィールドを求めて、 そのスクリーン空間での微分値を使って各ピクセルのアルファ値を決定する。 距離フィールドというのは、次のような感じのもの。 ![距離フィールドの例](distance_field.png) 黒が距離 0, 白に行くほど離れている。 これを適当なところで区切ることで、目的の形状を得る。 参考 https://qiita.com/7CIT/items/fe33b9b341b9918b6c3d で、外形だけを得たい場合には、 とりあえず距離フィールドは連続でさえあれば足りるのだけれど、 枠線を描画しようと思うと連続なだけでは問題が出る。 枠の表現は、塗りの時は距離 0.0 から 1.0 までの範囲で区切っていたものを、 距離 0.9 から 1.0 の範囲に置き換えて描画することで実現する。 ここでただ連続なだけだと、0.1 の幅が各所で異なっているときに、 "太さが歪な" 枠線になってしまう。 (距離フィールドを内外二種類求めて、それぞれに計算する方法もあるが、 どうしても細い枠線の時に汚くなる) 太さがある程度均一な枠線を描くには、 形状の外側から、一定のスケールで距離が変化するようにしなければならない。 矩形と円だけなら、適切に領域を分ければ作れるのだけど、 問題は角に楕円が現れたとき。 楕円そのものは円を縦横に拡縮すれば描けるけれど、 距離関数を拡縮で求めれば、当然縦と横でスケールが変わってしまう。 楕円内の点と、楕円の距離を適切に決めなければならない。 「楕円と(内側の)点の距離」なんてありがちな問題、どこかに転がってるだろうと思ったけれど、甘かった。 一応、解いている人たちは居たのだけれど、 これらは楕円と楕円の外側の点を考えて、 それを内側にも適用する、というものなので、目的のものとは異なっている。 模様としては面白いけれど、枠線を書くための距離フィールドとしては使うことができない。 参考 https://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm で、仕方ないので自分で求めることにしたら、最終的にこうなった。 $$ \frac{x^2}{(c + d)^2} + \frac{y^2}{d^2} = 1 $$ $x, y$ は楕円内の座標、$c$ は 長軸 - 短軸、$d$ が目的の距離になる。 一見シンプルな式だけれど、$d$ について考えると、四次方程式になる。 変数を置き換えて式を変形すれば三次の項は消せるけれど、一次の項が消えないので、 複二次方程式には落とせない。 で、[フェラーリの解法](https://ja.wikipedia.org/wiki/%E5%9B%9B%E6%AC%A1%E6%96%B9%E7%A8%8B%E5%BC%8F)で解こうとしたのだけれど、結論から言うと失敗だった。 計算途中の値の幅が大きすぎて、double (精度 53 bit相当)でさえ誤差が大きすぎて使い物にならない。 ピクセルシェーダ側の浮動小数点数は最低精度 10 bit 程度なので、 ちょっとやそっと頑張ったくらいでは GPU には持ち込めなさそう。 どうにか計算誤差を低減できないものかとあれこれ試しはしたものの、ほとんど効果は得られなかった。 結局、代数的に求めるのは諦めて、回数固定の[ニュートン法](https://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%88%E3%83%B3%E6%B3%95)で求めた。 楕円が円からかけ離れるほど収束が遅くなるけれど、とりあえず 8 回程度回せば、 見た目上問題ない程度の精度はでた。12 回ほど回すと、精度的にほぼ正しい値になるようだ。 と、ここ最近はこんなことばかりしていた。 そして作ったはいいが、そこそこコストが高くつくので、 矩形にテクスチャ張り付けるだけで良い時(ゲームの背景やキャラとか)と、角丸などの形状をきれいに描画したい時(UIなど)では、 レンダリングパス自体を分けたほうがよさそうだなあと思う今日この頃。