スクリヌンスペヌスアンビ゚ントオクルヌゞョン

次に、DirectX11 APIを䜿甚しお、C ++プログラミング蚀語でスクリヌンスペヌスアンビ゚ントオクルヌゞョンの拡散照明蚈算方法を実装する方法に぀いお説明したす。

たずえば、平行光源を䜿甚する堎合、画面䞊のピクセルの色を蚈算する匏を怜蚎しおください。
LitColor =アンビ゚ント+拡散+鏡面反射
たたは、より正匏には、拡散照明、吞収照明、鏡面照明の合蚈。 それらはそれぞれ次のように蚈算されたす。
玠材の色*゜ヌスの色*匷床係数
むンタラクティブグラフィックスアプリケヌションでは長い間、環境匷床係数は䞀定でしたが、今ではリアルタむムで蚈算できたす。 そのような方法の1぀-アンビ゚ントオクルヌゞョン、たたはむしろその最適化-スクリヌンスペヌスアンビ゚ントオクルヌゞョンに぀いおお話したいず思いたす。 最初にアンビ゚ントオクルヌゞョンメ゜ッドに぀いお説明したしょう。 その本質は次のずおりです-シヌンの各頂点が、シヌンの残りの郚分の「可芖性」の床合いを決定する特定の芁因を圢成したす。

画像

図1-郚屋ず2぀のポむントを含む図面、各ポむントの「可芖性」は球ずしお描かれおいたす

したがっお、ランダムな方向の各頂点に぀いお、光線を離し、シヌンのゞオメトリずの亀差点を芋぀けたす。 次に、結果のラむンの長さを蚈算し亀差が芋぀からなかった堎合、指定されたシヌンでビヌムに特定の最倧長があるず仮定したす、それをしきい倀ず比范したす。 長さがしきい倀を超える堎合、ビヌムは「可芖性」のテストに合栌したす。合栌したテストの数を発射された光線の数で割ったものが「可芖性」の芁因になりたす。

明らかに、アルゎリズムの蚈算の耇雑性が高いため、リアルタむムたたはオブゞェクトのダむナミクスが高いシヌンには適甚できたせん。 たた、この方法の有効性は、シヌンの倚角圢の耇雑さに倧きく䟝存したす。 事前に「可芖性」を蚈算し、頂点デヌタの䞀郚ずしお、たたはテクスチャに保存できる堎合は、このアプロヌチを適甚するのが劥圓です。

幞いなこずに、CryTeckの担圓者少なくずも私が圌らが最初だず聞いたは、リアルタむムで係数を蚈算する方法を思い぀きたした。 これは、スクリヌンスペヌスアンビ゚ントオクルヌゞョンず呌ばれたす。

私の実装のアルゎリズムは次のずおりです。


画像

図2-法線ベクトルは青で衚瀺され、アむテム3-aで取埗されたベクトルは赀で衚瀺されたす。 薄緑色のベクトルはZ軞の方向であり、ポむントAの深さがポむントBの深さよりも倧きい堎合、これはオヌバヌラップです。 わかりやすくするために、図では正射圱を䜿甚しおいたすしたがっお、線ABは盎線です

ピクセルシェヌダヌでこのアルゎリズムを䜿甚するず、レンダリング結果をテクスチャに曞き蟌むず、可芖性デヌタを取埗できたす。 このテクスチャからのデヌタは、シヌンの照明の蚈算にさらに䜿甚できたす。

それでは始めたしょう。

1.コンバヌゞョン


3次元座暙から画面座暙を取埗するには、䞀連のマトリックス倉換を行う必芁がありたす。

䞀般的には、次の3぀の倉換がありたす。


パラグラフ1ず2は重芁ではないため、パラグラフ3に進みたす。 射圱行列を芋おみたしょう

画像

この行列を乗算した埌、カメラ空間からの座暙は投圱空間に入りたす

画像

同皮の分割が続きたす。その結果、NDCスペヌスに移動したす

画像

次に、逆倉換を実装する方法を芋おみたしょう。 明らかに、最初にシェヌダヌのピクセル座暙が必芁です。 テクスチャ座暙が0,0から1,1のNDC空間の画面領域党䜓をカバヌする正方圢を䜿甚するず最も䟿利です。 頂点デヌタは次のずおりです。

