青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

永遠也不完美的程序

不斷學習,不斷實踐,不斷的重構……

常用鏈接

統計

積分與排名

好友鏈接

最新評論

Cascaded shadow map(轉)

轉自:http://class.gd/content/shadow-map%E9%98%B4%E5%BD%B1%E8%B4%B4%E5%9B%BE%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%8E%A2%E2%85%A2

本文來源:http://www.zwqxin.com/archives/opengl

        上篇里的最后最后,提及了幾種比較有名的Shadow Map的延展技術,Cascaded Shadow Maps是其中比較近期才出現的,而且它引進了Cascade(級聯,層)這個概念,與另一個頗為我們中國人驕傲的名詞PSSM(Parallel- split Shadow Maps)中的Parallel-split指的是同一個概念。事實上兩者的原理是基本一樣的。

        它先在我們的視錐上動手腳,用幾個與近遠平面平行的截面把視錐分成幾份(Parallel-split);然后針對每一份,通過修改光源投影矩陣,使之后 生成的Shadow Map中只有該份“Splited視錐”里的物體;這樣,在pass1階段就生成了幾張針對不同“Splited視錐”的Shadow Maps,在渲染階段,依據像素深度就可以判斷該位置應用哪張Shadow Map了。

        這樣做的好處在上篇已經講過了。在距離眼睛近的地方,應用的是分辨率高的陰影圖,距離眼睛遠的地方則是低分辨率。這樣是符合視覺特點的,而且沒有什么浪費的地方。

        如圖,假設光從視錐正上方射下來(其他方向同理),按CSM的意思,應該把光源視覺下的投影面放在圖示位置(四條短的水平的線)。這里我把視錐分割成四 份,因此需要對應的四張ShadowMap,與人看東西一樣,視像面越靠近陰影(假設位于被投影面,圖中長水平線),看到的陰影越清晰。反映在生成陰影圖 階段,表現為具體caster(被光源直接照射的投射物表面)在光源投影面上占據的范圍大。假設陰影圖尺寸是固定的(譬如1024*1024),在第一個 “Splited視錐”和第四個“Splited視錐”里的投射陰影的物體[投射物]大小也相同(其陰影在實際世界里占地面積必然也相同),則其陰影在陰 影圖里占的像素數會有很大差別(譬如前者占500,000個,后者可能才占5000個),這就是分辨率的差異。最后把ShdowMap帖在場景里(假設在 世界空間下該種投射物的陰影應該占100,000個像素),前者就會比后者效果好很多。(一個是需要進行OverSampling,另一個就得進行 UnderSampling。)所以越靠近眼睛的、越小的Splited視錐里的陰影越高“畫質”,反之則越粗糙(但比起傳統Shadow Map技術也許效果還好一點)——而我們正希望要眼前的事物清晰,遠處的事物模糊甚至不表現出影子也可以——CSM(或者說,PSSM)做到了。

        重新回頭看看技術實現過程。這里有兩個主要的技術點,一是“怎么分割視錐”,二是“怎么設置每個小視錐的光源投影矩陣”。

        1. Cascade(Split)的準則

        從上圖和上分析可以看出,“Splited視錐”沿視線的長度(Zfar - Znear)應該越分越大比較合理,指數增長符合這個規律,但指數增長一般太夸張了,所以配合一個線性增長比較好。在PSSM里,這兩種分法叫 logarithmic split scheme和the uniform split scheme,前者的表達式是經過科學的推導的,這部分也是CSM/PSSM最數學的部分,在GPU GEMS3里有詳細的推導,或者你看PSSM推廣人Fan Zhang [HKUST]那篇"Hardware-Accelerated Parallel-Split Shadow Maps." (IF YOU CAN FIND IT)也該有。它從Shadow-Map Aliasing(dp/ds,單位陰影圖像素單位對應的屏幕像素)的推導開始,找出能滿足使perspective aliasing(由投影縮減效應形成)在各個視錐里均勻分配的分割式。

        后者只是一個線性式,但它的調和作用避免了“Splited視錐”的過小與過大,通過一個mix因子混合兩式子(我在應用中默認分配logarithmic split scheme的因子是0.75,余者0.25):

