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

永遠也不完美的程序

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

常用鏈接

統計

積分與排名

好友鏈接

最新評論

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精品免费| 亚洲自拍偷拍麻豆| 亚洲女女女同性video| 亚洲你懂的在线视频| 午夜精品视频网站| 久久激情网站| 久久久久久免费| 欧美激情亚洲视频| 国产精品色一区二区三区| 国产亚洲欧美日韩一区二区| 在线精品亚洲| 一区二区三区产品免费精品久久75| 一本色道精品久久一区二区三区| 亚洲综合精品一区二区| 久久九九国产| 亚洲黄色三级| 99综合电影在线视频| 亚洲免费中文字幕| 久久久久在线观看| 欧美亚洲不卡| 亚洲黄色成人| 午夜日本精品| 亚洲人体大胆视频| 久久久久久久久久久久久久一区| 欧美激情aⅴ一区二区三区| 国产精品婷婷午夜在线观看| …久久精品99久久香蕉国产| 一本色道久久综合亚洲精品不卡 | 午夜精品三级视频福利| 久久青草福利网站| 日韩午夜黄色| 久久蜜桃精品| 国产精品一区在线播放| 亚洲精品乱码视频| 玖玖综合伊人| 欧美中文字幕不卡| 国产精品久久二区二区| 在线日本高清免费不卡| 欧美一二三视频| 日韩一区二区精品视频| 男人插女人欧美| 1000部国产精品成人观看| 久久精品九九| 小黄鸭精品密入口导航| 欧美午夜国产| 亚洲综合社区| 国产精品美女www爽爽爽| 欧美a级一区| 黄色成人av在线| 久久精品三级| 午夜伦欧美伦电影理论片| 欧美日韩高清在线播放| 亚洲人成高清| 免费不卡在线视频| 久久久久**毛片大全| 国产一区成人| 久久天堂成人| 久久都是精品| 韩国av一区| 美女91精品| 免费观看不卡av| 91久久精品国产91久久性色| 美女91精品| 男人天堂欧美日韩| 99国内精品久久| 一二三区精品| 国产精品久久久久一区| 亚洲欧美日韩视频二区| 亚洲永久免费av| 国产一区二区三区精品欧美日韩一区二区三区| 亚洲欧美国产视频| 午夜精品视频| 在线高清一区| 日韩天堂在线观看| 国产精品视频免费一区| 欧美在线网址| 久久久天天操| 一区二区不卡在线视频 午夜欧美不卡在 | 午夜亚洲视频| 亚洲国产精品一区二区久| 牛牛国产精品| 欧美日韩国产丝袜另类| 亚洲欧美中文另类| 久久久久女教师免费一区| 亚洲精品乱码久久久久久按摩观| 亚洲精品少妇30p| 国产精品丝袜白浆摸在线| 久久精品伊人| 欧美国产综合视频| 久久精品视频免费观看| 嫩草伊人久久精品少妇av杨幂| 一区二区三区毛片| 久久精品二区三区| 亚洲永久在线观看| 久久综合伊人| 亚洲男人av电影| 久久一区二区视频| 小嫩嫩精品导航| 9色porny自拍视频一区二区| 亚洲综合久久久久| 亚洲精品偷拍| 久久国产精品免费一区| 在线性视频日韩欧美| 欧美一区二区精美| 欧美日韩国产免费| 欧美日韩一二三四五区| 羞羞色国产精品| 国产一区 二区 三区一级| 欧美在线视频二区| 欧美—级在线免费片| 久久爱另类一区二区小说| 欧美日韩国语| 亚洲激情黄色| 在线观看日韩av电影| 一本色道久久综合| 9i看片成人免费高清| 久久亚洲综合网| 久久综合一区二区三区| 国产精自产拍久久久久久| 日韩视频精品在线| 日韩一区二区福利| 欧美激情aaaa| 亚洲电影在线看| 激情久久久久久久| 久久精品水蜜桃av综合天堂| 欧美成人精品在线播放| 久久久综合网站| 国产综合婷婷| 欧美一区二区视频在线观看2020| 亚洲综合色网站| 国产精品日韩在线一区| 亚洲无限乱码一二三四麻| 日韩图片一区| 欧美欧美在线| 亚洲精品少妇| 亚洲综合大片69999| 国产精品手机视频| 亚洲免费在线看| 久久久91精品国产一区二区三区| 国产欧美在线看| 欧美在线首页| 免费亚洲网站| 欧美在线一二三四区| 老司机凹凸av亚洲导航| 黄色精品一区| 欧美激情一区二区在线| 99视频日韩| 欧美在线黄色| 亚洲夫妻自拍| 欧美日韩精品一区二区天天拍小说| 亚洲精品视频在线观看免费| 亚洲特级片在线| 国产日韩欧美不卡| 六月婷婷一区| 这里只有精品视频在线| 久久久在线视频| 亚洲黄色成人| 国产精品h在线观看| 久久大逼视频| 亚洲精品在线二区| 久久精品免视看| 亚洲精品少妇30p| 国产毛片久久| 免费在线观看成人av| 在线综合亚洲欧美在线视频| 老巨人导航500精品| 99国产成+人+综合+亚洲欧美| 欧美日韩在线一区| 欧美伊久线香蕉线新在线| 亚洲国产精品成人久久综合一区| 99精品国产热久久91蜜凸| 欧美一区二区视频在线| 亚洲国产精品一区二区www在线| 欧美成人乱码一区二区三区| 在线看视频不卡| 国产精品久久久久久久久久直播| 久久精品久久99精品久久| 亚洲免费观看高清完整版在线观看熊 | 久久中文字幕一区| 一区二区三区 在线观看视频| 久久久国产精品一区| 亚洲精品在线免费| 国产在线精品一区二区中文| 欧美激情视频免费观看| 亚洲欧美影音先锋| 亚洲精品在线观看视频| 麻豆av一区二区三区久久| 亚洲免费一在线| 亚洲人久久久| 亚洲国产成人在线| 国产午夜亚洲精品羞羞网站 | 国产欧美1区2区3区| 欧美日韩不卡| 欧美成人精品在线| 久色婷婷小香蕉久久| 午夜在线a亚洲v天堂网2018|