なにも知らなすぎるので、コンパイルできないと悩みまくる
ひとまず
import Listは
import Data.List
にする見たい。
パターンマッチの
prefixp line = pattern `isPrefixOf` line
の`isPrefixOf`はshift+7の'では、もちろん無く
`(shift+@)のほうでした。。。
2011年11月28日月曜日
import Systemでコンパイルエラーが出る
ふつうのHaskellプログラミングのecho.hsのところで
import System
がコンパイル通らないのでググルと
http://tnomura9.exblog.jp/14966671/
import System.Environment (getArgs)
このように記述する旨書いてありました。
ghcのバージョンのせいなのか
The Glorious Glasgow Haskell Compilation System, version 7.2.2
なにがなんだか理解していませんが、ひとまず先に進めそうです。
よくみると
import System.Environment (getArgs)
じゃなくて
import System
の代わりに
import System.Environment
でいいっぽい
本の時代から
Systemが細分化されたのかな?
import System
がコンパイル通らないのでググルと
http://tnomura9.exblog.jp/14966671/
import System.Environment (getArgs)
このように記述する旨書いてありました。
ghcのバージョンのせいなのか
The Glorious Glasgow Haskell Compilation System, version 7.2.2
なにがなんだか理解していませんが、ひとまず先に進めそうです。
よくみると
import System.Environment (getArgs)
じゃなくて
import System
の代わりに
import System.Environment
でいいっぽい
本の時代から
Systemが細分化されたのかな?
2011年11月17日木曜日
ふつうのHaskellプログラミングのUS-states.txt
US-states.txtに近いものを用意してみましたw
AK Alaska - Juneau
AL Alabama - Montgomery
AR Arkansas - Little Rock
AS American Samoa
AZ Arizona - Phoenix
CA California - Sacramento
CO Colorado - Denver
CT Connecticut - Hartford
DC District of Columbia, which is the seat of the Federal government
DE Delaware - Dover
FL Florida - Tallahassee
GA Georgia - Atlanta
GU Guam
HI Hawaii - Honolulu
IA Iowa - Des Moines
ID Idaho - Boise
IL Illinois - Springfield
IN Indiana - Indianapolis
KS Kansas - Topeka
KY Kentucky - Frankfort
LA Louisiana - Baton Rouge
MA Massachusetts - Boston
MD Maryland - Annapolis
ME Maine - Augusta
MI Michigan - Lansing
MN Minnesota - Saint Paul
MO Missouri - Jefferson City
MP Northern Mariana Islands, Commonwealth
MS Mississippi - Jackson
MT Montana - Helena
NC North Carolina - Raleigh
ND North Dakota - Bismarck
NE Nebraska - Lincoln
NH New Hampshire - Concord
NJ New Jersey - Trenton
NM New Mexico - Santa Fe
NV Nevada - Carson City
NY New York - Albany
OH Ohio - Columbus
OK Oklahoma - Oklahoma City
OR Oregon - Salem
PA Pennsylvania - Harrisburg
PR Puerto Rico, Commonwealth
RI Rhode Island - Providence
SC South Carolina - Columbia
SD South Dakota - Pierre
TN Tennessee - Nashville
TX Texas - Austin
UT Utah - Salt Lake City
VA Virginia - Richmond
VI the United States Virgin Islands
VT Vermont - Montpelier
WA Washington - Olympia
WI Wisconsin - Madison
WV West Virginia - Charleston
WY Wyoming - Cheyenne
2011年10月16日日曜日
マネージメント&ディレクション
直近のプロジェクトでプロジェクトの運用の方面に近い箇所を担当する事になって、今までプロジェクトを回してもらってきて不満に思っていた箇所がどういう事で発生するのかわかってきた気がします。
一番思ったことは、プロジェクトの方針がだんだんぼやけてくる事。
あれ?聞いていた事と違う事が発生している?と疑問に思うことが増えてくると、だんだん不信感が高まってきます。
運営側がすべてを隠蔽するくらいのものならばそれでいいのかもしれませんが、現場に叱咤激励を飛ばしてくる場合にだんだん失敗の尻拭いをさせられている気になってきます。
個人的に簡単で効果があることは、プロジェクトの現在の方針を細かいPDCのイテレーションのなかのPで更新共有することではないかと思います。
マネージメントなどを一人で行なっている場合にどうしてもPDDDDCになってしまいがちかと思います。
それを全員でPDCを共有することで抜けやミスをする機会を減らせるのでは無いかと思います。
そうすれば、結果的にPを忘れてんぞ!とかCして無いけどおかしいなとなってくるのではないかな。
結構現場ではCしてないってことには気がついているものです。
ほったらかされているのが、まさにCしていないってことです。
この際に、まあいいか。知らねー。なんでやらないのか!と心で思っているだけではなく、気づいた人からアクションを行えるようになったらいいな。と思います。
とにかく、何かの役割を人に背負わせるのは、その人が失敗したときに責めて終わりというわけには行きません。
意識していきたいなー。
2011年6月23日木曜日
SSAO僕にも出来たよ!
シェーダプログラムをやっておかないとなと思って、さしあたってSSAOにロマンを感じていたので挑戦してみました。
シェーダの前提が全然わからず、かなり辛かったのですがいろいろ協力いただいてひとまず表示までこぎつけました。
実装方法です。初心者向けです。
まずフレームワークというかウインドウの初期化やDirectXの初期化は適当に済ませてください。
まるぺけさんのhttp://marupeke296.com/TIPS_UltraShortDirectXProg.htmlとかいいと思います。
プログラムの方は以下の雰囲気です。
makeRayMap関数は以下のとおりです。
コメントアウトしている方が本来のレイの作成方法なのですが、拾ってきたベクトル群の方が見た目いい感じだったので、そっちにしました。
これは
http://d.hatena.ne.jp/shuichi_h/20100318
から拝借したもので、コードの説明自体は同じことです。
SSAO.fx
はじめのバーテックスシェーダとピクセルシェーダは通常のモデル描画&カラー、法線深度値出力用
シェーダの前提が全然わからず、かなり辛かったのですがいろいろ協力いただいてひとまず表示までこぎつけました。
![]() |
ランバートライティングのみで光源がちょうど反対側でしょうか。 のっぺりしているところを撮りました。 |
![]() |
同じ角度でSSAOを利かせますと、陰影がディティール感出ますね。 |
まずフレームワークというかウインドウの初期化やDirectXの初期化は適当に済ませてください。
まるぺけさんのhttp://marupeke296.com/TIPS_UltraShortDirectXProg.htmlとかいいと思います。
プログラムの方は以下の雰囲気です。
ID3DXEffect* pEffect; D3DXMATRIX View, Proj; LPDIRECT3DSURFACE9 backbuffer; LPDIRECT3DTEXTURE9 texColor; LPDIRECT3DSURFACE9 surColor; LPDIRECT3DTEXTURE9 texNormalDepth; LPDIRECT3DSURFACE9 surNormalDepth; LPDIRECT3DTEXTURE9 rayMap; // エフェクトの読み込み LPD3DXBUFFER pErr = NULL; HRESULT hr = D3DXCreateEffectFromFile( d3ddev, _T("SSAO.fx"), NULL, NULL, D3DXSHADER_DEBUG, NULL, &pEffect, &pErr); if( FAILED( hr ) ) { if( pErr != NULL ) { // コンパイルエラー出力 const char *err = (char*)pErr->GetBufferPointer(); pErr->Release(); } } // バックバッファを覚えとく d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); // SSAOで使う。レイマップ/法線深度マップ/通常のカラーマップを作成します // GetScreenWidth/GetScreenHeightはスクリーンサイズを取ってくるのを作ってください d3ddev->CreateTexture(GetScreenWidth(), GetScreenHeight(), 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT, &texColor, 0); texColor->GetSurfaceLevel(0, &surColor); d3ddev->CreateTexture(GetScreenWidth(), GetScreenHeight(), 1, D3DUSAGE_RENDERTARGET, D3DFMT_A32B32G32R32F,D3DPOOL_DEFAULT, &texNormalDepth, 0); texNormalDepth->GetSurfaceLevel(0, &surNormalDepth); // 16x16のレイマップを作成しますmakeRayMapという // コールバック関数を自分で用意してテクスチャに書き込んじゃいます d3ddev->CreateTexture(16, 16, 1, 0, D3DFMT_A32B32G32R32F, D3DPOOL_MANAGED, &rayMap, 0); D3DXFillTexture(rayMap, makeRayMap, 0); // ビュー変換・射影変換 D3DXMatrixPerspectiveFovLH( &Proj, D3DXToRadian(45), static_castで初期化をしておきます。(GetScreenWidth())/static_cast (GetScreenHeight()), 1.0f, 10000.0f);
makeRayMap関数は以下のとおりです。
コメントアウトしている方が本来のレイの作成方法なのですが、拾ってきたベクトル群の方が見た目いい感じだったので、そっちにしました。
static void WINAPI makeRayMap(D3DXVECTOR4* pOut, const D3DXVECTOR2* pTexCoord, const D3DXVECTOR2* pTexelSize, void* data) { /* float r = 1.0f * (float)rand() / (float)RAND_MAX; float t = 6.2831853f * (float)rand() / ((float)RAND_MAX + 1.0f); float cp = 2.0f * (float)rand() / (float)RAND_MAX - 1.0f; float sp = sqrt(1.0f - cp * cp); float ct = cos(t), st = sin(t); pOut->x = abs(r * sp * ct); pOut->y = abs(r * sp * st); pOut->z = abs(r * cp); pOut->w = 0; */ static int i=0; D3DXVECTOR3 vec; switch(i%16) { case 0: vec = D3DXVECTOR3(0.53812504f, 0.18565957f, -0.43192f); break; case 1: vec = D3DXVECTOR3(0.13790712f, 0.24864247f, 0.44301823f); break; case 2: vec = D3DXVECTOR3(0.33715037f, 0.56794053f, -0.005789503f); break; case 3: vec = D3DXVECTOR3(-0.6999805f, -0.04511441f, -0.0019965635f); break; case 4: vec = D3DXVECTOR3(0.06896307f, -0.15983082f, -0.85477847f); break; case 5: vec = D3DXVECTOR3(0.056099437f, 0.006954967f, -0.1843352f); break; case 6: vec = D3DXVECTOR3(-0.014653638f, 0.14027752f, 0.0762037f); break; case 7: vec = D3DXVECTOR3(0.010019933f, -0.1924225f, -0.034443386f); break; case 8: vec = D3DXVECTOR3(-0.35775623f, -0.5301969f, -0.43581226f); break; case 9: vec = D3DXVECTOR3(-0.3169221f, 0.106360726f, 0.015860917f); break; case 10:vec = D3DXVECTOR3(0.010350345f, -0.58698344f, 0.0046293875f); break; case 11:vec = D3DXVECTOR3(-0.08972908f, -0.49408212f, 0.3287904f); break; case 12:vec = D3DXVECTOR3(0.7119986f, -0.0154690035f, -0.09183723f); break; case 13:vec = D3DXVECTOR3(-0.053382345f, 0.059675813f, -0.5411899f); break; case 14:vec = D3DXVECTOR3(0.035267662f, -0.063188605f, 0.54602677f); break; case 15:vec = D3DXVECTOR3(-0.47761092f, 0.2847911f, -0.0271716f); break; } // テクスチャなので0-1の値しか持てないので、-1,1を0,1へ変換 pOut->x = (vec.x+1.f)*0.5f; pOut->y = (vec.y+1.f)*0.5f; pOut->z = (vec.z+1.f)*0.5f; pOut->w = 0.f; ++i; }毎フレのループが以下の感じです。
float f,l; // 毎フレ動かしたかったので適当な変数 struct d3dverts { float x, y, z, w; float u, v; enum { fvf = D3DFVF_XYZRHW | D3DFVF_TEX1 }; }; d3ddev->BeginScene(); f+=dt; // もちろん適当でdtはデルタタイムをどっかから持ってくる l=sin(f)*2.f+300.f; D3DXMATRIX mat; D3DXMatrixLookAtLH( &View, &D3DXVECTOR3(l*sin(f),5.f,-l*cos(f)), &D3DXVECTOR3(0,0,0), &D3DXVECTOR3(0,1,0) ); D3DXMatrixIdentity( &mat ); mat = mat * View * Proj; // シェーダ内で使う変数更新 pEffect->SetMatrix( "m_WVP", &mat ); pEffect->SetVector( "m_LightDir", &D3DXVECTOR4(1,1,1,0) ); pEffect->SetVector( "m_Ambient" , &D3DXVECTOR4(0.5,0.5,0.5,0)); // MRTをつかって普通のカラーレンダリングと法線と深度値のテクスチャを // それぞれ surColor(texColor)/surNormalDepth(texNormalDepth)に出力する d3ddev->SetRenderTarget(0, surColor); d3ddev->SetRenderTarget(1, surNormalDepth); d3ddev->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0, 1.0f, 0); // 描画開始 pEffect->SetTechnique( "RenderScene0" ); UINT numPass; pEffect->Begin( &numPass, 0 ); pEffect->BeginPass(0); // ここはどうにかメッシュでもモデルデータを表示する処理を作っておいてください for(auto ite = m_kMeshObjectList.begin(); m_kMeshObjectList.end()!=ite; ite++) { (*ite)->Draw(); } pEffect->EndPass(); // ここまででカラーと法線深度テクスチャの完成 // ここから上で作ったテクスチャをもとに画面に描画する d3ddev->SetRenderTarget(0, backbuffer); d3ddev->SetRenderTarget(1, NULL); // これやらないと表示されない!! d3ddev->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0, 1.0f, 0); // それぞれ0,1,2の順番でそれぞれのテクスチャを設定する d3ddev->SetTexture(0, rayMap ); d3ddev->SetTexture(1, texNormalDepth ); d3ddev->SetTexture(2, texColor ); // SSAOはピクセルシェーダだけなのでバーテックスシェーダは走らせない pEffect->BeginPass(1); // 画面いっぱいに4角ポリゴンを表示してピクセルシェーダを走らせる // この時 d3dverts で適宜した D3DFVF_XYZRHW がヴァーテックスシェーダ走らせなくても計算済みの頂点(4角ポリ) // だよと定義するものらしいです。 d3ddev->SetFVF( d3dverts::fvf ); const d3dverts quadverts[4] = { { -0.5f+0.f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f}, { -0.5f+GetScreenWidth(), -0.5f, 0.0f, 1.0f, 1.0f, 0.0f}, { -0.5f+0.f, -0.5f+GetScreenHeight(), 0.0f, 1.0f, 0.0f, 1.0f}, { -0.5f+GetScreenWidth(), -0.5f+GetScreenHeight(), 0.0f, 1.0f, 1.0f, 1.0f}, }; d3ddev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quadverts, sizeof(d3dverts)); pEffect->EndPass(); pEffect->End(); d3ddev->EndScene(); d3ddev->Present( NULL, NULL, NULL, NULL );以下がシェーダです。
これは
http://d.hatena.ne.jp/shuichi_h/20100318
から拝借したもので、コードの説明自体は同じことです。
SSAO.fx
はじめのバーテックスシェーダとピクセルシェーダは通常のモデル描画&カラー、法線深度値出力用
float4x4 m_WVP; float4 m_LightDir; float4 m_Ambient = 0.0f; struct VS_OUTPUT { float4 Pos : POSITION; //頂点の座標 float4 Col : COLOR0; //頂点カラー float4 depth : COLOR1 ; float3 normal : COLOR2 ; }; VS_OUTPUT VS( float4 Pos : POSITION, //頂点の座標 float4 Normal : NORMAL ) //法線ベクトル { VS_OUTPUT Out = (VS_OUTPUT)0; Out.Pos = mul( Pos, m_WVP ); float3 L = -normalize( m_LightDir.xyz ); float3 N = normalize( Normal.xyz ); Out.Col = max( m_Ambient, dot(N, L) ); // 深度値は座標そのもの Out.depth = Out.Pos; // 法線 Out.normal = Normal.xyz; Out.normal = normalize(Out.normal); return Out; } struct SP_OUTPUT { float4 Color : COLOR0; // カラー float4 NormalDepth : COLOR1; // 法線と深度 }; SP_OUTPUT PS( VS_OUTPUT In ): COLOR0 { SP_OUTPUT Out = (SP_OUTPUT)0; Out.Color = In.Col; Out.NormalDepth.xyz = In.normal; // xyzが法線で Out.NormalDepth.w = In.depth.z/In.depth.w; // wに深度 return Out; } float totStrength = 1.38; float strength = 0.0007; float offset = 18.0; float falloff = 0.000002; float rad = 0.03; #define SAMPLES 16 const float invSamples = 1.0/SAMPLES; sampler rayMap : register(s0) = sampler_state { MipFilter = NONE; MinFilter = POINT; MagFilter = POINT; }; sampler normalMap : register(s1) = sampler_state { MipFilter = NONE; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler colorMap : register(s2) = sampler_state { MipFilter = NONE; MinFilter = LINEAR; MagFilter = LINEAR; }; float4 SSAO( float2 uv : TEXCOORD0) : COLOR0 { float4 Output = (Float4)0; float4 currentPixelSample = tex2D(normalMap, uv); float currentPixelDepth = currentPixelSample.a; float3 norm = currentPixelSample.xyz * 2.0f - 1.0f; float bl = 0.0; float radD = rad / currentPixelDepth; float3 ray, occNorm; float2 se; float occluderDepth, depthDifference, normDiff; for(int i=0; i'<'SAMPLES; ++i) { float3 fres = tex2D(rayMap, float2(float(i)/16.f,0)).xyz*2.f-1.f; ray = radD * fres; se = uv + sign(dot(ray,norm)) * ray * float2(1.0f, -1.0f); float4 occluderFragment = tex2D(normalMap, se.xy); occNorm = occluderFragment.xyz * 2.0f - 1.0f; depthDifference = currentPixelDepth - occluderFragment.a; normDiff = (1.0 - dot(normalize(occNorm), normalize(norm))); bl += step(falloff, depthDifference) * normDiff * (1.0 - smoothstep(falloff, strength, depthDifference)); } float ao = 1.0 - totStrength * bl * invSamples; Output = tex2D(colorMap, uv)*ao; return Output; } technique RenderScene0 { pass P0 { VertexShader = compile vs_3_0 VS(); PixelShader = compile ps_3_0 PS(); } pass P1 { PixelShader = compile ps_3_0 RenderScenePS0(); } }
2011年6月10日金曜日
winmainの隠蔽工作
全体を通しての技術経験をつけたいと思い、
フレームワークを作っていこうと思います。
まずはじめに、プログラムを実行するにあたって一番初めに俺のフレームワークのエントリーポイント的なものを考えます。
マルチプラットホーム向けに作るつもりは無いのですが、ひとまずwinmainから書くのが嫌なので隠蔽します。
どこかのサイトで調べたのですが忘れたので、見つけたら改めて記述します。
こうすると出来るそうです。
まあ、動いています。
とにかく使い方です。
ヘッダファイルにクラス定義
これで、
winmainなど書かなくても、自分のクラスのインスタンスをnewして誰かに渡さなくても、これだけでいきなりPrototypeのMainLoopが実行されるようになります。
からくりは以下です。
Prototypeは以下のFrameworkを継承しています。
こちらにはwinmainが書いてあり以下のようになっています。
ヘッダファイル
とにかくどこかにあるwinmainが呼ばれて起動します、そこでいきなり
しかし、FrameworkはPrototypeが自分を継承していることがわからないのでwinmainで呼ばれるFrameworkは本来誰だかわからないはずです。
ですが、どこかで
めでたくこのグローバルなFrameworkのポインタは継承先のPrototypeを差してくれます。
なので、誰が継承したのか知らない親クラスが継承先のPrototypeに乗っ取られてうごきだしちゃいます。的なイメージでしょうか。
以降、Frameworkを継承したクラスだけ書けば勝手に動いてくれます。
めでたし。
ああ、思い出したMFCの実装がこれです。
フレームワークを作っていこうと思います。
まずはじめに、プログラムを実行するにあたって一番初めに俺のフレームワークのエントリーポイント的なものを考えます。
マルチプラットホーム向けに作るつもりは無いのですが、ひとまずwinmainから書くのが嫌なので隠蔽します。
どこかのサイトで調べたのですが忘れたので、見つけたら改めて記述します。
こうすると出来るそうです。
まあ、動いています。
とにかく使い方です。
ヘッダファイルにクラス定義
class Prototype : public Framework { public: Prototype(void){} virtual ~Prototype(void){} virtual void MainLoop() { //ここに書いたものが自動で実行される } }cppファイル
Prototype theApp;
これで、
winmainなど書かなくても、自分のクラスのインスタンスをnewして誰かに渡さなくても、これだけでいきなりPrototypeのMainLoopが実行されるようになります。
からくりは以下です。
Prototypeは以下のFrameworkを継承しています。
こちらにはwinmainが書いてあり以下のようになっています。
ヘッダファイル
class Framework { public: static Framework* ms_pInstance; Framework(void); virtual ~Framework(void); int Boot(); }cppファイル
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE , LPTSTR lpCmdLine, int nCmdShow) { return Framework::ms_pInstance->Boot(); } Framework* Framework::ms_pInstance = NULL; Framework::Framework(void) { ms_pInstance = this; } Framework::~Framework(void){} int Framework::Boot() { // おれのエントリーポイント MainLoop(); return 0; }
とにかくどこかにあるwinmainが呼ばれて起動します、そこでいきなり
Framework::ms_pInstance->Boot();を呼ぶので継承したPrototypeのMainLoopまで来ます。
しかし、FrameworkはPrototypeが自分を継承していることがわからないのでwinmainで呼ばれるFrameworkは本来誰だかわからないはずです。
ですが、どこかで
Prototype theApp;と記述しておくとグローバル定義のインスタンスになって、これはもちろんコンストラクタよりも早くメモリに存在するわけで。するともちろん、Prototypeのコンストラクタが動くので必然的に親クラスのコンストラクタが呼ばれ、そこでこれまたグローバルな
ms_pInstance = this;Frameworkのポインタに継承した自分(Prototype theApp;で起動したコンストラクタから)のポインタが入って
めでたくこのグローバルなFrameworkのポインタは継承先のPrototypeを差してくれます。
なので、誰が継承したのか知らない親クラスが継承先のPrototypeに乗っ取られてうごきだしちゃいます。的なイメージでしょうか。
以降、Frameworkを継承したクラスだけ書けば勝手に動いてくれます。
めでたし。
ああ、思い出したMFCの実装がこれです。
2011年6月8日水曜日
SSDO Screen Space Directional Occlusion スクリーンスペース近似大域照明
http://www.mpi-inf.mpg.de/~ritschel/SSDO/
概要
リアルタイムで実現できる物理的に妥当な照明は、多くの場合近似を使って実現されています。
ひとつのポピュラーな例としてアンビエントオクルージョン(AO)があり、非常にシンプルでかつ効果的な実装は多くの製品で採用されています。
最近はスクリーンスペースでの近いジオメトリを使用したAOを近似する方法が採られています。
今回この論文で注目すべき点は、この手法が、スクリーンスペースで計算される遮蔽情報から、
例えば方向を考慮した影や間接的な色のにじみなど、より多くの影響を与える遮蔽情報を取得する為に使用することが出来ることです。
今回提案された手法は、スクリーンスペースで処理し従来のSSAOに比べてわずかなオーバーヘッドで直接光、単反射光を近似し、マクロ構造の過程をシミュレートするための別の手法と結合することができます。
そして、最悪のケースでも今回の手法を用いなかったSSAOと見た目的には同等になります。
この手法はスクリーンスペースでの処理になるため、ジオメトリ形状の複雑さに依存しません。
妥当な方向遮蔽と間接照明の効果はリアルタイムに、完全にランタイムで大規模なシーンを表現することが出来ます。
1はじめに
リアルタイムな大域照明は未だ、大規模で動的なシーンに於いて未解決の問題を持っています。
現在のところ十分なフレームレートを達成しているのは、近似を使う方法のみです。
そのような近似には、高速性と、実装の簡易さから映画やゲームなどに使われているAOがあります。
ただしAOは視界と照明を切り離しており、実際の照明の荒い近似のみをおこなっています。
AOは通常、空洞物の暗域を表現できますが、入射光の方向は無視されます。
今回Screen Space Directional Occlusion(SSDO)と名付けた、よりリアルな照明へ向けてSSAOを拡張しました。
本研究ではSSDOが
・どのように入射光の方向の集計を行うか
・どのように環境光の単反射を含むか
・どのように標準的なオブジェクトベースの大域照明の代わりをするか
・どのように少ない計算コストで済ませるか
の説明をしています。
構成は以下のとおりである。
まず、第2節で既存の動作を確認します。
第3節では、中央構造照明のAOの一般化を説明します。
第4節では、画質を向上させるための拡張機能について説明します。
第5節では完全なグローバルイルミネーションのシミュレーションと、本手法との統合が記載されています。
第6節で結果の提示。
第8章で結論づける前に第7章にて議論をしています。
2準備
物理的に妥当な照明は、全体の可視領域と照明の方向について計算をする時、 AOは可視領域と照明の2つの個別の積を積算します。
静的なシーンでは、AOは可視性を事前計算し、(頂点やテクスチャを使用して)表面上のスカラー場としてそれを保存することができます。
単純な乗算を使用した静的なAOと動的な照明の組み合わせは、高フレームレートで見た目に妥当な結果をもたらします。
AOフィールドの導入によってオブジェクトの並行移動、回転とアニメションキャラクターに特化した解決方法も存在します。
変形する面と間接光の反射は、Bunnell [2005]のディスクセットを使用したジオメトリの近似によって解決されます。
より堅牢なものはHoberock and Jia [2007]によって提示され、さらにChristensen [2008]によってポイントベースのアンビエントオクルージョンと相互反射に拡張されました。
Mendez et al. [2006]ジオメトリ周辺の平均アルベドを使用した単純な色にじみ効果を計算します。
この手法は表面の離散化もしくはレイトレーシングへのどちらかを使用しています。
これはゲームのような現在のインタラクティブなアプリケーションで使用される動的なジオメトリの量に比例して増大してしまいます。
したがって、最近の方法は代わりにスクリーンスペースの表面を使って近似された遮蔽を計算します。[Shanmugam and Arikan 2007; Mittring 2007; Bavoil et al. 2008; Filion and McNaughton 2008].
SSAOの好まれる点はその単純な実装とハイパフォーマンスにあります。
これは、出力が感覚的で、ポストプロセスとして処理され、追加データ(例えば、表面の定義、BVH、kD-trees、シャドウマップのような空間の表現に使われる構造)が必要なく、さまざまなタイプのジオメトリで動作します(例:ディスプレースメントや法線マップ、頂点またはジオメトリシェーダ、等値面の光線と平面の交点を求めるレイキャスティング)。
イメージ空間の手法はまた効率的に地下散乱をシミュレートすることができます。[Mertens et al. 2005].
同時にSSAOは様々な制限に対して近似を求められます。
これも今回の研究であり、後のセクションで詳しく説明します。
AOは一般的な光伝播に対しては粗い近似値になります。
例えば、PRT [Lehtinen and Kautz 2003]の指向性遮蔽(DO)と相互反射をサポートし、
事前計算では、しばしば空間や方向の解像度増大の制限のために圧縮形式のデータを大量に格納する必要が出てきてしまいます。
我々のアプローチは、非常に小さいサーフェイスの詳細、全方位の解像度のどちらも解決することができます:"無周波数"AO、全周波数イメージベースの照明やポイントライトからシャープな影。
PRTは広い照明範囲とそれなりの複雑さの静的なジオメトリとうまく動作しますが、
SSAOは妥協のないシンプルさがゆえに実際のアプリケーションに適応されています。
要約すると私たちの研究は、あらかじめ計算されている、
以前は動的には不可能だった最終的なリアリティーに影響する指向性遮蔽と間接的反射の2つ両方の近似計算をリアルタイムに、よりリッチなジオメトリに対してSSAOプロセス[Shanmugam and Arikan 2007] を用いることで活用しています。
3 イメージベースの近接場光伝搬
画像空間の光伝搬を計算するために、本手法は位置と法線のフレームバッファ[Segovia et al. 2006]を入力として使用しています。
フレームバッファは直接光及び間接バウンスの2つのレンダリングパス使用して照明ピクセルを出力します。
DOを用いた指向性照明
通常のSSAOは隣接するピクセルから、最初の計算で可視値の平均を求めピクセルの色を決定します。
この遮蔽の値はすべての入射方向からくる非遮蔽照明を乗算して求めます。
遮蔽と照明の関連性の低くさを取り除く処理を行う方法を次のように提案します。
3次元上に法線nの座標Pのピクセルがあり
方向ωiのN個のサンプリングから算出される直接光輝度をLdirとし、
均一に⊿ω=2π/Nで半球状に分散していると定義したとき
Ldir(P) = Σ(i:1->N)ρ/πLin(ωi)V(ωi)cosθi⊿ω.
各サンプルは、入射輝度Linと可視ベクトルVとディフューズBRDFρ/πの積を計算します。
我々は、Linを効率的にポイントライトや環境マップから計算することができることを前提としています。
スクリーンスペースで遮蔽物を近似する事で可視ベクトルVの算出に、レイトレーシング計算の使用を回避します。
点Pから方向ωiのランダムな距離λi [0...rmax]にあるすべてのサンプルを算出します。
rmaxは任意に定義した半径です。
これは結果、Pを中心とする半球に内包するサンプリング点P + λiωiと法線n方向との組みとなります。
続いて、3次元の点として点P周辺での局所的なサンプリングポイントを生成します。
そのうちのいくつかは表面の上、またいくつかは下に位置します。
サンプリングポイントが隣接するジオメトリの表面より下にあるものはすべて遮蔽物として近似の可視性判定として扱われます。
今3次元の位置情報はポジションバッファから取得でき、その点がジオメトリの表面上に投影されます(赤の矢印)。
もし、このジオメトリ表面への投影によって観測点からサンプル点への距離が減少すれば、サンプル点は表面の下に位置すると分類することが出来ます。
例えば Fig. 2において点ABDは観測点に近づいたので表面の下に位置し、点Cは観測点から遠ざかります。
SSAOと対照的に、すべての点から照明の計算をせずに見えている方向(サンプル点C)からのみにします。
特に入射光照明が違う方向から、違う色でやってくる場合、この指向性の情報は結果を大幅に向上させることが出来ると言うことになります。
Fig.3の通り、SSAOでは単純にグレーカラーで影を表示するだけですが、こちらは正しい色で影を表示することが出来ます。
間接反射
1つ前のパスで、直接光がフレームバッファに書き込んだ単反射のライト情報を含むことで以下のことが可能になる。
遮蔽物として扱われる各サンプル点(ABD)に対応するピクセルカラーLpixelは表面上に配置される単位面の発光輝度として使用される。(Fig2右)
ここで発光単位面の裏側からの色漏れを避けるために発光側の法線を考慮する。
ジオメトリ周囲の付加輝度は以下の式で近似できます。
Lind(P) = Σ(i:1->N)ρ/πLpixel(1-V(ωi))(As cosθsi cosθri)/di^2
diは点Pと遮蔽物iとの距離(diは特異点問題を避けるために1でクランプする)
θsiとθriはそれぞれ発光者(sender)と受光者(receiver)の法線と透過方向の角度です。
Asは発光者単位面に関連する領域です。
単位面領域の前提値として半球内の平面を想定しています。
基礎円はN個の領域に分割されているので、As=πr^2max/Nの領域をお互いにカバーします。
半球内の斜面の分布に応じて、実際の値は高くすることが出来ます。
このパラメータを色にじみ強度の手動のコントロールとしてに使うことが出来ます。
例として、fig2において単位平面Aは裏を向いているために間接光計算は何も寄与していません。
単位平面Cは点Pの半球の反対側にありますのでこれもまた関与しません。
単位平面BCは点Pに対して間接光の発光者となります。
Fig3は間接光の反射を標示しています。
細部の実装
従来のSSAO [Shanmugam and Arikan 2007] は同様の手順と計算コストがかかります。
今回の手法は可視判定に相当するシェーディングモデルを判断する為により多くの計算を必要とします。
今回は太陽光のような既知の重要な光源には、スクリーンスペースでの手法ではなく、代わりにジオメトリの距離から影をキャプチャーするシャドウマップを適応する追加のサンプルを使います。
N個の事前計算された食い違いの少ないサンプルλiωiのセットM個を格納するためにMxNのテクスチャを使います。
各ピクセルは実行時にMセット外の一つを使っています。
最後のパスでノイズを取り除くために微細なジオメトリブラー[Segovia et al. 2006]を適応します。
これはピクセルあたりのサンプルのリダクションによって提案されています。
4 マルチピクセル
スクリーンスペースでの処理なので、すべての遮蔽物や間接光源が見えているわけではありません。
Fig.4の例は間接照明の光源が遮蔽されていくにつれて、色のにじみがフェードアウトしていくのを示しています。
それらは、付属のビデオのとおり見た目にひどくがっかりするような見た目ではありません。しかし、実際の結果とは違っているのです。
この差異を減らす措置として、そのような照明結果を克服する2つのアプローチ、深度値の剥離と追加カメラを提示します
単一深度の限界
Fig. 5(左)に示すように、サンプリング点はそれゆえに間違って判断されます。
いくつかの状況では、入射光を乖離判定してしまったり、本来遮蔽されるべき方向を可視判定してしまういます。
サンプル点Bの遮蔽判定ミスは、この方向のサンプル点を増やすことよって訂正することが出来ますが、サンプル点Aの乖離(そして、反射光の入射位置を近くしてしまう)は単一の深度値z1の背後のシーンについての情報が無い為に単一の視点からでは訂正することができません。
深度値の剥離
剥離深度[Everitt 2001]は、nレンダリングパスのあとに互いにフレームバッファへ単一の深度値nが書き込まれます。
これは、このシーンに関してより多くの情報を持つことで遮蔽物判定を改善する猶予があります。
もしサンプル点が第一深度z1の背後にあるならば、そのまま判定せずに、第二の深度値z2の前にあるかテストをする。
第一第二深度はオブジェクトの表面と裏面にそれぞれ対応し、2つの集合管ジオメトリを使用する場合、2つの面の間のサンプルはこのオブジェクトの内側にしなければならない。(Fig.5右)
概要
リアルタイムで実現できる物理的に妥当な照明は、多くの場合近似を使って実現されています。
ひとつのポピュラーな例としてアンビエントオクルージョン(AO)があり、非常にシンプルでかつ効果的な実装は多くの製品で採用されています。
最近はスクリーンスペースでの近いジオメトリを使用したAOを近似する方法が採られています。
今回この論文で注目すべき点は、この手法が、スクリーンスペースで計算される遮蔽情報から、
例えば方向を考慮した影や間接的な色のにじみなど、より多くの影響を与える遮蔽情報を取得する為に使用することが出来ることです。
今回提案された手法は、スクリーンスペースで処理し従来のSSAOに比べてわずかなオーバーヘッドで直接光、単反射光を近似し、マクロ構造の過程をシミュレートするための別の手法と結合することができます。
そして、最悪のケースでも今回の手法を用いなかったSSAOと見た目的には同等になります。
この手法はスクリーンスペースでの処理になるため、ジオメトリ形状の複雑さに依存しません。
妥当な方向遮蔽と間接照明の効果はリアルタイムに、完全にランタイムで大規模なシーンを表現することが出来ます。
1はじめに
リアルタイムな大域照明は未だ、大規模で動的なシーンに於いて未解決の問題を持っています。
現在のところ十分なフレームレートを達成しているのは、近似を使う方法のみです。
そのような近似には、高速性と、実装の簡易さから映画やゲームなどに使われているAOがあります。
ただしAOは視界と照明を切り離しており、実際の照明の荒い近似のみをおこなっています。
AOは通常、空洞物の暗域を表現できますが、入射光の方向は無視されます。
今回Screen Space Directional Occlusion(SSDO)と名付けた、よりリアルな照明へ向けてSSAOを拡張しました。
本研究ではSSDOが
・どのように入射光の方向の集計を行うか
・どのように環境光の単反射を含むか
・どのように標準的なオブジェクトベースの大域照明の代わりをするか
・どのように少ない計算コストで済ませるか
の説明をしています。
構成は以下のとおりである。
まず、第2節で既存の動作を確認します。
第3節では、中央構造照明のAOの一般化を説明します。
第4節では、画質を向上させるための拡張機能について説明します。
第5節では完全なグローバルイルミネーションのシミュレーションと、本手法との統合が記載されています。
第6節で結果の提示。
第8章で結論づける前に第7章にて議論をしています。
2準備
物理的に妥当な照明は、全体の可視領域と照明の方向について計算をする時、 AOは可視領域と照明の2つの個別の積を積算します。
静的なシーンでは、AOは可視性を事前計算し、(頂点やテクスチャを使用して)表面上のスカラー場としてそれを保存することができます。
単純な乗算を使用した静的なAOと動的な照明の組み合わせは、高フレームレートで見た目に妥当な結果をもたらします。
AOフィールドの導入によってオブジェクトの並行移動、回転とアニメションキャラクターに特化した解決方法も存在します。
変形する面と間接光の反射は、Bunnell [2005]のディスクセットを使用したジオメトリの近似によって解決されます。
より堅牢なものはHoberock and Jia [2007]によって提示され、さらにChristensen [2008]によってポイントベースのアンビエントオクルージョンと相互反射に拡張されました。
Mendez et al. [2006]ジオメトリ周辺の平均アルベドを使用した単純な色にじみ効果を計算します。
この手法は表面の離散化もしくはレイトレーシングへのどちらかを使用しています。
これはゲームのような現在のインタラクティブなアプリケーションで使用される動的なジオメトリの量に比例して増大してしまいます。
したがって、最近の方法は代わりにスクリーンスペースの表面を使って近似された遮蔽を計算します。[Shanmugam and Arikan 2007; Mittring 2007; Bavoil et al. 2008; Filion and McNaughton 2008].
SSAOの好まれる点はその単純な実装とハイパフォーマンスにあります。
これは、出力が感覚的で、ポストプロセスとして処理され、追加データ(例えば、表面の定義、BVH、kD-trees、シャドウマップのような空間の表現に使われる構造)が必要なく、さまざまなタイプのジオメトリで動作します(例:ディスプレースメントや法線マップ、頂点またはジオメトリシェーダ、等値面の光線と平面の交点を求めるレイキャスティング)。
イメージ空間の手法はまた効率的に地下散乱をシミュレートすることができます。[Mertens et al. 2005].
同時にSSAOは様々な制限に対して近似を求められます。
これも今回の研究であり、後のセクションで詳しく説明します。
AOは一般的な光伝播に対しては粗い近似値になります。
例えば、PRT [Lehtinen and Kautz 2003]の指向性遮蔽(DO)と相互反射をサポートし、
事前計算では、しばしば空間や方向の解像度増大の制限のために圧縮形式のデータを大量に格納する必要が出てきてしまいます。
我々のアプローチは、非常に小さいサーフェイスの詳細、全方位の解像度のどちらも解決することができます:"無周波数"AO、全周波数イメージベースの照明やポイントライトからシャープな影。
PRTは広い照明範囲とそれなりの複雑さの静的なジオメトリとうまく動作しますが、
SSAOは妥協のないシンプルさがゆえに実際のアプリケーションに適応されています。
要約すると私たちの研究は、あらかじめ計算されている、
以前は動的には不可能だった最終的なリアリティーに影響する指向性遮蔽と間接的反射の2つ両方の近似計算をリアルタイムに、よりリッチなジオメトリに対してSSAOプロセス[Shanmugam and Arikan 2007] を用いることで活用しています。
3 イメージベースの近接場光伝搬
画像空間の光伝搬を計算するために、本手法は位置と法線のフレームバッファ[Segovia et al. 2006]を入力として使用しています。
フレームバッファは直接光及び間接バウンスの2つのレンダリングパス使用して照明ピクセルを出力します。
DOを用いた指向性照明
通常のSSAOは隣接するピクセルから、最初の計算で可視値の平均を求めピクセルの色を決定します。
この遮蔽の値はすべての入射方向からくる非遮蔽照明を乗算して求めます。
遮蔽と照明の関連性の低くさを取り除く処理を行う方法を次のように提案します。
3次元上に法線nの座標Pのピクセルがあり
方向ωiのN個のサンプリングから算出される直接光輝度をLdirとし、
均一に⊿ω=2π/Nで半球状に分散していると定義したとき
Ldir(P) = Σ(i:1->N)ρ/πLin(ωi)V(ωi)cosθi⊿ω.
各サンプルは、入射輝度Linと可視ベクトルVとディフューズBRDFρ/πの積を計算します。
我々は、Linを効率的にポイントライトや環境マップから計算することができることを前提としています。
スクリーンスペースで遮蔽物を近似する事で可視ベクトルVの算出に、レイトレーシング計算の使用を回避します。
点Pから方向ωiのランダムな距離λi [0...rmax]にあるすべてのサンプルを算出します。
rmaxは任意に定義した半径です。
これは結果、Pを中心とする半球に内包するサンプリング点P + λiωiと法線n方向との組みとなります。
続いて、3次元の点として点P周辺での局所的なサンプリングポイントを生成します。
そのうちのいくつかは表面の上、またいくつかは下に位置します。
サンプリングポイントが隣接するジオメトリの表面より下にあるものはすべて遮蔽物として近似の可視性判定として扱われます。
Fig.2:左 指向性遮蔽による直接光 互いのサンプル点は遮蔽物かどうかのテストに使われる。上記の例では、点Pは方向Cからのみ照明される。 右 間接光 直接光がフレームバッファに置いた、小さい範囲が互いの遮蔽物として表面上に配置されたものであり、対象点の輝度として使われる |
Fig. 2 (左) はサンプリングポイントABCDのN=4の例です。サンプル点ABDは表面の下に位置している為点Pの遮蔽物として分類される、サンプル点Cは表面の上に位置しているので可視点として分類されます。
サンプル点が表面の下に位置するかテストして、イメージ上に投影されます。今3次元の位置情報はポジションバッファから取得でき、その点がジオメトリの表面上に投影されます(赤の矢印)。
もし、このジオメトリ表面への投影によって観測点からサンプル点への距離が減少すれば、サンプル点は表面の下に位置すると分類することが出来ます。
例えば Fig. 2において点ABDは観測点に近づいたので表面の下に位置し、点Cは観測点から遠ざかります。
SSAOと対照的に、すべての点から照明の計算をせずに見えている方向(サンプル点C)からのみにします。
特に入射光照明が違う方向から、違う色でやってくる場合、この指向性の情報は結果を大幅に向上させることが出来ると言うことになります。
Fig.3の通り、SSAOでは単純にグレーカラーで影を表示するだけですが、こちらは正しい色で影を表示することが出来ます。
Fig3 |
Fig3 |
間接反射
1つ前のパスで、直接光がフレームバッファに書き込んだ単反射のライト情報を含むことで以下のことが可能になる。
遮蔽物として扱われる各サンプル点(ABD)に対応するピクセルカラーLpixelは表面上に配置される単位面の発光輝度として使用される。(Fig2右)
ここで発光単位面の裏側からの色漏れを避けるために発光側の法線を考慮する。
ジオメトリ周囲の付加輝度は以下の式で近似できます。
Lind(P) = Σ(i:1->N)ρ/πLpixel(1-V(ωi))(As cosθsi cosθri)/di^2
diは点Pと遮蔽物iとの距離(diは特異点問題を避けるために1でクランプする)
θsiとθriはそれぞれ発光者(sender)と受光者(receiver)の法線と透過方向の角度です。
Asは発光者単位面に関連する領域です。
単位面領域の前提値として半球内の平面を想定しています。
基礎円はN個の領域に分割されているので、As=πr^2max/Nの領域をお互いにカバーします。
半球内の斜面の分布に応じて、実際の値は高くすることが出来ます。
このパラメータを色にじみ強度の手動のコントロールとしてに使うことが出来ます。
例として、fig2において単位平面Aは裏を向いているために間接光計算は何も寄与していません。
単位平面Cは点Pの半球の反対側にありますのでこれもまた関与しません。
単位平面BCは点Pに対して間接光の発光者となります。
Fig3は間接光の反射を標示しています。
細部の実装
従来のSSAO [Shanmugam and Arikan 2007] は同様の手順と計算コストがかかります。
今回の手法は可視判定に相当するシェーディングモデルを判断する為により多くの計算を必要とします。
今回は太陽光のような既知の重要な光源には、スクリーンスペースでの手法ではなく、代わりにジオメトリの距離から影をキャプチャーするシャドウマップを適応する追加のサンプルを使います。
N個の事前計算された食い違いの少ないサンプルλiωiのセットM個を格納するためにMxNのテクスチャを使います。
各ピクセルは実行時にMセット外の一つを使っています。
最後のパスでノイズを取り除くために微細なジオメトリブラー[Segovia et al. 2006]を適応します。
これはピクセルあたりのサンプルのリダクションによって提案されています。
4 マルチピクセル
![]() |
Fig.4 |
Fig.4の例は間接照明の光源が遮蔽されていくにつれて、色のにじみがフェードアウトしていくのを示しています。
それらは、付属のビデオのとおり見た目にひどくがっかりするような見た目ではありません。しかし、実際の結果とは違っているのです。
この差異を減らす措置として、そのような照明結果を克服する2つのアプローチ、深度値の剥離と追加カメラを提示します
単一深度の限界
Fig. 5(左)に示すように、サンプリング点はそれゆえに間違って判断されます。
いくつかの状況では、入射光を乖離判定してしまったり、本来遮蔽されるべき方向を可視判定してしまういます。
サンプル点Bの遮蔽判定ミスは、この方向のサンプル点を増やすことよって訂正することが出来ますが、サンプル点Aの乖離(そして、反射光の入射位置を近くしてしまう)は単一の深度値z1の背後のシーンについての情報が無い為に単一の視点からでは訂正することができません。
深度値の剥離
剥離深度[Everitt 2001]は、nレンダリングパスのあとに互いにフレームバッファへ単一の深度値nが書き込まれます。
これは、このシーンに関してより多くの情報を持つことで遮蔽物判定を改善する猶予があります。
もしサンプル点が第一深度z1の背後にあるならば、そのまま判定せずに、第二の深度値z2の前にあるかテストをする。
第一第二深度はオブジェクトの表面と裏面にそれぞれ対応し、2つの集合管ジオメトリを使用する場合、2つの面の間のサンプルはこのオブジェクトの内側にしなければならない。(Fig.5右)
複雑さの高い深度のシーンのすべての影を再構築する為には、第3、第4深度値と連続する深度値のすべての組みは同じ方法で評価しなければならない[Lischinski and Rappoport 1998]
登録:
投稿 (Atom)