01 // <a href="http://www.ZwqXin.com" title="www.ZwqXin.com">www.ZwqXin.com</a>  Cascaded Shadow Maps
02 void CCascadingSM::ComputeSplits(float strength, float Dis_Near, float Dis_Far)
03 {
04    float distance_scale = Dis_Far / Dis_Near;
05    
06    splitfrust[0].ResightNear(Dis_Near); //開始分割
07    
08    float partisionFactor = 0.0;
09    float lerpValue1 = 0.0, lerpValue2 = 0.0;
10    float SplitsZ = 0.0;
11    
12     for(int i = 1; i < NumofSplits; ++i)
13     {
14         partisionFactor = i / (float)NumofSplits;
15    
16         lerpValue1 = Dis_Near + partisionFactor * (Dis_Far - Dis_Near);
17    
18         lerpValue2 = Dis_Near * powf(distance_scale, partisionFactor);
19    
20         // 分割面的Z值. 1.005f防止前一個子視錐的遠裁切面與后一個子視錐的近裁切面沖突
21         SplitsZ =  (1-strength) * lerpValue1  + strength * lerpValue2; 
22                      
23         splitfrust[i].ResightNear(SplitsZ * 1.002f);
24         splitfrust[i-1].ResightFar(SplitsZ);
25     }
26    
27     splitfrust[NumofSplits-1].ResightFar(Dis_Far);//結束分割
28 }

        2. Crop  It !

        針對每個光源投影矩陣進行的調整,在CSM/PSSM里稱為Crop(這么有詩情畫意噶?)。這個過程其實很好理解的,我們在照相的時候,一開始要在 CCD液晶屏的畫面上把焦點確定吧——Cascaded Shadow Maps技術中的光源就是照相者,光源的視像平面就是屏幕,我們是對每個“Splited視錐”都照一張相,因為照的是casters,所以可以說是照人 物相片——把casters所在的“Splited視錐”(對應人物背景)在光源投影空間的中心挪移到視像平面的中心,然后進行光學變焦,使人物背景盡量 充滿屏幕,從而突出人物——casters,噢,不,應說是shadows。

        恩,這是個具有平移和縮放的線性變換——CROP MATRIX,合適地構造它,然后乘在光源投影矩陣前面(形成新的投影矩陣),就能完成匹配投影矩陣匹配“Splited視錐”的任務。假如目前處理第i 個分割視錐,生成CropMatrix[i],那么對場景坐標系的變換就是:(CropMatrix[i] * LightProjectMatrix) * LightViewMatrix * ModelMatrix * pos。也可認為(CropMatrix[i] * LightProjectMatrix)是二次投影,因為Crop Matrix實質也是個投影矩陣,而且是個名副其實的Otho正交投影矩陣。

