はじめに
Temporal AAアルゴリズムは前フレームに描画された情報をサンプルとして利用する、時間方向でのスーパーサンプリング・アンチエイリアス・アルゴリズムです。Temporal AAの実装方法自体は幾つかあるみたいですが、前フレームと現在フレームの透視投影変換行列をジッタさせ、ブレンドして表示するという単純なアルゴリズムを自分は実装しました。この種類のTemporal AAはBeyond3Dのフォーラムでも話題になっています(http://forum.beyond3d.com/showthread.php?t=46241)。
Nao_uさんもTemporal AAを実装されています。http://game.g.hatena.ne.jp/Nao_u/20100924でNao_uさんのTemporal AA実装のスクリーンキャプチャーや冷静で的確な考察が読めます。
いつものようにプログラムのバイナリとソースが置かれているCodePlexページへのリンクを記事の最後の方に貼っています。
Halo: Reach Temporal AA
http://www.eurogamer.net/articles/digitalfoundry-halo-reach-tech-analysis-article (AAはページ3で取り上げられています。)
Digital Foundryが最近Halo: ReachをTech Analysisで取り上げました。いつもように自社がメディアや開発者に販売している機材を利用して、Halo: Reachを解析していますが、ゲーム内でのアンチエイリアスの特性から、Halo: Reachでのアンチエイリアスは何らかのTemporal AAだと話題にされています。
http://d.hatena.ne.jp/yakiimo02/20100920/1284950755
Digital Foundryの記事に関する自分の日記です
http://d.hatena.ne.jp/yakiimo02/20100923/1285230579
Halo: Reachを実際に購入してプレイした自分のアンチエイリアスに対する感想です。
CryEngine3 Temporal AA
http://advances.realtimerendering.com/s2010/index.html
Siggraph 2010の「Advances in Real-Time Rendering in 3D Graphics and Games」コースの「CryENGINE 3: Reaching the Speed of Light」でCryEngine3でのAA実装についての紹介がありました。CryEngine3では手前の物体と遠方の物体に別々のAAアルゴリズムが適用されるハイブリッドな手法を採用しているそうです。手前の物体に関してはEdge検出ベースのポストプロセスAAが適用され、遠方の物体に関してはTemporal AA(the temporal reprojection with cache miss手法)が適用されるそうです。
Crytekの公開資料でtemporal reprojectionのレファレンスとして下の二つのペーパーが上げられていました。
Accelerating Real-Time Shading with Reverse Reprojection Caching (ACM SIGGRAPH Symposium on Graphics Hardware 2007)
http://www.cse.ust.hk/~psander/
http://www.cse.ust.hk/~psander/docs/reproj2.pdf (pdf)
and
Spatio-Temporal Upsampling on the GPU (I3D 2010)
http://www.mpi-inf.mpg.de/~rherzog/
http://www.mpi-inf.mpg.de/~rherzog/Papers/spatioTemporalUpsampling_preprintI3D2010.pdf (pdf)
http://d.hatena.ne.jp/yakiimo02/20100921/1285088322
CryEngine3 Temporal AAに関する自分のはてな日記記事です。
スクリーンショット

No AA

MSAA 2x

MSAA 4x

Temporal AA 2x
スクリーンショットに対してのコメント
(2010/10/02追加)
http://www.yakiimo3d.com/ja/2010/10/02/temporal-aa-texture-mipmap-settings-2/
上の画像をキャプチャーした時、texture filteringのコードに設定ミスがあったみたいです。texture filteringが正しく適用された場合は、texture内のaliasingがなくなります。Mipmapが普通に適用されている場合はtextureのaliasingに関してはtemporal AAの効果は余りないみたいです。
自分の今回実装したTemporal AAは2サンプルなので、2サンプルのSSAAと同じくらいのクオリティーのはずです。アンチエイリアスの比較用に、ハードウェアのMSAA 2xとMSAA 4xも実装しました。上記スクリーンショットをみて分かると思いますが、長いエッジに関してはMSAA 2xとTemporal AA 2xは同じぐらいのアンチの効き具合です。MSAA 4xの方がTemporal AA 2xとMSAA 2xより長いエッジに関してはジャギーが少なく、綺麗だと思います。テクスチャの内部を見た場合だと、MSAA 2x、MSAA 4xは余り効果がないのに対して、Temporal AA 2xの方は直線がつながり、全体的にaliasingが軽減されているみたいです。Temporal AAは一種のsupersamplingなのでedgeのjaggyだけでなく、ポリゴン表面上のaliasingも軽減してくれます。ただし上記画像のようにtexture aliasingに関してはhardwareのtexture filterがaliasingを軽減してくれるので、簡単に大きい改善を見つける事は難しいようです。ポリゴン表面上のPixel shader aliasingをサンプルと用意した方がよかったです。
自分の日記に格子状パターンのテクスチャの場合の各アルゴリズムのスクリーンショットをアップしました。MSAA 2x、4xはどちらも余り効果がないのに対して、テクスチャ内部のディテールが多い、高周波なテクスチャに関してはTemporal AAは効果が大きいみたいです。http://d.hatena.ne.jp/yakiimo02/20100926/1285523408
MSAAはpixel shaderの描画結果のサンプルは1個しか使用しないですが、SSAA的なTemporal AAでは複数個のpixel shaderサンプルが使用されます。Temporal AAの場合だとある意味画面サイズより高い解像度で実行されているので、細かいディテールに対してアンチエイリアスがかかります。
今回実装したTemporal AAは処理負荷が軽く、静止画には対しては効果バツグンですが、カメラが動き始めると(特に横方向移動)、画面がぼやけてしまう欠点があります。前フレームと現在フレームの差分が大きくなると、ブレンドした時にアンチがかかるというより、残像がかかる風になってしまうみたいです。
実装の詳細
http://glprogramming.com/red/chapter10.html
OpenGL赤本の第10章にaccumulation buffer AAアルゴリズムが説明されています。
http://www.cse.msu.edu/~cse872/tutorial5.html
OpenGL赤本のaccumulation buffer AAアルゴリズムの説明はMichigan State大学のCSE 872 Advanced Computer Graphics授業の上記チュートリアルを読んで知りました。
OpenGL赤本に透視投影変換行列のジッタとOpenGL accumulation bufferを使用した、アンチエイリアスの実装方法の説明が書かれています。アルゴリズムの手順としては、シーンをn-sample回再描画し、毎回の描画時に透視投影変換行列をsubpixel分だけジッタし、結果をaccumulation bufferにブレンドします。ジッタしたsubpixelをn-sample分ブレンドしているので、結果の画像にはSSAA相当なアンチエイリアスがかかりますが、1フレーム内にシーンをn-sample回再描画する必要がある為、多くのリアルタイムアプリにとっては処理負荷が高すぎる手法だと思います。
今回のTemporal AAの実装の考え方はOpenGL accumulation bufferを使用した透視投影変換行列をジッタするアンチと同じですが、処理負荷を軽減する為に、1パス内では1回のシーン描画におさえ、前フレームのサンプルを流用するようにします。自分のTemporal AA実装は前フレームのサンプルと現在フレームのサンプルの2サンプルを使用したAAです。メモリ的には前フレームのカラーバッファを保持するために、フレームバッファと同じサイズのレンダーターゲットが一つ余分に必要となります(現在フレームのレンダーターゲットがすでに必要で無料と考えた場合)。偶数と奇数フレームで違うsubpixelジッタ量を利用して、シーンを描画し、前フレームと現在フレームの描画結果をフルスクリーンのポストプロセスパスでブレンドします。OpenGLのaccumulation buffer AAと同じようにTemporal AA 2xはSSAA 2xと同等な描画結果のはずです。今回の実装で自分が悩んだ点としては、どのようにして透視投影変換行列を加工すればscreen space座標系でのsubpixelのジッタが表現できるのかという事でしたが、下にその計算を行う関数のコードを貼っておきます(ソースを落としてもらえれば、実装は全部みれますが。)
// From temporalAA.cpp
/**
Based on the OpenGL Red Book Chapter 10.
http://glprogramming.com/red/chapter10.html
*/
D3DXMATRIX TemporalAA::JitteredFrustum(float left, float right, float bottom,
float top, float fNear, float fFar, float pixdx,
float pixdy, float eyedx, float eyedy, float focus, const CameraInfo& cameraInfo) const
{
float xwsize, ywsize;
float dx, dy;
xwsize = right - left;
ywsize = top - bottom;
// translate the screen space jitter distances into near clipping plane distances
dx = -(pixdx*xwsize/cameraInfo.fBackBufferW +
eyedx*fNear/focus);
dy = -(pixdy*ywsize/cameraInfo.fBackBufferH +
eyedy*fNear/focus);
D3DXMATRIX mPerspective;
D3DXMatrixPerspectiveOffCenterLH( &mPerspective, left + dx, right + dx, bottom + dy, top + dy, fNear, fFar );
return mPerspective;
}
Perspective Matrix Jittering Temporal AA Demo
今回のデモではDirectX June 2010 SDKの「powerplant」 sdkmeshモデルを使用しています。DX11 Cascaded Shadowmapsのデモで使用されているモデルです。
デモ中にカメラを移動させていたら描画ががたつく時があります。DXUTのCFirstPersonCameraを使用していますが、Cascaded Shadowmapsデモでも同じような現象が発生します。自分の実装のどこかに問題があるのかもしれませんが、Temporal AAのアルゴリズムの問題ではないです。
Source Code & Binary
http://yakiimo3d.codeplex.com/releases/view/53001
最後に
今回実装したTemporal AAでは実装が比較的容易で、軽い処理負荷でSSAA 2x同等のアンチエイリアスが実現できました。しかし、カメラが動き始めると画面がぼやけてしまい、移動中はTemporal AAを切るなりなどをしないと実用的ではないです。Crytekが採用しているTemporal Reprojectionを使用したTemporal AAは未実装なので、そちらも実装して今回の実装と比較してみるといいかもしれないと思いました。