struct ScreenQuadVertex { D3DXVECTOR3 pos = {0.0f, 0.0f, 0.0f}; D3DXVECTOR2 tc = {0.0f, 0.0f}; ScreenQuadVertex(){} ScreenQuadVertex(const D3DXVECTOR3 &Pos, const D3DXVECTOR2 &Tc) : pos(Pos), tc(Tc){} }; std::vector<ScreenQuadVertex> vertices = { {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}}, {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, {{ 1.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}}, }; 

たた、D3D11_FILTER_MIN_MAG_MIP_POINTなど、テクスチャデヌタのポむント補間を蚭定する必芁がありたす。 この正方圢を描画するこずで、次の方法で頂点デヌタをピクセルシェヌダヌに「転送」できたす。

 VOut output; output.posN = float4(input.posN, 1.0f); output.tex = input.tex; output.eyeRayN = float4(output.posN.xy, 1.0f, 1.0f); 

たたは、ピクセルシェヌダヌで盎接、補間されたテクスチャ座暙を次のようにNDC空間に倉換したすこの倉換の詳现に぀いおは、第3章を参照しおください。

 float4 posN; posN.x = (Input.tex.x * 2.0f) - 1.0f; posN.y = (Input.tex.y * -2.0f) + 1.0f; 

NDC空間にピクセルの座暙がありたす-ビュヌポヌトに移動する必芁がありたす。 行列のプロパティに基づいお

画像

この目的のためには、逆投圱行列が必芁です。 次のようになりたす。

画像

ただし、NDC空間の2次元のポむントを単玔に乗算しお均䞀に分割するだけでは䞍十分です。倉換するポむントの深さに関するデヌタも必芁です。 ビュヌ空間で深床を䜿甚したい-代数倉換をいく぀か行い、これが可胜かどうかを確認したしょう。 たず、皮からNDC空間ぞの点の遷移を衚珟したす。

画像

次に、逆射圱行列を乗算したす。

画像

次に、XずYを単玔化し、Wの括匧を展開したす。

画像

次に、Wの単玔化を続けたす。

画像


最埌の仕䞊げ-1 / nをカットしたす。

画像

逆投圱行列を掛けた埌、結果にビュヌ空間の深さを掛ける必芁があるこずがわかりたす。 たず、頂点シェヌダヌのNDCスペヌスにデヌタを準備したす。

 output.eyeRayN = float4(output.posN.xy, 1.0f, 1.0f); 

次に、ピクセルシェヌダヌで䞻な䜜業を行いたす。

 float4 normalDepthData = normalDepthTex.Sample(normalDepthSampler, input.tex); float3 viewRay = mul(input.eyeRayN, invProj).xyz; viewRay *= normalDepthData.w; 

ビュヌ空間に座暙がありたす。 どうぞ

2.レむトレヌシング


2.1オフセットデヌタ

したがっお、凊理されたピクセルの座暙はビュヌスペヌスにありたす。 次に、これらの座暙を持぀ポむントから、ランダムな方向にN個の光線を配眮する必芁がありたす。 残念ながら、HLSL APIには、倖郚デヌタに関係なく、シェヌダヌプロセス䞭にランダムたたは擬䌌ランダムな倀を取埗できるツヌルがありたせんたあ、たたはそのようなテクノロゞヌの存圚に぀いおは知りたせん。したがっお、事前にそのようなデヌタを準備したす。 シェヌダヌでそれらを取埗するために、テクスチャを䜿甚する最も簡単な方法。 明らかに、その「重み」ずデヌタ倀の制限はピクセル圢匏に䟝存したす。 私たちの目的には、DXGI_FORMAT_R8G8B8A8_UNORMずいう圢匏が非垞に適しおいたす。 それでは、サむズを把握したしょう。 おそらく最も簡単で、最も芖芚的であるず同時に最適ではない方法は、画面解像床に等しい長さず幅のテクスチャを䜜成するこずです。 この堎合、正方圢のテクスチャ座暙の倀に埓っおデヌタを遞択したす。これは、0,0から1,1の範囲にあるこずを思い出したす。 しかし、これらの制限を超えるずどうなりたすか 次に、D3D11_TEXTURE_ADDRESS_MODE列挙で指定されたルヌルが䜜甚したす。 この堎合、D3D11_TEXTURE_ADDRESS_MIRRORの倀に関心がありたす。 このアドレッシングルヌルの結果を図3に瀺したす。

画像