01 // <a href="http://www.ZwqXin.com" title="www.ZwqXin.com">www.ZwqXin.com</a>  Cascaded Shadow Maps 
02 void CCascadingSM::ApplyCropProjectMatrix(CFrustum &frust) 
03 {  
04     CVector3 maxFrustumCoord, minFrustumCoord; 
05     
06     CMatrix16 CurrentMatrix;//當前矩陣 
07     CMatrix16 CropMatrix;//協調光源視野與視錐的Crop Matrix                      
08     
09     //光源視圖矩陣 
10     glGetFloatv(GL_MODELVIEW_MATRIX, CurrentMatrix.mt); 
11     
12       //生成視錐的AABB特征向量,視錐先經CurrentMatrix變換到光源視圖空間 
13     GetFrustumAABBCoords(frust, maxFrustumCoord, minFrustumCoord, &CurrentMatrix); 
14     
15      //計算給Crop Matrix的調整參數 
16     float scaleX = 2.0f/(maxFrustumCoord.x - minFrustumCoord.x); 
17     float scaleY = 2.0f/(maxFrustumCoord.y - minFrustumCoord.y); 
18     float offsetX = -0.5f*(maxFrustumCoord.x + minFrustumCoord.x) * scaleX; 
19     float offsetY = -0.5f*(maxFrustumCoord.y + minFrustumCoord.y) * scaleY; 
20     
21     CropMatrix = CMatrix16(scaleX,    0.0f,  0.0f, 0.0f, 
22                              0.0f,  scaleY,  0.0f, 0.0f, 
23                              0.0f,    0.0f,  1.0f,  0.0f, 
24                           offsetX, offsetY,  0.0f,  1.0f ); 
25     
26    //CropProjectMatrix(光源投影矩陣 = CropMatrix*ProjectZMatrix) 
27      glLoadIdentity(); 
28      glLoadMatrixf(CropMatrix.mt); 
29      //以max_Z和min_Z作為遠近裁切面的正投影矩陣  
30      glOrtho(-1.0, 1.0, -1.0, 1.0, -maxFrustumCoord.z, -minFrustumCoord.z ); 
31     
32 }

        CropMatrix簡直就跟glOrtho生成的矩陣一模一樣,功用也一樣。只不過這里我沒有對Z坐標進行變換,因為把它交給生成光源投影矩陣的 glOrtho了(反而它只變換Z坐標)。前面不是說把坐標都變換到光源投影CLip空間后再提取AABB嗎,為什么就到光源視圖空間就比較了?因為這里 是平行光的投影,所以用的是正交投影glOrtho,在glOrtho中沒有對X,Y坐標進行變換(看看它的spec就知道了,-1與1為參數是不改變 X,Y數值的),所以兩個空間下的X,Y坐標是一致的,而CropMatrix正是只變換X,Y坐標,所以實在沒必要多此一舉。

        但有兩種情況是“需要多此一舉”的。一是光源為點光源且需要透視投影;二是在光源與視錐之間還有其他caster。對第二種情況尤其值得注意??椿匚以谖? 章最上面放的自畫示意圖,有個打了X的地方,那里假設有只bird,那么它會否對地面產生陰影呢?——按照CSM基礎理論,不會!因為 CropMATRIX修改后的光源投影平面已經越過它了,已經看不見它了——我們只能看見視錐里(更準確說是視錐的AABB包圍盒里)的物體所留下的陰 影!解決法是把該物件bondingbox在光源視圖空間下的最大Z坐標作為上述算法最后的minFrustumCoord.z,使光源投影平面恰在該位 置而不再下降。這樣做多了些麻煩,而且該“Splited視錐”對應的Shadow Map的分辨率會降低,物體離視錐越遠,分辨率下降越嚴重。所以,如非必要投射那樣的物體(或者部分穿出視錐之外的物體)的陰影,不必這樣做:

        先計算普適意義下的光源投影矩陣和視圖矩陣(類似傳統SM那樣),用它們的積Light-ProjectView把各個小視錐變換到CLIP投影空間,用 同樣方法得到該空間下的包圍盒(特征向量maxFrustumCoord, minFrustumCoord),這里繼續計算的Crop矩陣就需要用到Z值了,因為我們要修改其中的minFrustumCoord.z。讓它等于 -1——OPENGL在CLIP投影空間的最小坐標值。沒錯,即使該物件在光源正體位置之上,也把它計算入要投影的物件集(casters)里(況且平行 光源本來該是無限遠而不是在那個虛擬位置上的)。最后依然是:CropMatrix[i] *( LightProjectMatrix * LightViewMatrix) * ModelMatrix * pos。

