カーソルをアクティブウィンドウのクライアント領域に制限する
カーソルをアクティブウィンドウのクライアント領域に制限するには、Win32 APIの ClipCursor 関数を使うのですが、なかなか期待する動作をしてくれません。
多くのサイトでは、ウィンドウのアクティブ/非アクティブ状態をWM_ACTIVATEで検出し、クリッピングの開始と終了を行えばよい、と解説されています。しかしこの方法だけだと、ウィンドウを非アクティブな状態から「タイトルバーをクリックして」アクティブ状態にした場合、カーソルがデスクトップ全体を移動できる状態になってしまいます。
きちんと調べたわけではないのですが、どうもWM_ENTERSIZEMOVE~WM_EXITSIZEMOVEの間で勝手にClipCursorの設定が無効化されているようです。
ということで、WM_EXITSIZEMOVEの中でもClipCursorします。これにより、ウィンドウの移動を終えた後に、カーソルをクライアント領域にクリッピングできるようになります。
Haskell@Windowsのコンソールで日本語が文字化けする話の対策
コンソールのコードページがCP932なので、最終的にCP932に変換しなければならないのですが、どうせWindowsでしか起こらない問題なので、WindowsのAPIを使っちゃいます。
import Data.Word import Foreign.Ptr import Foreign.C.Types import Foreign.C.String import Foreign.Marshal.Alloc import System.IO.Unsafe foreign import stdcall "WideCharToMultiByte" c_WideCharToMultiByte :: Int -> -- CodePage Word32 -> -- dwFlags Ptr CWchar -> -- lpWideCharStr Int -> -- cchWideChar Ptr CChar -> -- lpMultiByteStr Int -> -- cchMultiByte Ptr CChar -> -- lpDefaultChar Ptr Bool -> -- lpUsedDefaultChar IO Int toCP932 :: String -> String toCP932 cs = unsafePerformIO $ do withCWStringLen cs (\(src,srcLen) -> do dstLen <- c_WideCharToMultiByte 0 0 src srcLen nullPtr 0 nullPtr nullPtr allocaBytes dstLen (\dst -> do c_WideCharToMultiByte 0 0 src srcLen dst dstLen nullPtr nullPtr peekCStringLen (dst,dstLen))) main = putStrLn $ toCP932 "ほげ"
Frequency Domain Normal Map Filtering実装
少し忙しくて放置してたのですがFrequency Domain Normal Map Filteringを実装しました。
画像はそれぞれ次をあらわしていて、左上をリファレンス画像だと思って見ます。
通常のバンプマップの32倍スーパーサンプリング | 通常のバンプマップ |
NDFをmovMFで近似 | NDFをSHで近似 |
バンプマップはこんな感じの単純なものを利用しています。
モデルはStanford Bunnyに球面UV展開を適用して出力したやつです。球面UV展開なのでウサギの背のところで山と谷が密集していますが、ここでスーパーサンプリングしていない通常のバンプマップは法線が潰れて真っ白になってしまっているのが一目瞭然ですね。
別の角度から。ウサギの尻。
拡大していると他との違いが全然分からないのですが、縮小すると違いが明確に出ます。
pixel shaderでshared memoryって使えるの?
ここまで最適化を進めてきたのですが、これ以上はpixel shaderでは高速化は無理な気がしてきました…
ループに[unroll]を付けたり階乗計算を漸化式的に書き直したりした結果、アセンブリコードの大半は積和計算になりました。それでも遅いということは、レジスタ数が多すぎるか、定数バッファへのアクセスのレイテンシが大きいのか、そのあたりが問題なのではないかと見ています。
よく知らないのですが、DX11のpixel shader内のcbufferはconstant memory領域に割り振られるのでしょうか? そうだとすると、BRDFや入射光の球面調和係数をshader memory領域に割り振ることで劇的に速くなるかもしれません。
ただ、pixel shaderでshared memoryを使う方法が分かりません…。というか、そもそも使えないのかもしれません。もし使えないとすると、pixel shaderは必要最小限の情報をUAVとかで出力して、それをcompute shaderで処理するのでしょうか。そうなるとdeferred rendering的な形のほうが良いことになるのかなあ。
球面調和関数の練習
やったこと
やっと時間取れたので実装してみました。上段はリファレンスとなる通常の照明計算、下段はそれを球面調和関数の係数空間で計算しました。左から順にそれぞれ次のような照明計算を行いました。
- Lambertian BRDF * DirectionalLight
- Phong BRDF * DirectionalLight
- Lambertian BRDF * ConstantLight
- Lambertian BRDF * (DirectionalLight + ConstantLight) + Phong BRDF * DirectionalLight
Lambertian BRDFやPhong BRDFの球面調和関数展開は「Lambertian Reflectance and Linear Subspaces(Basri and Jacobs)」にあるので、こちらを参考にします。平行光源はデルタ分布の光源と考えれば球面調和関数展開の係数が求まります。定数光源のほうはだけ使って展開すればいいです。
疑問など
シェーダ内でを必要とするのですが、どうやって与えるのが普通なんだろう。今回はシェーダ内で漸化式を解きながら計算したけど、どうも処理が重かった…。
あと、Phong BRDFでshininessが32とかになると、球面調和関数を次数15くらいまで伸ばさないとまともに近似できないらしい。次数15になると256項も必要になります。うーむ。
Frequency Domain Normal Map Filtering続き^4
※式が違ったので修正(12/24)
セクション4.3の次の式
について、次のようにそれぞれ球面調和関数展開できるとき、
その係数が次のようになる、
というこの部分について流し読みしていたのですが、よくよく考えてみると、よくある球面調和関数の内積と違ってを含んでいるため、全然分からないことに気づいてしまいました。
というわけで何故これが成り立つのか調べていたのですが、これはLambertian Reflectance and Linear Subspaces (Basri and Jacob, IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE, VOL. 25, NO. 2, FEBRUARY 2003)に背景が説明されていました。この論文によれば、kが放射的に対称でzonal harmonicsで展開できるとき、次が成り立つそうです(Funk-Hecke Theorem)。
つまり、言い換えると「何も解決してねえ!」という感じなのですが、法線分布関数だけ先に展開してFunk-Hecke Theoremを適用すれば上記式が求まることは分かります。なので次はこの定理を掘り下げれば良さそうなのですが、どうもこの定理を深追いするのはかなり大変であるようです。しがないプログラマとしてはツールとして使うのが良さそうですね…。