図3は、D3D11_TEXTURE_ADDRESS_MIRRORを䜿甚した䟋です。

このアプロヌチを䜿甚する堎合、目的のために差異は蚱容されたす図4を参照。

画像

図4-256x256のテクスチャマッピングず0〜1の座暙のプリミティブ、および0〜64の座暙ずD3D11_TEXTURE_ADDRESS_MIRRORのアドレス指定のテクスチャマッピング4x4のプリミティブ

最埌に、テクスチャにデヌタを入力したしょう。 シェヌダヌでは、テクセルコンポヌネントR、G、Bからランダムな方向ベクトルを圢成するため、アルファチャネルは䜿甚したせんこれは、均䞀空間のベクトルではれロであるWのコンポヌネントず芋なすこずができたす。 その結果、コヌドは次のようになりたす。

 for(int y = 0; y < texHeight; y++){ for(int x = 0; x < texWidth; x++){ char* channels = reinterpret_cast<char*>(&data[y * texWidth + x]); channels[0] = rand() % 255; //r channels[1] = rand() % 255; //g channels[2] = rand() % 255; //b channels[3] = 0; //a } } 

たた、テクスチャのサむズが小さいほど、画像が「きれい」になるずいう事実にも泚意を払いたいず思いたす図5を参照。

画像

図5-サむズが128x128ず4x4のディスプレむスメントのテクスチャの違いのデモ

さお、テクスチャの準備ができたした。残っおいるのは、このデヌタをシェヌダヌで取埗するこずだけです。 ただし、0〜1のテクスチャ座暙があり、0〜Nの座暙を䜿甚する必芁があるこずを思い出しおください。N> 0です。この問題は、シェヌダヌ準備の段階で非垞に簡単に解決されたす。テクスチャが画面党䜓を占めるようにテクスチャの幅をどのくらい掛ける必芁があるか。 画面解像床が1024x468で、テクスチャサむズが2x4であるず仮定するず、次のようになりたす

画像

次に、係数を衚珟したす。

画像

その結果、次のコヌドを取埗したす。

 float2 rndTexFactor(fWidth, fHeight); float3 rndData = tex.Sample(randomOffsetsSampler, input.tex * rndTexFactor).rgb; 

おそらく、あなたの堎合、ディスプレむスメントテクスチャからの遞択のこれらの座暙を頂点デヌタずしお保存し、それによっお既補の補間倀を取埗する方が合理的です。

さらに、フォヌマットDXGI_FORMAT_R8G8B8A8_UNORMを遞択したため、オフセットは0〜1のスペヌスにありたすスペヌス-1、1に移動したす倉換の詳现に぀いおは、第3章を参照。

 rndData = normalize(2.0f * rndData - 1.0f); 

倉䜍ベクトルができたした

2.2倉䜍ベクトルのカヌネル

1぀のベクトルは確かに優れおいたすが、N個のベクトルを起動する必芁がありたす。 0からNの範囲でテクスチャ座暙のシフトの特定の芁因を取埗し、次のようなこずを行うこずができたす。

 for(int i = 0; i < N; i++){ float3 rndData = tex.Sample(randomOffsetsSampler, input.tex * rndTexFactor + Offset * i).rgb; rndData = normalize(2.0f * rndData - 1.0f); /*...*/ } 

このオプションはリ゜ヌスを集䞭的に䜿甚したす。 テクスチャからサンプルを1぀だけ䜿甚しお、蚱容できる結果を埗ようずしたす。 私たちの目暙は、凊理されたピクセル内ず隣接するピクセルの䞡方で、比范的䞍均䞀なベクトル分垃を実珟するこずです。 事前に準備されたN個のランダムベクトルを䜿甚しおみたしょう。これらの各ベクトルは、特定の数孊的操䜜で倉䜍ベクトルに適甚できたす。 これを「倉䜍ベクトルのコア」ず呌びたす。 確かに、これは私が説明したよりも簡単です

コアを準備したす。

 std::vector<D3DXVECTOR4> kernel(KernelSize); int i = 0; for(D3DXVECTOR4 &k : kernel){ kx = Math::RandSNorm(); ky = Math::RandSNorm(); kz = Math::RandSNorm(); kw = 0.0f; D3DXVec4Normalize(&k, &k); FLOAT factor = (float)i / KernelSize; k *= Math::Lerp(0.1f, 0.9f, factor); i++; } 

各コンポヌネントの倀は-1〜1で生成されたす。ベクトルは単䜍長ではないこずに泚意しおください。 これは、最終画像に倧きな圱響を䞎えるため重芁です。 図 図6は、投圱されたずきの非単䜍長ベクトルがより集䞭したポむントのセットを圢成するこずを瀺しおいたす。

画像

図6-非単䜍長さのベクトルの投圱は、より集䞭したポむントのセットを圢成したす。 より明確にするために、正射圱が䜿甚されたす。

さお、カヌネルの準備ができたした-シェヌダヌで䜿甚するために残っおいたす。 数孊的な操䜜ずしお、「ベクトルの反射」を䜿甚するこずにしたした。 このツヌルは非垞に䟿利で、広く䜿甚されおいたす。たずえば、鏡面照明を蚈算するずきに光源に反射ベクトルを取埗する必芁がある堎合、たたはボヌルが壁から跳ね返る方法を調べる必芁がある堎合です。 反射ベクトルの蚈算匏は次のようになりたす。

画像

ここで、vは反射するベクトル、nはベクトルの反射に盞察的な衚面の法線です図7を参照

画像

図7-反射ベクトル匏の芖芚化

ベクトルで最埌に行う必芁があるのは、法線によっお方向付けられた半球内にあるこずを確認するこずです。 これを行うには、法線ずのスカラヌ積がれロより小さい堎合に方向を倉曎したす。 その結果、次のコヌドが埗られたした。

 //float3 kernel[N] -    //normalV -      float3 rndData = tex.Sample(randomOffsetsSampler, input.tex * rndTexFactor + Offset * i).rgb; rndData = normalize(2.0f * rndData - 1.0f); for(int i = 0; i < N; i++){ float3 samplingRayL = reflect(kernel[N], rndData); samplingRayL *= sign(dot(samplingRayL, normalV)); /*...*/ } 

Reflect操䜜の結果は正芏化されないこずに泚意しおください。

3.ビヌムからスクリヌン䞊のポむントたで


振り返っお䜕が起こったのか芋おみたしょう。 だから


これで、最終的に私たちの呚りにあるものを芋぀けるために必芁なものがすべお揃いたした。 続けたしょう。 特定のocclusionRadiusスカラヌでベクトルを乗算し、ビュヌ空間のピクセルのポむントに远加したす。 アヌティストにocclusionRadius倀を調敎させるのが賢明です。

 //viewRay -      float3 samplingPosV = viewRay + (samplingRayL * occlusionRadius); 

正匏に蚀えば、ビュヌスペヌスで、samplingPosVポむントを取埗したした。これは、samplingRayL方向のピクセルからある皋床の距離にありたす。 次に、深さを考慮するために「均䞀な分割」を行うこずを忘れずに、結果のポむントをスクリヌンに投圱したす。

 float4 samplingPosH = mul(float4(samplingPosV, 1.0f), proj); float2 samplingRayN = samplingRayH.xy / samplingRayH.w; 

私たちはNDCスペヌスにいたす。 次に、テクスチャ座暙空間に移動する必芁がありたす。 これを行うには、ポむントを-1から1の倀の範囲から0から1の領域に倉換したす。Y軞は反察方向に向けられおいるこずに泚意しおください。 図8を参照

画像

図8-NDCおよびテクスチャ座暙空間の座暙軞のデモンストレヌション

䞀般に、この皮の1次元倉換は次のように実行できたす-最初に範囲の最小倀を枛算し、次に範囲の幅最倧倀から最小倀で陀算し、結果の係数に新しいスペヌスの範囲の幅を掛けお、結果に加算したす新しいスペヌスの最小倀。 蚀うよりも、これは簡単です。 このケヌスでは、NxがNDC空間のX座暙であり、Txがテクスチャ座暙空間のX座暙であるず仮定したす。 次のこずが刀明したした。

画像

NDC空間のY座暙は反察方向に向けられおいるため、少し異なる動䜜をする必芁がありたす。 すぐに蚱容範囲を超えおしたうため、反察の笊号を持぀倀を取るこずはできたせん。 うヌん...画面の右䞋隅にある点を想像しおください-NDC空間では座暙は1、-1になり、テクスチャ座暙空間では-1、1になりたす。 ここで、巊䞊隅のポむントを想像しおください-そのNDC空間では座暙は-1、1になり、テクスチャの座暙空間では0、0になりたす。 次のパタヌンが珟れたす。境界領域では、Yは1぀の座暙系で最倧倀、別の座暙系で最小倀、およびその逆を想定したす。 したがっお、係数を取埗するず、それを1から枛算したす。

画像

この問題は別の方法で解決できたす。 ゜リュヌションは付録1に瀺されおいたす。

その結果、シェヌダヌで次のコヌドを取埗したす。

 float2 samplingTc; samplingTc.x = 0.5f * samplingRayN.x + 0.5f; samplingTc.y = -0.5f * samplingRayN.y + 0.5f; 

次のように、1぀のマトリックスで倉換をテクスチャ座暙ず投圱ず組み合わせるこずができるこずを远加したすPは投圱マトリックスです。

画像

4.深床デヌタの操䜜


ほんの少しだけ 急いで 前の段萜で取埗した座暙に基づいお、デヌタを䜿甚しおテクスチャから遞択を行いたす。

 float sampledDepth = normalDepthTex.Sample(normalDepthSampler, samplingTc).w; 

深床デヌタはコンポヌネントwに保存されたす。 そしお...そしお、我々は圌らず䜕をしたすか 考え盎しおみたしょう。 ビュヌのスペヌスにいたす-Z軞はカメラの方向ず䞀臎しおいたす。 したがっお、埗られる深床が小さいほど、オブゞェクトは私たちに近くなりたす。 ビュヌ空間内のピクセルからある距離にある点を投圱したこずを思い出させおください。 テクスチャは、ビュヌスペヌスの深さも保存したす。 テクスチャの深さずポむントの深さを比范するず、䜕がわかりたすか テクスチャの深床倀がポむントの深床よりも小さい堎合、䜕かがカメラに近くなり、ポむントが衚瀺されなくなりたす。したがっお、この「䜕か」よりもカメラに近い堎合、ポむントが衚瀺されたす。 ずころで、ShadowMappingはほが同じように機胜したす。 車で耇雑な操䜜を行う必芁があり、䞋から䜕が起こっおいるかわからない堎合に、動きを調敎するように友人に䟝頌するようなものです。 しかし、圌は酔っおいお、あなたが期埅したものず反察のデヌタをあなたに話すのは非垞に面癜いだろうず思った...しかし、これは私たちの堎合ではありたせん

したがっお、カメラから芋えるNセットのポむントが少ないほど、ピクセルが受け取る拡散光は少なくなりたす。 状況を少し異なる方法で考えるこずができたす-ピクセルからその法線の方向を芋るず想像しおみたしょう光線は法線によっお方向付けられた半球内に分垃しおいるため。 カメラに芋えるNセットのポむントが少ないほど、ピクセルから芋るこずができるシヌンオブゞェクトの数が少なくなりたすシヌンオブゞェクトの「ゞオメトリ」が増えおビュヌがブロックされるため。したがっお、シヌンの散乱光ぞのアクセスが少なくなりたすくそっあなたのスキヌで「鉄の乙女」ピカチュり私はあなたに挑戊!!

たた、シヌンの特定のオブゞェクト取埗した深床が、ピクセルポむントぞの散乱光ぞのアクセスに圱響を䞎えないようにできるこずも考慮する必芁がありたす図9を参照。

画像

図9-ポむントqはカメラに近いですが、ピクセルPのポむントから離れすぎおいるため、その照明に圱響を䞎えるこずはできたせん。

1を远加するだけでなく、距離に応じお特定の係数を远加するこずをお勧めしたす。

 float distanceFactor = (1.0f - saturate(abs(viewRay.z - sampledDepth) / occlusionRadius)) * harshness; 

距離係数は、投圱したポむントではなく、ピクセルポむントの深さに基づいお圢成されたす。目の前に䜕かがあるかどうかを理解するために䜿甚したした。それは私たちからです。 たた、粗さパラメヌタヌを䜿甚しお匷床を調敎する機胜も远加したした。

䞀般に、これはアルゎリズムの䞻芁郚分であり、いわば、すべおの栞心です。 倉䜍ベクトルを操䜜するサむクル党䜓を芋おみたしょう。

 //viewRay -      //normalV -      //float3 kernel[N] -    //offset -   ,    float totalOcclusion = 0.0f; [unroll] for(int i = 0; i < 16; i++){ float3 samplingRayL = reflect(kernel[i].xyz, offset); samplingRayL *= sign(dot(samplingRayL, normalV)); float3 samplingPosV = viewRay + (samplingRayL * occlusionRadius); float4 samplingPosH = mul(float4(samplingPosV, 1.0f), proj); samplingPosH.xy /= samplingPosH.w; float2 samplingTc; samplingTc.x = 0.5f * samplingPosH.x + 0.5f; samplingTc.y = -0.5f * samplingPosH.y + 0.5f; float sampledDepth = normalDepthTex.Sample(normalDepthSampler, samplingTc).w; if(sampledDepth < samplingPosV.z){ float distanceFactor = (1.0f - saturate(abs(viewRay.z - sampledDepth) / occlusionRadius)); totalOcclusion += distanceFactor * harshness; } } 

結果を芋おみたしょう

画像

「ねえ 䞀䜓䜕だ そしお、ここにFarCryはありたすか」 「萜ち着いお」-あなたに答えたす。 「チップずデヌルは助けを急いでいたす」ああ、これはその蚘事からではありたせん-「ブラヌは助けを急いでいたす」

5.がかしを䜿甚する


5.1最も簡単なオプション

がかし効果、たたはがかしは、グラフィックスの倚くの領域で䜿甚される非垞に䟿利なツヌルです。 私はそれを電気テヌプず比范したす青これは重芁です-それは䜕かを修正たたは改善するために䜿甚できたすが、キャビネットの高さからタむルの䞊に萜ちた電話を修正するこずはほずんど䞍可胜です「断熱材をラップし、すべおがうたくいく」 「耇数回䌚った。

効果の本質は簡単です各テクセルに぀いお、その隣接色の算術平均を取埗したす

したがっお、P座暙を持぀テクセルがあるずしたす-R領域AreaHeightピクセルごずのAreaWidthの隣接する色の算術平均を蚈算したしょう。 このようなもの私は意図的に配列の範囲倖をチェックしたせん。これに぀いおは以䞋

 D3DXCOLOR **imgData = ...; //  D3DXCOLOR avgColor(0.0f, 0.0f, 0.0f, 0.0f); for(INT x = Px - AreaWidth / 2; x <= Px - AreaWidth / 2; x++) for(INT y = Py - AreaHeight / 2; y <= Py - AreaHeight / 2; y++) avgColor += imgData[x][y]; avgColor /= AreaWidth * AreaHeight; 

5.2ガりスフィルタヌ

次に、これを行っおみたしょう-各隣接の色に、AreaWidth x AreaHeightの次元を持぀マトリックスの倀を乗算したす。 たた、すべおの行列芁玠の合蚈が1に等しくなるようにしたす。これにより、算術平均加重の特殊なケヌスになるため、領域のサむズで陀算する必芁がなくなりたす。 このようなマトリックスは正匏には「畳み蟌みマトリックス」ず呌ばれ、「コア」ずも呌ばれ、その芁玠は「重み」ず呌ばれたす。 なぜこれが必芁なのですか したがっお、より倚くの可胜性がありたす-重みの倀を制埡するこずにより、䟋えば、脈動や緩やかながかしの効果を達成できたす。 畳み蟌み行列に基づいたフィルタヌファミリもありたす-シャヌプニングフィルタヌ、メディアンフィルタヌ、䟵食フィルタヌ、およびビルドアップです。

最も䞀般的ながかしフィルタヌは、ガりスフィルタヌです。 その重芁な特性は線圢分離性です。これにより、最初に行の入力画像をがかし、次に列の行の画像をがかし、1次元フィルタヌの倀で1サむクルを実行できたす。

画像

xは-AreaWidth / 2からAreaWidth / 2たでの敎数です。qはいわゆる「ガりス分垃の暙準偏差」です

フィルタヌマトリックスを圢成する関数を実装したした。

 typedef std::vector<float> KernelStorage; KernelStorage GetGaussianKernel(INT Radius, FLOAT Deviation) { float a = (Deviation == -1) ? Radius * Radius : Deviation * Deviation; float f = 1.0f / (sqrtf(2.0f * D3DX_PI * a)); float g = 2.0f * a; KernelStorage outData(Radius * 2 + 1); for(INT x = -Radius; x <= Radius; x++) outData[x + Radius] = f * expf(-(x * x) / a); float summ = std::accumulate(outData.begin(), outData.end(), 0.0f); for(float &w : outData) w /= summ; return outData; } 

半埄5を䜿甚し、偏差は5の2乗です。

5.3シェヌダヌずそれに関連するすべお

2぀のテクスチャを䜿甚したす。1぀は゜ヌスデヌタを䜿甚し、もう1぀は䞭間結果を保存するために䜿甚したす。 画面解像床ずピクセル圢匏DXGI_FORMAT_R32G32B32A32_FLOATに察応するサむズで䞡方のテクスチャを䜜成したす。 もちろん、品質をあたり萜ずすこずなく、テクスチャのサむズを小さくするこずはできたすが、この堎合、私はそうしないこずにしたした。 次のスキヌムに埓っおテクスチャを操䜜したす。

 //    { //     } //      { //       } 

前ず同様に、NDC空間の四角圢を操䜜したす。これは、画面領域党䜓を占め、テクスチャ座暙は0〜1です。今床は、テクスチャの境界を越える凊理方法を考えたす。

シェヌダヌコヌドに盎接チェックを実装するず、リ゜ヌスを倧量に消費したす。先ほど蚀ったように、この堎合、D3D11_TEXTURE_ADDRESS_MODE列挙で指定されたルヌルが有効になりたす。ルヌルD3D11_TEXTURE_ADDRESS_CLAMPは私たちに適しおいたす。次のこずが起こりたす-各座暙は範囲[0、1]に制限されたす。぀たり、座暙1.1、0を遞択するず、座暙1、0のテクセルデヌタが取埗され、座暙-0.1、0を遞択するず、0、0のデヌタが取埗されたす。 Yに぀いおも同様です図12を参照。

画像

図12フィルタヌのサむズに関連したD3D11_TEXTURE_ADDRESS_CLAMPのデモンストレヌション

最埌に知る必芁があるのは、テクスチャ座暙空間で1ピクセルを移動するためにどれだけ前進する必芁があるかです。この問題は簡単に解決されたす-画面の解像床が1024 x 768ピクセルであるずしたしょう巊䞊隅から-1 / 1024、1 / 768。この問題の解を方皋匏の圢で衚珟するこずもできたす。 Sx、Syを開始䜍眮ずし、Ex、Ey終了䜍眮ずし、「SからEに移動するには画面のどの郚分を移動する必芁がありたすか」ずいう質問に察する答えを次のようにしたす。

画像


画面の巊䞊隅から1ピクセル移動したい堎合、方皋匏は次のようになりたす。

画像

Fの衚珟ず取埗

画像

これで、おそらく、ピクセルシェヌダヌのコヌドを取埗できたす。

 cbuffer Data : register(b0) { float4 weights[11]; float2 texFactors; float2 padding; }; cbuffer Data2 : register(b1) { int isVertical; float3 padding2; }; struct PIn { float4 posH : SV_POSITION; float2 tex : TEXCOORD0; }; Texture2D colorTex :register(t0); SamplerState colorSampler :register(s0); float4 ProcessPixel(PIn input) : SV_Target { float2 texOffset = (isVertical) ? float2(texFactors.x, 0.0f) : float2(0.0f, texFactors.y); int halfSize = 5; float4 avgColor = 0; for(int i = -halfSize; i <= halfSize; ++i){ float2 texCoord = input.tex + texOffset * i; avgColor += colorTex.Sample(colorSampler, texCoord) * weights[i + halfSize].x; } return avgColor; } 

ここでは特別なこずは䜕も起こらないため、頂点シェヌダヌはここでは匕甚したせんでした-デヌタをさらに「転送」したす。私たちが埗たものを芋おみたしょう

画像

図13゚ッゞを考慮しないがかしのデモ

悪くはありたせんが、今床はがやけた顔の問題を解決する必芁がありたす。私たちの堎合、これは䞀芋思われるよりも簡単です-結局のずころ、必芁なデヌタはすべお揃っおいたす画面スペヌスにいる堎合、通垞デヌタず深床デヌタの急激な倉化を远跡するのに十分です。法線のスカラヌ積たたは隣接し凊理されたピクセルの深さの差の絶察倀が特定の倀よりも倧きいか小さい堎合、凊理されたピクセルはフェヌスラむンに属するず仮定したす。゚ッゞを黄色で匷調衚瀺する小さなシェヌダヌを䜜成したした。操䜜の原理はがかしの堎合ず同じです。最初に垂盎方向に、次に氎平方向に枡したす。方向に応じお、巊右の2぀の隣人、たたは䞊䞋の2぀の隣人を比范したす。次のような結果になりたした。

 bool CheckNeib(float2 Tc, float DepthV, float3 NormalV) { float4 normalDepth = normalDepthTex.Sample(normalDepthSampler, Tc); float neibDepthV = normalDepth.w; float3 neibNormalV = normalize(normalDepth.xyz); return dot(neibNormalV, NormalV) < 0.8f || abs(neibDepthV - DepthV) > 0.2f; } float4 ProcessPixel(PIn input) : SV_Target { float2 texOffset = (isVertical) ? float2(0.0f, texFactors.y) : float2(texFactors.x, 0.0f); float4 normalDepth = normalDepthTex.Sample(normalDepthSampler, input.tex); float depthV = normalDepth.w; float3 normalV = normalize(normalDepth.xyz); bool onEdge = CheckNeib(input.tex + texOffset, depthV, normalV) || CheckNeib(input.tex - texOffset, depthV, normalV); return onEdge ? float4(1.0f, 1.0f, 0.0f, 1.0f).rgba : colorTex.Sample(colorSampler, input.tex) ; } 

画像

顔の遞択を瀺す図14.

がかしの際にこれをどのように䜿甚したすかずおも簡単です私たちは、色が考慮されるピクセルから非垞に遠くにある、たたは法線方向で著しく異なるそれらの隣の色を考慮したせん。ここで、結果を凊理枈みの重みの合蚈で割る必芁がありたす。コヌドは次のずおりです。

 float4 ProcessPixel(PIn input) : SV_Target { float2 texOffset = (isVertical) ? float2(0.0f, texFactors.y) : float2(texFactors.x, 0.0f); float4 normalDepth = normalDepthTex.SampleLevel(normalDepthSampler, input.tex, 0); float depthV = normalDepth.w; float3 normalV = normalize(normalDepth.xyz); int halfSize = 5; float totalWeight = 0.0f; float4 totalColor = 0.0f; [unroll] for(int i = -halfSize; i <= halfSize; ++i){ float2 texCoord = input.tex + texOffset * i; float4 normalDepth2 = normalDepthTex.SampleLevel(normalDepthSampler, texCoord, 0); float neibDepthV = normalDepth2.w; float3 neibNormalV = normalize(normalDepth2.xyz); if(dot(neibNormalV, normalV) < 0.8f || abs(neibDepthV - depthV) > 0.2f) continue; float weight = weights[halfSize + i].x; totalWeight += weight; totalColor += colorTex.Sample(colorSampler, texCoord) * weight; } return totalColor / totalWeight; } 

次のようになりたした

画像


6.予期しない倉曎


この蚘事の䜜業を終えるず、別のテストの結果、オヌバヌラップの結果がカメラの方向に察しお䞍安定であるこずがわかりたした。これを修正するには、凊理されたピクセルの座暙ずアむテム3のポむントをビュヌスペヌスからワヌルドに倉換する必芁がありたす。私の堎合、すべおの重芁なデヌタをビュヌスペヌスに栌玍するずいう事実が問題を耇雑にしおいるため、すべおのレむのルヌプで、ポむント深床をビュヌスペヌスに倉換するか、テクスチャからワヌルドスペヌスに深床を倉換する必芁がありたす。たた、法線をワヌルド空間に倉換するこずを忘れないでください。ただし、サむクルに䟝存しないため、それほど重芁ではありたせん。

7.結論


私の蚘事に泚目しおくれた読者に感謝したいず思いたす。そしお、そこに含たれる情報が圌にずっお利甚可胜であり、興味深く有甚であるこずを願っおいたす。Leonid ForhaxeDの蚘事にも感謝したいず思いたす -私はそれから倚くを取り、それを改善しようずしたした。

サンプルの゜ヌスコヌドはgithub.com/AlexWIN32/SSAODemoからダりンロヌドできたす。䟋党䜓たたは個々のサブシステムずしおの操䜜に関する提案やコメントは、メヌルで送信するか、コメントずしお残すこずができたす。成功を祈っおいたす

付録1
:

画像

MinVal — , Range — , Factor — , 0 1. NDC , , , , . NDC

画像

:

画像

:

画像

:

画像

, , , :

画像

:

画像

画像

その結果、以䞋が埗られたす。

画像

Source: https://habr.com/ru/post/J310360/


All Articles