01 // <a href="http://www.ZwqXin.com" title="www.ZwqXin.com">www.ZwqXin.com</a>  Cascaded Shadow Maps  
02     CVector3 maxFrustumCoord, minFrustumCoord; 
03   //.....
04     GetFrustumAABBCoords(frust, maxFrustumCoord, minFrustumCoord, &CurrentMV);
05    
06     minFrustumCoord.z = -1.0f;
07    
08      //計算給Crop Matrix的調整參數
09     float scaleX = 2.0f/(maxFrustumCoord.x - minFrustumCoord.x);
10     float scaleY = 2.0f/(maxFrustumCoord.y - minFrustumCoord.y);
11     float scaleZ  = 2.0f / (maxFrustumCoord.z - minFrustumCoord.z);
12     float offsetX = -0.5f*(maxFrustumCoord.x + minFrustumCoord.x) * scaleX;
13     float offsetY = -0.5f*(maxFrustumCoord.y + minFrustumCoord.y) * scaleY;
14     float offsetZ = -0.5f*(maxFrustumCoord.z + minFrustumCoord.z) * scaleZ;
15    
16     
17     CropMatrix = CMatrix16(scaleX,    0.0f,  0.0f, 0.0f, 
18                              0.0f,  scaleY,  0.0f, 0.0f, 
19                              0.0f,    0.0f,  scaleZ,  0.0f, 
20                           offsetX, offsetY,  0.0f,  1.0f ); 
21 //

        3. Cast 陰影

        通過上面矩陣配合(0,1)映射矩陣之類的生成shadow maps后,這就來到第二PASS了,它與傳統Shadow Map(Shadow Map陰影貼圖技術之探Ⅰ)一樣,只是根據像素深度決定用哪張而已。注意,把視錐分割的是近/遠平面,其值是距視點的距離,定義于視圖空間——把它變換到 眼睛的屏幕CLIP空間,就能在shader里“分割”像素深度,把像素都分到SplitNum個區域里(應用中我取了4個)。好了,接下來你知道怎么用 if-else來Cast 陰影圖了吧。

01 // <a href="http://www.ZwqXin.com" title="www.ZwqXin.com">www.ZwqXin.com</a>  Cascaded Shadow Maps
02 //fragment shader中獲取當前像素陰影狀態:
03 //shadow_color [陰影factor], 還是1.0[表明不貢獻陰影之factor]
04    
05 const float shadow_color = 0.3;
06 const float depth_error = 0.005;
07 //上面提到的那幾個分割值,藏在xyz通道了
08 uniform vec3 frustum_far; 
09 uniform sampler2DArray shadowmap;
10    
11 vec4 shadeFact()
12 {
13    int index = 3;
14      
15    //決定cascade,應用的shadowMap index
16    //gl_FragCoord(當前pixel的x,y窗口坐標,z分量為深度)
17    
18    if(gl_FragCoord.z < frustum_far.x) 
19    {
20      index = 0;
21    }
22    else if(gl_FragCoord.z < frustum_far.y)
23    {
24      index = 1;
25    }
26    else if(gl_FragCoord.z < frustum_far.z)
27    {
28      index = 2;
29    }
30      
31      //轉換像素位置參量pos, 到光源視覺(Croped)-紋理空間
32      vec4 shadowTexcoord = gl_TextureMatrix[index] * pos;
33    
34      //對紋理投影,變換到紋理空間的場景坐標總作為TEXCOORD,這時就得自行為之“透視相除”了
35      //小聲:對正交投影其實是不必的。。。
36      if(shadowTexcoord.w != 1.0)
37      {
38         shadowTexcoord = shadowTexcoord / shadowTexcoord.w;
39      }
40    
41      //映射到(0~1)以進行紋理檢索
42      shadowTexcoord = 0.5 * shadowTexcoord + 0.5; 
43        
44      //本像素的位置在當前空間(光源視覺(Croped)-紋理空間)的實際深度
45      float realDepth = shadowTexcoord.z;
46    
47      //Texture Array 中以z分量選擇紋理Layer(Shadow Map No.i)
48      shadowTexcoord.z = float(index); 
49        
50     //檢索出Shadow Map中對應位置(x,y)的深度值
51     float depth =  texture2DArray(shadowmap, shadowTexcoord.xyz).x;
52    
53     //當 depth >= realDepth, 該位置所屬caster 或 no-shadow領域, 輸出陰影分量1.0[無陰影]
54     //當 depth <  realDepth, 該位置所屬shadowed領域           , 輸出陰影分量0.0[有陰影]
55     float diff = depth - realDepth;
56    
57     //為了精度問題,如果差值diff是個很小很小的負量,把該量設定為1.0
58     //當diff > -0.005(根據應用調節), 認為depth - realDepth >= 0.0[無陰影]
59     diff = diff / depth_error + 1.0;
60    
61     return vec4(diff < 0.0 ? shadow_color : 1.0) ;
62 }

        最后是放出演示DEMO了吧

        在該日志將展示DEMO并淺談一下CSM一些小細節的地方,包括caster-receiver-splitedFrustum組合生成的SCREEN DEPENDENT的crop矩陣。最后是這段時間個人學習Shadow技法的小小總結。



