• <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>

            永遠(yuǎn)也不完美的程序

            不斷學(xué)習(xí),不斷實(shí)踐,不斷的重構(gòu)……

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            好友鏈接

            最新評(píng)論

            Cascaded shadow map(轉(zhuǎn))

            轉(zhuǎn)自: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的延展技術(shù),Cascaded Shadow Maps是其中比較近期才出現(xiàn)的,而且它引進(jìn)了Cascade(級(jí)聯(lián),層)這個(gè)概念,與另一個(gè)頗為我們中國(guó)人驕傲的名詞PSSM(Parallel- split Shadow Maps)中的Parallel-split指的是同一個(gè)概念。事實(shí)上兩者的原理是基本一樣的。

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

                    這樣做的好處在上篇已經(jīng)講過了。在距離眼睛近的地方,應(yīng)用的是分辨率高的陰影圖,距離眼睛遠(yuǎn)的地方則是低分辨率。這樣是符合視覺特點(diǎn)的,而且沒有什么浪費(fèi)的地方。

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

                    重新回頭看看技術(shù)實(shí)現(xiàn)過程。這里有兩個(gè)主要的技術(shù)點(diǎn),一是“怎么分割視錐”,二是“怎么設(shè)置每個(gè)小視錐的光源投影矩陣”。

                    1. Cascade(Split)的準(zhǔn)則

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

                    后者只是一個(gè)線性式,但它的調(diào)和作用避免了“Splited視錐”的過小與過大,通過一個(gè)mix因子混合兩式子(我在應(yīng)用中默認(rèn)分配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防止前一個(gè)子視錐的遠(yuǎn)裁切面與后一個(gè)子視錐的近裁切面沖突
            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);//結(jié)束分割
            28 }

                    2. Crop  It !

                    針對(duì)每個(gè)光源投影矩陣進(jìn)行的調(diào)整,在CSM/PSSM里稱為Crop(這么有詩(shī)情畫意噶?)。這個(gè)過程其實(shí)很好理解的,我們?cè)谡障嗟臅r(shí)候,一開始要在 CCD液晶屏的畫面上把焦點(diǎn)確定吧——Cascaded Shadow Maps技術(shù)中的光源就是照相者,光源的視像平面就是屏幕,我們是對(duì)每個(gè)“Splited視錐”都照一張相,因?yàn)檎盏氖莄asters,所以可以說是照人 物相片——把casters所在的“Splited視錐”(對(duì)應(yīng)人物背景)在光源投影空間的中心挪移到視像平面的中心,然后進(jìn)行光學(xué)變焦,使人物背景盡量 充滿屏幕,從而突出人物——casters,噢,不,應(yīng)說是shadows。

                    恩,這是個(gè)具有平移和縮放的線性變換——CROP MATRIX,合適地構(gòu)造它,然后乘在光源投影矩陣前面(形成新的投影矩陣),就能完成匹配投影矩陣匹配“Splited視錐”的任務(wù)。假如目前處理第i 個(gè)分割視錐,生成CropMatrix[i],那么對(duì)場(chǎng)景坐標(biāo)系的變換就是:(CropMatrix[i] * LightProjectMatrix) * LightViewMatrix * ModelMatrix * pos。也可認(rèn)為(CropMatrix[i] * LightProjectMatrix)是二次投影,因?yàn)镃rop Matrix實(shí)質(zhì)也是個(gè)投影矩陣,而且是個(gè)名副其實(shí)的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;//當(dāng)前矩陣 
            07     CMatrix16 CropMatrix;//協(xié)調(diào)光源視野與視錐的Crop Matrix                      
            08     
            09     //光源視圖矩陣 
            10     glGetFloatv(GL_MODELVIEW_MATRIX, CurrentMatrix.mt); 
            11     
            12       //生成視錐的AABB特征向量,視錐先經(jīng)CurrentMatrix變換到光源視圖空間 
            13     GetFrustumAABBCoords(frust, maxFrustumCoord, minFrustumCoord, &CurrentMatrix); 
            14     
            15      //計(jì)算給Crop Matrix的調(diào)整參數(shù) 
            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作為遠(yuǎn)近裁切面的正投影矩陣  
            30      glOrtho(-1.0, 1.0, -1.0, 1.0, -maxFrustumCoord.z, -minFrustumCoord.z ); 
            31     
            32 }

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

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

                    先計(jì)算普適意義下的光源投影矩陣和視圖矩陣(類似傳統(tǒng)SM那樣),用它們的積Light-ProjectView把各個(gè)小視錐變換到CLIP投影空間,用 同樣方法得到該空間下的包圍盒(特征向量maxFrustumCoord, minFrustumCoord),這里繼續(xù)計(jì)算的Crop矩陣就需要用到Z值了,因?yàn)槲覀円薷钠渲械膍inFrustumCoord.z。讓它等于 -1——OPENGL在CLIP投影空間的最小坐標(biāo)值。沒錯(cuò),即使該物件在光源正體位置之上,也把它計(jì)算入要投影的物件集(casters)里(況且平行 光源本來該是無限遠(yuǎn)而不是在那個(gè)虛擬位置上的)。最后依然是: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      //計(jì)算給Crop Matrix的調(diào)整參數(shù)
            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了,它與傳統(tǒng)Shadow Map(Shadow Map陰影貼圖技術(shù)之探Ⅰ)一樣,只是根據(jù)像素深度決定用哪張而已。注意,把視錐分割的是近/遠(yuǎn)平面,其值是距視點(diǎn)的距離,定義于視圖空間——把它變換到 眼睛的屏幕CLIP空間,就能在shader里“分割”像素深度,把像素都分到SplitNum個(gè)區(qū)域里(應(yīng)用中我取了4個(gè))。好了,接下來你知道怎么用 if-else來Cast 陰影圖了吧。

            01 // <a href="http://www.ZwqXin.com" title="www.ZwqXin.com">www.ZwqXin.com</a>  Cascaded Shadow Maps
            02 //fragment shader中獲取當(dāng)前像素陰影狀態(tài):
            03 //shadow_color [陰影factor], 還是1.0[表明不貢獻(xiàn)陰影之factor]
            04    
            05 const float shadow_color = 0.3;
            06 const float depth_error = 0.005;
            07 //上面提到的那幾個(gè)分割值,藏在xyz通道了
            08 uniform vec3 frustum_far; 
            09 uniform sampler2DArray shadowmap;
            10    
            11 vec4 shadeFact()
            12 {
            13    int index = 3;
            14      
            15    //決定cascade,應(yīng)用的shadowMap index
            16    //gl_FragCoord(當(dāng)前pixel的x,y窗口坐標(biāo),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      //轉(zhuǎn)換像素位置參量pos, 到光源視覺(Croped)-紋理空間
            32      vec4 shadowTexcoord = gl_TextureMatrix[index] * pos;
            33    
            34      //對(duì)紋理投影,變換到紋理空間的場(chǎng)景坐標(biāo)總作為TEXCOORD,這時(shí)就得自行為之“透視相除”了
            35      //小聲:對(duì)正交投影其實(shí)是不必的。。。
            36      if(shadowTexcoord.w != 1.0)
            37      {
            38         shadowTexcoord = shadowTexcoord / shadowTexcoord.w;
            39      }
            40    
            41      //映射到(0~1)以進(jìn)行紋理檢索
            42      shadowTexcoord = 0.5 * shadowTexcoord + 0.5; 
            43        
            44      //本像素的位置在當(dāng)前空間(光源視覺(Croped)-紋理空間)的實(shí)際深度
            45      float realDepth = shadowTexcoord.z;
            46    
            47      //Texture Array 中以z分量選擇紋理Layer(Shadow Map No.i)
            48      shadowTexcoord.z = float(index); 
            49        
            50     //檢索出Shadow Map中對(duì)應(yīng)位置(x,y)的深度值
            51     float depth =  texture2DArray(shadowmap, shadowTexcoord.xyz).x;
            52    
            53     //當(dāng) depth >= realDepth, 該位置所屬caster 或 no-shadow領(lǐng)域, 輸出陰影分量1.0[無陰影]
            54     //當(dāng) depth <  realDepth, 該位置所屬shadowed領(lǐng)域           , 輸出陰影分量0.0[有陰影]
            55     float diff = depth - realDepth;
            56    
            57     //為了精度問題,如果差值diff是個(gè)很小很小的負(fù)量,把該量設(shè)定為1.0
            58     //當(dāng)diff > -0.005(根據(jù)應(yīng)用調(diào)節(jié)), 認(rèn)為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一些小細(xì)節(jié)的地方,包括caster-receiver-splitedFrustum組合生成的SCREEN DEPENDENT的crop矩陣。最后是這段時(shí)間個(gè)人學(xué)習(xí)Shadow技法的小小總結(jié)。



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

            久久久av波多野一区二区| 久久久婷婷五月亚洲97号色| 久久亚洲美女精品国产精品| 日本亚洲色大成网站WWW久久| 99久久国产亚洲高清观看2024| 青青草原精品99久久精品66| 99久久国产宗和精品1上映| 日本加勒比久久精品| 色播久久人人爽人人爽人人片aV| 99精品久久久久久久婷婷| 久久久九九有精品国产| 久久久久夜夜夜精品国产| 99久久免费国产精精品| 狠狠狠色丁香婷婷综合久久五月| 久久AV高清无码| 久久国产亚洲精品麻豆| 久久精品国产91久久麻豆自制| 精品久久久久久久久中文字幕| 久久这里只有精品久久| 国产激情久久久久影院| 久久综合久久伊人| 2021国产精品久久精品| 久久一日本道色综合久久| 国内精品久久久久久99| 国产99久久久国产精免费| 久久亚洲欧洲国产综合| 亚洲国产精品无码久久久不卡 | 久久九色综合九色99伊人| 久久久99精品一区二区| 亚洲愉拍99热成人精品热久久 | 狠狠色丁香久久婷婷综合五月| .精品久久久麻豆国产精品 | 久久国产视屏| 超级碰碰碰碰97久久久久| 国产亚洲色婷婷久久99精品| 国产午夜电影久久| 国产精品99久久久精品无码| 久久99精品国产一区二区三区| 日韩久久久久中文字幕人妻| 99久久国产宗和精品1上映| 日韩亚洲欧美久久久www综合网|