珍しくプログラムと数学が混じったネタで行きたいと思います。 今回はマクローリン展開を多用するのでその辺が分からない人は結果だけ見るといいと思います。 なお、この話についてはシェーダモデル3.0までの話です。4.0以降の場合で三角関数を使うとあっさりsincos命令を使っています。
HLSLでシェーダモデル3.0までで三角関数を使うと大半は近似式になる
WindowModePatchの更新記事でも少し触れましたが、HLSLでシェーダモデル3.0まででsinやcosを使っても大半は近似式になります。
理由は推察ですが、
- シェーダ命令であるsinやcosを計算する命令のsincosはシェーダモデル3.0のまで場合スカラ値のみの対応。(シェーダモデル4.0の場合はレジスタの各要素すべてでできる)
- sincosの命令スロット数は8(PixelShader2.0時)であり、しかも角度は-π~+πでなければならないという制約を守ると演算数がかさむ(シェーダモデル4.0の場合は制約はほぼない)
が効いているのだと思います。おかげでシェーダモデル4.0を使うと普通に書くだけで済むのですが、3.0の場合は近似式を使われる、というわけです。基本的にDirectX9までで書こうとすると最大のシェーダモデルは3.0なので制約にかかってしまったわけですね・・・。
近似式の計算方法について
シェーダアセンブラ上でsinやcosを計算する場合にシェーダモデル2.0の場合の説明で変な定数が出てきますが、これが対象です。 まずは上記制約のうち後ろの制約より、角度の範囲を切りそろえる作業です。このやり方は簡単で
という式を使います。式の中に出てくる[]は小数部のみを取り出す演算(シェーダならfracという命令)です。
そしてsinやcosの計算です。これで角度の範囲が決まっているのでマクローリン展開を素直に使います。Σを使わないで書くと
となります。が、sinを使うことはまずありません。なぜならxの乗算が単体で存在してしまうためです。cosであればxの偶数乗の項しかないのであらかじめ二乗を計算しておけば
となり一件落着です。しかも
Sinc関数の近似について
Sinc関数の定義はLanczosの説明回でもあったとおり
となります。で、これを上記のマクローリン展開に当てはめて計算すると
となり、展開式の形がcosの係数違いになるわけです。もちろん、sincの場合は角度の範囲が限られているわけではないのですが、角度の範囲を限定することができるならこちらで計算した方が良さそうだと思われます。
また、0付近の近似精度もこちらの式を使った方がcosから計算した値より精度は良くなります。
これを使ってLanczosのシェーダを書き直した
いろいろと現象を確認してSinc関数をマクローリン展開に従って書き直したわけですね。 いくつかの条件下ではこの近似精度の問題が響くことがあるので演算数は多少増えますが良いと思います。個人的には。