重疊的陰影圖- Win32 apps
文章推薦指數: 80 %
shadow map coverage ... cascade overlap increases as light direction becomes parallel ... Interval-Based Selection 與Map-Based Selection.
跳到主要內容
已不再支援此瀏覽器。
請升級至MicrosoftEdge,以利用最新功能、安全性更新和技術支援。
下載MicrosoftEdge
其他資訊
目錄
結束焦點模式
閱讀英文
儲存
目錄
閱讀英文
儲存
Twitter
LinkedIn
Facebook
電子郵件
WeChat
目錄
重疊的陰影圖
發行項
05/12/2022
5位參與者
此頁面有所助益嗎?
Yes
No
還有其他意見反應嗎?
系統會將意見反應傳送給Microsoft:按下[提交]按鈕,您的意見反應將用來改善Microsoft產品和服務。
隱私權原則。
送出
謝謝。
本文內容
重迭陰影貼圖(CSM)是對抗其中一個最普遍錯誤與陰影:檢視方塊別名的最佳方法。
本技術文章假設讀者熟悉陰影對應,可處理CSM的主題。
具體而言,它會:
說明CSM的複雜度;
提供CSM演算法可能變化的詳細資料;
描述兩種最常見的篩選技術:(PCF)百分比更接近篩選,並使用差異陰影圖篩選(VSM);
識別並解決與將篩選新增至CSM相關聯的一些常見陷阱;和
示範如何將CSM對應至Direct3D10到Direct3D11硬體。
本文中使用的程式碼可以在CascadedShadowMaps11和VarianceShadows11範例中的DirectX軟體發展工具組(SDK)中找到。
本文在實作技術文章中涵蓋的技術之後,實作改善陰影深度的常見技術地圖之後,將證明最有用。
串聯陰影地圖和檢視方塊別名
陰影圖中的檢視方塊別名是要克服的最困難問題之一。
在技術文章中,會描述改善陰影深度的常見技術地圖、檢視方塊別名,以及識別出一些減輕問題的方法。
在實務上,CSM通常會是最佳解決方案,而且通常用於新式遊戲。
CSM的基本概念很容易瞭解。
相機範圍的不同區域需要具有不同解析度的陰影圖。
最接近眼睛的物件需要比遠距物件更高的解析度。
事實上,當眼睛非常接近幾何時,最接近眼睛的圖元可能需要太多解析度,即使4096×4096陰影圖也不足。
CSM的基本概念是將frustum分割成多個frusta。
每個子圖形都會轉譯陰影圖;然後,圖元著色器會從地圖取樣,最符合所需的解析度(圖2)。
圖1.陰影圖涵蓋範圍
在圖1中,品質會顯示(從最高到最低)由左至右。
代表陰影貼圖的一系列格線,其檢視外框(紅色圓錐形)顯示圖元涵蓋範圍如何受到不同解析度陰影圖的影響。
當光線空間中有1:1比例的圖元與陰影圖中的紋素時,陰影是最高品質的(白色圖元)。
當太多圖元對應至相同的陰影材質時,檢視方塊別名會以大型、封鎖紋理貼圖的形式(左影像)。
當陰影圖太大時,會進行取樣。
在此情況下,會略過紋素、引進填充成品,並影響效能。
圖2.CSM陰影品質
圖2顯示圖1中每個陰影圖中最高品質區段的剪下。
最接近位置圖元的陰影圖(位於頂點)最接近眼睛。
技術上,這些是大小相同的地圖,使用白色和灰色來示範串聯陰影圖的成功。
白色很理想,因為它會顯示良好的涵蓋範圍,這是眼球空間圖元和陰影貼圖紋素的1:1比例。
CSM需要每個畫面格的下列步驟。
將frustum分割成subfrusta。
計算每個子集的正視投影。
轉譯每個子圖形的陰影圖。
轉譯場景。
系結陰影圖和轉譯。
頂點著色器會執行下列動作:
除非在圖元著色器)中計算所需的紋理座標,否則計算每個光線子(的紋理座標。
轉換並亮起頂點等等。
圖元著色器會執行下列動作:
決定適當的陰影圖。
視需要轉換紋理座標。
取樣串聯。
將圖元亮起。
分割Frustum
分割frustum是建立subfrusta的動作。
分割Frustum的其中一個技巧是計算Z方向中從零%到100%的間隔。
然後,每個間隔都會以Z軸的百分比表示接近平面和遠平面。
圖3.任意檢視分割的frustum
實際上,重新計算每個畫面的Frustum分割會導致陰影邊緣閃爍。
一般接受的做法是針對每個案例使用一組靜態串聯間隔。
在此案例中,Z軸上的間隔是用來描述分割frustum時所發生的子伺服器。
判斷指定場景的正確大小間隔取決於數個因素。
場景幾何的方向
相對於場景幾何,相機方向會影響串聯間隔選取。
例如,非常接近地面的相機,例如足球遊戲中的地面相機,具有與天景相機不同的靜態串聯間隔集。
圖4顯示一些不同的相機及其各自的分割區。
當場景的Z範圍非常大時,需要更多分割平面。
例如,當眼睛非常接近地面平面,但遠距的物件仍然可見時,可能需要多個串聯。
分割frustum,讓更多分割接近眼睛(其中檢視別名會變更最快)也很重要。
當大部分的幾何都壓縮成小型區段(例如額外負荷檢視或飛行模擬器)檢視frustum時,需要較少的串聯。
圖4.不同的組態需要不同的Frustum分割
(Left)當幾何在Z中有高動態範圍時,需要許多串聯。
(中心)當幾何在Z中具有低動態範圍時,多個Frustum幾乎沒有好處。
(Right)動態範圍為中時,只需要三個分割區。
淺色和相機的方向
每個串聯的投影矩陣會緊密配合其對應的子範圍。
在檢視相機和光線方向為正交的組態中,串聯可以緊密配合,且重迭很少。
當光線和檢視相機移到平行對齊(圖5)時,重迭會變得更大。
當光線和檢視相機幾乎平行時,其稱為「duelingfrusta」,而且是大部分陰影演算法的困難案例。
限制光線和相機並不常見,因此不會發生此案例。
不過,CSM的執行效能優於此案例中的其他許多演算法。
圖5.重迭重迭會隨著光線方向與相機方向平行增加
許多CSM實作都使用固定大小的frusta。
圖元著色器可以使用Z深度,在frustum以固定大小的間隔分割時,將串連陣列編制索引。
計算View-Frustum系結
選取frustum間隔之後,會使用兩者之一來建立subfrusta:適合場景並符合串聯。
符合場景
所有frusta都可以使用相同的接近平面來建立。
這會強制重迭串聯。
CascadedShadowMaps11範例會呼叫這項技術適合場景。
調整為Cascade
或者,您也可以使用實際分割間隔來建立frusta,做為接近和遠平面。
這會導致更緊密地配合,但在因應frusta的情況下,會因應場景而變質。
CascadedShadowMaps11範例會呼叫這項技術適合串聯。
圖6顯示這兩種方法。
適合串聯浪費較少的解析度。
符合串聯的問題在於,正視投影方向會根據檢視範圍成長和縮小。
適合場景的技術會以檢視範圍的最大大小來填補正視投影,移除檢視相機移動時出現的成品。
改善陰影深度的常見技巧地圖解決在「移動紋素大小遞增的光線」一節中出現的成品。
圖6.符合場景大小與重迭顯示大小
轉譯陰影圖
CascadedShadowMaps11範例會將陰影圖轉譯成一個大型緩衝區。
這是因為紋理陣列上的PCF是Direct3D10.1功能。
針對每個串聯,會建立一個檢視區,其中涵蓋對應至該串聯之深度緩衝區的區段。
Null圖元著色器系結,因為只需要深度。
最後,每個串聯都會設定正確的檢視區和陰影矩陣,因為深度圖一次轉譯成主要陰影緩衝區。
轉譯場景
包含陰影的緩衝區現在會系結至圖元著色器。
有兩種方法可用來選取CascadedShadowMaps11範例中實作的串聯。
這兩種方法會使用著色器程式碼來說明。
Interval-Based串聯選取
圖7.以間隔為基礎的串聯選取
在以間隔為基礎的選取(圖7)中,頂點著色器會計算頂點世界空間中的位置。
Output.vDepth=mul(Input.vPosition,m_mWorldView).z;
圖元著色器會收到插入深度。
fCurrentPixelDepth=Input.vDepth;
以間隔為基礎的串聯選取會使用向量比較和點乘積來判斷正確的cacade。
CASCADE_COUNT_FLAG指定串聯的數目。
m_fCascadeFrustumsEyeSpaceDepths_data會限制檢視frustum分割區。
比較之後,fComparison會包含值1,其中目前的圖元大於屏障,而當目前的串聯較小時,值為0。
點乘積會將這些值加總成陣列索引。
float4vCurrentPixelDepth=Input.vDepth;
float4fComparison=(vCurrentPixelDepth>m_fCascadeFrustumsEyeSpaceDepths_data[0]);
floatfIndex=dot(
float4(CASCADE_COUNT_FLAG>0,
CASCADE_COUNT_FLAG>1,
CASCADE_COUNT_FLAG>2,
CASCADE_COUNT_FLAG>3)
,fComparison);
fIndex=min(fIndex,CASCADE_COUNT_FLAG);
iCurrentCascadeIndex=(int)fIndex;
選取串聯之後,紋理座標必須轉換成正確的串聯。
vShadowTexCoord=mul(InterpolatedPosition,m_mShadow[iCascadeIndex]);
然後,這個紋理座標會用來取樣具有X座標和Y座標的紋理。
Z座標是用來進行最終深度比較。
Map-Based串聯選取
以地圖為基礎的選取(圖8)會針對串聯的四邊進行測試,以尋找涵蓋特定圖元的最緊密地圖。
頂點著色器會計算每個串聯的檢視空間位置,而不是計算世界空間中的位置。
圖元著色器會逐一查看串聯,以縮放和移動紋理座標,以便為目前的串聯編制索引。
接著會根據紋理界限測試紋理座標。
當紋理座標的X和Y值落在串聯內時,它們會用來取樣紋理。
Z座標是用來進行最終深度比較。
圖8.以地圖為基礎的串聯選取
Interval-BasedSelection與Map-BasedSelection
以間隔為基礎的選取比以地圖為基礎的選取專案稍微快一點,因為可以直接進行串聯選取。
地圖型選取範圍必須與串聯界限交集紋理座標。
當陰影圖不完全對齊時,地圖型選取範圍會更有效率地使用串聯(請參閱圖8)。
在串聯之間混合
本文稍後(討論的VSM)和篩選技術,例如PCF可以搭配低解析度CSM使用,以產生軟陰影。
不幸的是,這會導致串聯層之間的可見接合(圖9),因為解析度不相符。
解決方案是在陰影圖之間建立陰影圖之間的帶狀圖,其中會針對這兩個串聯執行陰影測試。
接著,著色器會根據混合帶中的圖元位置,在兩個值之間線性插補。
CascadedShadowMaps11和VarianceShadows11範例提供GUI滑杆,可用來增加和減少此模糊帶。
著色器會執行動態分支,讓大部分的圖元只能從目前的串聯讀取。
圖9.串聯接縫
(左)可見接縫可看到重迭重迭的位置。
(Right)在混合串聯時,不會發生接縫。
篩選陰影地圖
PCF
篩選一般陰影貼圖不會產生柔和模糊的陰影。
篩選硬體會模糊深度值,然後將這些模糊值與光線空間紋素進行比較。
因為通過/失敗測試所產生的硬邊緣仍然存在。
模糊陰影圖只能用來錯誤地移動硬邊緣。
PCF可讓您篩選陰影圖。
PCF的一般概念是根據通過子取樣總數的子取樣數目,計算陰影中圖元的百分比。
Direct3D10和Direct3D11硬體可以執行PCF。
PCF取樣器的輸入是由紋理座標和比較深度值所組成。
為了簡單起見,PCF會以四點選篩選來說明。
紋理取樣器會讀取紋理四次,類似于標準篩選準則。
不過,傳回的結果是通過深度測試的圖元百分比。
圖10顯示通過四個深度測試之一的圖元在陰影中為25%。
傳回的實際值是根據紋理讀取的子材質座標來產生平滑漸層的線性插補。
如果沒有這個線性插補,四點數PCF只能傳回五個值:{0.0,0.25,0.5,0.75,1.0}。
圖10.PCF篩選的影像,其中涵蓋25%的選取圖元
您也可以在沒有硬體支援的情況下執行PCF,或將PCF擴充至較大的核心。
某些技術甚至使用加權核心進行取樣。
若要這樣做,請建立核心(,例如N×N方格的Gaussian)。
權數最多必須加1。
紋理接著會取樣N2次。
每個樣本都會依核心中的對應權數來調整。
CascadedShadowMaps11範例會使用此方法。
深度偏差
使用大型PCF核心時,深度偏差會變得更重要。
只有在深度圖中,才能比較圖元的光線空間深度與其對應至深度圖的圖元。
深度圖紋素的芳鄰是指不同的位置。
此深度可能類似,但視場景而定,可能會非常不同。
圖11醒目提示所發生的成品。
單一深度會與陰影圖中的三個鄰近紋素進行比較。
其中一個深度測試錯誤失敗,因為其深度不會與目前幾何的計算光線空間深度相互關聯。
此問題的建議解決方案是使用較大的位移。
不過,太大的位移可能會導致PeterPanning。
計算緊密近平面和遠平面有助於減少使用位移的效果。
圖11.錯誤的自我陰影
錯誤的自我陰影結果,是比較光線空間深度中的圖元與陰影圖中未相互關聯的紋素。
光線空間中的深度會與深度圖中的陰影紋2相互關聯。
紋素1大於光線空間深度,而2相等且3小於。
紋素2和3通過深度測試,而紋素1則失敗。
使用DDX和DDY計算大型PCF的Per-Texel深度偏差
針對大型PCF使用ddx和ddy計算每個紋素深度偏差,是一種計算正確深度偏差的技術,假設表面是平面,適用于相鄰陰影圖紋素。
這項技術使用衍生資訊,將比較深度放入平面。
由於這項技術在計算上很複雜,因此只有在GPU有要備援的計算週期時,才應該使用它。
使用非常大的核心時,這可能是唯一可用來移除自我陰影成品的技術,而不會造成PeterPanning。
圖12醒目提示問題。
光線空間的深度已知于要比較的一個紋素。
對應至深度圖中鄰近紋素的光線空間深度未知。
圖12.場景和深度地圖
呈現的場景會顯示在左側,而具有範例紋素區塊的深度圖會顯示在右側。
眼睛空間紋素會對應至區塊中央標示為D的圖元。
此比較正確。
與鄰近D的圖元相互關聯的正確眼球空間深度未知。
只有當我們假設圖元與D相同三角形時,才能將鄰近的紋素對應回眼球空間。
深度對於與光線空間位置相互關聯的紋素而言是已知的。
深度圖中鄰近紋素的深度未知。
概括而言,這項技術會使用ddx和ddyHLSL作業來尋找光線空間位置的衍生專案。
這是非常數,因為衍生作業會傳回與螢幕空間相關的光線空間深度漸層。
若要將此轉換成光線空間深度的漸層,必須計算轉換矩陣。
著色器程式碼的說明
演算法其餘部分的詳細資料會以執行這項作業的著色器程式碼說明提供。
您可以在CascadedShadowMaps11範例中找到此程式碼。
圖13顯示光線空間紋理座標如何對應至深度圖,以及如何使用X和Y中的衍生專案來建立轉換矩陣。
圖13.螢幕空間到光線空間矩陣
X和Y中光線空間位置的衍生專案是用來建立此矩陣。
第一個步驟是計算光線檢視空間位置的衍生。
float3vShadowTexDDX=ddx(vShadowMapTextureCoordViewSpace);
float3vShadowTexDDY=ddy(vShadowMapTextureCoordViewSpace);
Direct3D11類別GPU會以平行方式執行2×2四個圖元的四個圖元,並針對ddx從X中的鄰近減去紋理座標,以及從Y的Y中計算這些衍生專案。
這兩個衍生專案組成2×2矩陣的資料列。
在此矩陣的目前形式中,此矩陣可用來將螢幕空間鄰近圖元轉換成光線空間斜率。
不過,需要此矩陣的反轉。
需要將光線空間鄰近圖元轉換成螢幕空間斜率的矩陣。
float2x2matScreentoShadow=float2x2(vShadowTexDDX.xy,vShadowTexDDY.xy);
floatfInvDeterminant=1.0f/fDeterminant;
float2x2matShadowToScreen=float2x2(
matScreentoShadow._22*fInvDeterminant,
matScreentoShadow._12*-fInvDeterminant,
matScreentoShadow._21*-fInvDeterminant,
matScreentoShadow._11*fInvDeterminant);
圖14.淺色空間到螢幕空間
接著,此矩陣會用來轉換目前材質上方和右邊的兩個紋素。
這些芳鄰會以目前材質的位移表示。
float2vRightShadowTexelLocation=float2(m_fTexelSize,0.0f);
float2vUpShadowTexelLocation=float2(0.0f,m_fTexelSize);
float2vRightTexelDepthRatio=mul(vRightShadowTexelLocation,
matShadowToScreen);
float2vUpTexelDepthRatio=mul(vUpShadowTexelLocation,
matShadowToScreen);
矩陣建立的比例最後乘以深度衍生專案,以計算鄰近圖元的深度位移。
floatfUpTexelDepthDelta=
vUpTexelDepthRatio.x*vShadowTexDDX.z
+vUpTexelDepthRatio.y*vShadowTexDDY.z;
floatfRightTexelDepthDelta=
vRightTexelDepthRatio.x*vShadowTexDDX.z
+vRightTexelDepthRatio.y*vShadowTexDDY.z;
這些權數現在可以用於PCF迴圈,以將位移新增至位置。
for(intx=m_iPCFBlurForLoopStart;x
延伸文章資訊
- 1LearnOpenGL - CSM
Cascaded shadow mapping is a direct answer to the third point, however while implementing it we w...
- 2重疊的陰影圖- Win32 apps
shadow map coverage ... cascade overlap increases as light direction becomes parallel ... Interva...
- 3Cascaded Shadow Maps(CSM)实时阴影的原理与实现 - 知乎专栏
本文翻译自: https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascade...
- 4Cascaded Shadow Maps(CSM)實時陰影的原理與實現 - GetIt01
本文翻譯自:https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded...
- 5Cascaded Shadow maps not quite right - Stack Overflow
I've mostly implemented cascading shadow maps (CSM), ... And samples the shadow map array with th...