posted on 2010-11-24 10:20 狂爛球 閱讀(5009) 評論(0)  編輯 收藏 引用 所屬分類: 圖形編程

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲视频欧美视频| 久久久99国产精品免费| 亚洲第一区在线| 久久久久国产精品一区| 亚洲国产精品美女| 亚洲精品小视频在线观看| 欧美国产亚洲视频| 香港久久久电影| 欧美中文在线视频| 亚洲日本欧美| 国产精品99久久久久久久久久久久| 欧美日韩亚洲综合一区| 午夜视频在线观看一区| 久久久国产精品一区| 亚洲精品综合精品自拍| 亚洲视频精选| 精品成人久久| 亚洲美女av网站| 国外成人网址| 亚洲国产精品久久| 国产三区二区一区久久| 亚洲电影免费观看高清完整版在线观看| 国内伊人久久久久久网站视频| 亚洲欧美国产精品桃花| 久久久久久伊人| 亚洲专区欧美专区| 老司机精品导航| 欧美一区激情| 欧美激情视频在线免费观看 欧美视频免费一 | 亚洲免费av网站| 国产视频一区在线观看一区免费| 欧美大片在线影院| 国产欧美日韩综合| 亚洲国产欧美一区二区三区久久 | 亚洲欧洲一区二区三区在线观看 | 欧美韩日高清| 久久一二三四| 国产欧美高清| 一本色道久久精品| 亚洲精品久久久久中文字幕欢迎你| 亚洲永久免费| 亚洲一区二区三区激情| 欧美91视频| 欧美 亚欧 日韩视频在线| 国产精品视频xxxx| 99国产一区| 一区二区三区日韩在线观看| 久久久久se| 久久99伊人| 国产精品乱人伦一区二区| 亚洲国产精品一区在线观看不卡| 国产亚洲精品一区二区| 一本色道久久综合亚洲91| 亚洲激情精品| 久久久亚洲人| 久久久久五月天| 国产欧美一级| 亚洲综合色激情五月| 亚洲天堂av在线免费| 欧美好吊妞视频| 亚洲国产一区二区三区高清| 亚洲七七久久综合桃花剧情介绍| 久久裸体视频| 亚洲东热激情| 99精品欧美一区二区三区| 欧美精品色综合| av不卡在线| 亚洲欧美在线一区二区| 国产精品日韩在线播放| 午夜在线视频一区二区区别| 欧美一二三区在线观看| 国产欧美精品| 久久久国际精品| 欧美成人一品| 一区二区三区.www| 国产精品欧美一区二区三区奶水| 亚洲一区二区三区视频播放| 欧美一级久久久| 国产一区日韩一区| 亚洲深夜福利在线| 亚洲国产女人aaa毛片在线| 久久人体大胆视频| 亚洲日本va午夜在线电影| 亚洲视频axxx| 国产亚洲欧洲| 欧美福利专区| 亚洲综合不卡| 欧美大片一区| 亚洲一区三区电影在线观看| 国产精品一区视频| 免费欧美在线| 亚洲一区三区视频在线观看| 你懂的视频一区二区| 一本大道久久a久久精二百| 国产精品区一区二区三区| 久久久久久高潮国产精品视| 亚洲精品欧美| 欧美一区二区三区免费视| 亚洲国产精品久久久久秋霞不卡| 欧美日本三级| 亚洲免费电影在线| 久久久欧美一区二区| 亚洲久久视频| 国产欧美日本在线| 欧美激情导航| 午夜亚洲视频| 亚洲精品日日夜夜| 麻豆91精品91久久久的内涵| 亚洲婷婷综合久久一本伊一区| 国产一区视频网站| 欧美激情网站在线观看| 久久精品国产免费观看| 亚洲精品在线观| 欧美大片一区| 久久综合九色欧美综合狠狠| 亚洲视频国产视频| 亚洲精品久久久久久一区二区| 国产日本欧美一区二区| 欧美视频观看一区| 欧美电影免费| 久久一二三四| 久久精品主播| 久久精品官网| 性欧美激情精品| 中国女人久久久| 亚洲精品国产精品国产自| 老妇喷水一区二区三区| 久久精品男女| 久久aⅴ国产欧美74aaa| 亚洲欧美高清| 亚洲欧美国产高清va在线播| 一本到高清视频免费精品| 亚洲国产精品成人一区二区| 国产一区二区中文| 国产视频在线观看一区| 国产女人18毛片水18精品| 国产精品久久久久久久久婷婷| 欧美日韩亚洲一区在线观看| 欧美日韩国产小视频| 欧美日韩成人在线观看| 欧美美女bb生活片| 欧美另类变人与禽xxxxx| 欧美v亚洲v综合ⅴ国产v| 欧美成人在线免费观看| 欧美成人嫩草网站| 欧美精品免费观看二区| 欧美日韩国产成人高清视频| 欧美色偷偷大香| 国产精品稀缺呦系列在线| 国产欧美69| 激情国产一区二区| 亚洲国产另类久久久精品极度| 亚洲电影免费在线| 日韩午夜激情电影| 在线亚洲激情| 欧美专区在线观看| 欧美影院视频| 欧美中文字幕视频| 久久亚洲私人国产精品va| 久久久久久久久久久成人| 蜜桃av噜噜一区| 欧美日韩国产二区| 国产欧美丝祙| 亚洲国产日韩欧美综合久久| 亚洲美女中文字幕| 欧美亚洲免费高清在线观看| 老司机一区二区三区| 最新中文字幕一区二区三区| 亚洲午夜精品国产| 久久永久免费| 欧美日韩综合一区| 红杏aⅴ成人免费视频| 99视频一区| 久久久久久久网| 亚洲精品欧美日韩专区| 午夜免费在线观看精品视频| 麻豆精品在线播放| 国产精品日韩在线一区| 亚洲国产欧美一区二区三区同亚洲 | 99re66热这里只有精品4| 欧美一区二区三区在线观看 | 亚洲欧美日韩天堂一区二区| 美女脱光内衣内裤视频久久影院 | 久久精品夜色噜噜亚洲aⅴ| 欧美激情一区二区三区在线视频观看 | 亚洲精品免费在线| 久久99伊人| 国产精品国内视频| 亚洲精品在线视频观看| 久久福利精品| 亚洲午夜高清视频| 欧美不卡视频| 在线观看久久av| 久久激情五月激情| 日韩一级在线| 欧美激情第五页| 亚洲国产精品ⅴa在线观看| 久久电影一区| 亚洲无线观看| 欧美日韩日韩|