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

            實(shí)時(shí)陰影繪制技術(shù)研究

            C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
              48 Posts :: 20 Stories :: 57 Comments :: 0 Trackbacks

            GameRes游戲開發(fā)資源網(wǎng) http://www.gameres.com

            軟陰影

            www.GameDev.net 作者:Anirudh.S Shastry 

            http://www.gamedev.net/reference/articles/article2193.asp

            譯者:赟

            介紹... 1

            近況... 1

            軟陰影... 2

            那么它如何工作?... 2

            步驟一:渲染陰影映射圖(shadow map... 2

            步驟二:將帶陰影的場景渲染到緩沖中... 4

            步驟三:對(duì)屏幕緩沖進(jìn)行模糊... 7

            步驟四:渲染帶陰影的場景... 11

            參考文獻(xiàn)... 13

             

             

            介紹

            最初,動(dòng)態(tài)陰影技術(shù)只有在有限的幾種情況下才能實(shí)現(xiàn)。但是,隨著強(qiáng)大的可編程圖形硬件的面世,動(dòng)態(tài)陰影技術(shù)已經(jīng)完全取代了以前的如light map這樣的靜態(tài)陰影技術(shù)及像projected shadows這樣的半動(dòng)態(tài)陰影技術(shù)。目前兩種流行的動(dòng)態(tài)陰影技術(shù)分別是shadow volumesshadow mapping

             

            近況

            shadow volumes技術(shù)是一種基于幾何形體的技術(shù),它需要幾何體在一定方向的燈光下的輪廓去產(chǎn)生一個(gè)封閉的容積,然后通過光線的投射就可以決定場景的陰影部分(常常使用模板緩沖去模擬光線的投射)。這項(xiàng)技術(shù)是像素精確的,不會(huì)產(chǎn)生任何的鋸齒現(xiàn)象,但是與其他的技術(shù)一樣,它也有缺點(diǎn)。最主要的兩個(gè)問題一是極度依賴幾何形體,二是需要非常高的填充率。由于這些缺點(diǎn),使得shadow mapping技術(shù)漸漸地變得更為流行起來。

            陰影映射技術(shù)是一種圖像空間的技術(shù),它首先在以光源位置作為視點(diǎn)的情況下渲染整個(gè)場景的深度信息,然后再使用這些深度信息去決定場景的哪一部分是處于陰影之中。雖然這項(xiàng)技術(shù)有許多優(yōu)點(diǎn),但它有鋸齒現(xiàn)象并且依賴z-緩沖技術(shù)。不過它的優(yōu)點(diǎn)足以抵消它的這些缺點(diǎn),因此本文選用了這項(xiàng)技術(shù)。

             

            軟陰影

            硬陰影破壞了場景的真實(shí)性,因此,我們必須仿造軟陰影來提升場景的可視效果。許多狂熱的學(xué)者都拿出了描述軟陰影技術(shù)的論文。但實(shí)際上,這些技術(shù)大部分都是很難在一個(gè)較為復(fù)雜的場景下實(shí)現(xiàn)實(shí)時(shí)效果。直到我們擁有了能克服這些技術(shù)局限性的硬件后,我們才真正的采用了這些方法。

            本文采用了基于圖像空間的方法,并利用shadow mapping技術(shù)來產(chǎn)生軟陰影。這個(gè)方法不能產(chǎn)生完美的陰影,因?yàn)闆]有真正的模擬出本影和半影,但它不僅僅可以解決陰影映射技術(shù)的鋸齒現(xiàn)象,還能以賞心悅目的軟陰影來提升場景的可視效果。

             

            那么它如何工作?

            首先,我們生成陰影映射圖(shadow map),具體方法是以光源位置為視點(diǎn),將場景的深度信息渲染到浮點(diǎn)格式的緩沖中去。然后我們不是像通常那樣在陰影下渲染場景,而是將陰影區(qū)域渲染到一幅屏幕大小的緩沖中去,這樣就可以使用bloom filter進(jìn)行模糊并將它投射回屏幕空間中使其顯示在屏幕上。是不是很簡單?

            本文只處理了聚光燈源這種情況,但可以很方便的推廣到點(diǎn)光源上。

            下面是具體步驟:

            通過將深度信息寫入浮點(diǎn)紋理的方法產(chǎn)生陰影映射圖(shadow map)。

            深度比較后將場景的陰影部分渲染到定點(diǎn)紋理,此時(shí)不要任何的燈光。

            使用bloom filter模糊上一步的紋理,本文采用了separable Gaussian filter,也可用其他的方法。

            在所有的光源下將上一步模糊后的紋理投射到屏幕空間中,從而得到最終的效果。

            步驟:渲染陰影映射圖(shadow map

            首先,我們需要?jiǎng)?chuàng)建一個(gè)能保存屏幕深度信息的紋理。因?yàn)橐堰@幅紋理作為render target,所以我們還要?jiǎng)?chuàng)建一個(gè)表面(surface)來保存紋理的表面信息。由于深度信息值的范圍很大因此這幅紋理必須是浮點(diǎn)類型的。R32F的格式有足夠的精度可以滿足我們的需要。下面是創(chuàng)建紋理的代碼片斷:

            // Create the shadow map 
            if( FAILED( g_pd3dDevice->CreateTexture( SHADOW_MAP_SIZE,
                                 SHADOW_MAP_SIZE, 1, D3DUSAGE_RENDERTARGET,
                                 D3DFMT_R32F, D3DPOOL_DEFAULT, &g_pShadowMap,
                                 NULL ) ) )
            {
               MessageBox( g_hWnd, "Unable to create shadow map!",
                           "Error", MB_OK | MB_ICONERROR );
               return E_FAIL;
            }
             
            // Grab the texture's surface
            g_pShadowMap->GetSurfaceLevel( 0, &g_pShadowSurf );

             

            為了完成陰影映射圖,我們要把場景的深度信息渲染到陰影映射圖中。為此在光源的世界-視點(diǎn)-投影變換矩陣(world-view-projection matrix)下渲染整個(gè)場景。下面是構(gòu)造這些矩陣的代碼:

            // Ordinary view matrix 
            D3DXMatrixLookAtLH( &matView, &vLightPos, &vLightAim, &g_vUp );
            // Projection matrix for the light
            D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian(30.0f),
            1.0f, 1.0f, 1024.0f );

            //實(shí)際上作者在例程中使用的是D3DXMatrixOrthoLH( &matProj, 45.0f, 45.0f, 1.0f, //1024.0f )。這個(gè)函數(shù)所構(gòu)造的project矩陣與D3DXMatrixPerspectiveFovLH()構(gòu)造的//不同之處在于:它沒有透視效果。即物體的大小與視點(diǎn)和物體的距離沒有關(guān)系。顯然例//程中模擬的是平行光源(direction light),而這里模擬的是聚光燈源(spot light不知翻譯得對(duì)不對(duì)?)

            // Concatenate the world matrix with the above 
            // two to get the required matrix
            matLightViewProj = matWorld * matView * matProj;
             

            下面是渲染場景深度的頂點(diǎn)渲染和像素渲染的代碼:

             

            // Shadow generation vertex shader
            struct VSOUTPUT_SHADOW
            {
               float4 vPosition    : POSITION;
               float  fDepth       : TEXCOORD0;
            };
             
            VSOUTPUT_SHADOW VS_Shadow( float4 inPosition : POSITION )
            {
               // Output struct
               VSOUTPUT_SHADOW OUT = (VSOUTPUT_SHADOW)0;
               // Output the transformed position
               OUT.vPosition = mul( inPosition, g_matLightViewProj );
               // Output the scene depth
               OUT.fDepth = OUT.vPosition.z;
               return OUT;
            }

            這里我們將頂點(diǎn)的位置與變換矩陣相乘,并將變換后的z值作為深度。在像素渲染中將深度值以顏色(color)的方式輸出。

            float4  PS_Shadow( VSOUTPUT_SHADOW IN ) : COLOR0
            {
               // Output the scene depth
               return float4( IN.fDepth, IN.fDepth, IN.fDepth, 1.0f );
            }

            瞧,我們完成了陰影映射圖,下面就是以顏色方式輸出的陰影映射圖,深藍(lán)色部分表明較小的深度值,淺藍(lán)色部分表明較大的深度值。

            步驟二:將帶陰影的場景渲染到緩沖中

            下面,我們要把場景的帶陰影的部分渲染到并不立即顯示的緩沖中,使我們可以進(jìn)行模糊處理,然后再將它投射回屏幕。首先把場景的陰影部分渲染到一幅屏幕大小的定點(diǎn)紋理中。

            // Create the screen-sized buffer map 
            if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH,
                        SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET,
                        D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                        &g_pScreenMap, NULL ) ) )
            {
               MessageBox( g_hWnd, "Unable to create screen map!",
                           "Error", MB_OK | MB_ICONERROR );
               return E_FAIL;
            }
            // Grab the texture's surface
            g_pScreenMap->GetSurfaceLevel( 0, & g_pScreenSurf );

            為了獲得投影紋理坐標(biāo)(projective texture coordinates),我們需要一個(gè)紋理矩陣,作用是把投影空間(projection space)中的位置變換到紋理空間(texture space)中去。

            // Generate the texture matrix
            float fTexOffs = 0.5 + (0.5 / (float)SHADOW_MAP_SIZE);
            D3DXMATRIX matTexAdj( 0.5f,     0.0f,      0.0f,  0.0f,
                                      0.0f,     -0.5f,     0.0f,  0.0f,
                                      0.0f,      0.0f,     1.0f,  0.0f,
                                      fTexOffs, fTexOffs, 0.0f, 1.0f );
            //這個(gè)矩陣是把projection space中范圍為[-11]x,y坐標(biāo)值轉(zhuǎn)換到紋理空間中
            //[0,1]的范圍中去。注意y軸的方向改變了。那個(gè)(0.5 / (float)SHADOW_MAP_SIZE)
            //的值有什么作用我還不清楚,原文也沒有說明。
             
            matTexture = matLightViewProj * matTexAdj;
             

            我們像往常那樣通過深度的比較來獲得陰影因數(shù),但隨后并不是像平常那樣輸出整個(gè)照亮了的場景,我們只輸出陰影因數(shù)。下面的頂點(diǎn)渲染和像素渲染完成這個(gè)工作。

            // Shadow mapping vertex shader
            struct VSOUTPUT_UNLIT
            {
               float4 vPosition   : POSITION;
               float4 vTexCoord   : TEXCOORD0;
               float  fDepth      : TEXCOORD1;
            };
             
            VSOUTPUT_UNLIT VS_Unlit( float4 inPosition : POSITION )
            {
               // Output struct
               VSOUTPUT_UNLIT OUT = (VSOUTPUT_UNLIT)0;
             
               // Output the transformed position
               OUT.vPosition = mul( inPosition, g_matWorldViewProj );
             
               // Output the projective texture coordinates
               OUT.vTexCoord = mul( inPosition, g_matTexture );
             
               // Output the scene depth
               OUT.fDepth = mul( inPosition, g_matLightViewProj ).z;
             
               return OUT;
            }

            我們采用percentage closer filtering (PCF)來平滑鋸齒邊緣。為了完成“PCF”,我們簡單的對(duì)周圍8個(gè)紋理點(diǎn)進(jìn)行采樣,并取得它們深度比較的平均值。

            // Shadow mapping pixel shader
            //注意這里采用的是tex2Dproj()函數(shù)以及轉(zhuǎn)換到紋理空間的向量(x,y,z,w)對(duì)紋理進(jìn)//行采樣。這與d3d9sdkshadowmap例子用tex2D()及向量(x,y)進(jìn)行采樣不同。具體//區(qū)別及原因很容易從程序中看出,我就不再啰嗦了。
            float4  PS_Unlit( VSOUTPUT_UNLIT IN ) : COLOR0
            {
               // Generate the 9 texture co-ordinates for a 3x3 PCF kernel
               float4 vTexCoords[9];
               // Texel size
               float fTexelSize = 1.0f / 1024.0f;
             
               // Generate the tecture co-ordinates for the specified depth-map size
               // 4 3 5
               // 1 0 2
               // 7 6 8
               VTexCoords[0] = IN.vTexCoord;
               vTexCoords[1] = IN.vTexCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
               vTexCoords[2] = IN.vTexCoord + float4(  fTexelSize, 0.0f, 0.0f, 0.0f );
               vTexCoords[3] = IN.vTexCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
               vTexCoords[6] = IN.vTexCoord + float4( 0.0f,  fTexelSize, 0.0f, 0.0f );
               vTexCoords[4] = IN.vTexCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
               vTexCoords[5] = IN.vTexCoord + float4(  fTexelSize, -fTexelSize, 0.0f, 0.0f );
               vTexCoords[7] = IN.vTexCoord + float4( -fTexelSize,  fTexelSize, 0.0f, 0.0f );
               vTexCoords[8] = IN.vTexCoord + float4(  fTexelSize,  fTexelSize, 0.0f, 0.0f );
               // Sample each of them checking whether the pixel under test is shadowed or not
               float fShadowTerms[9];
               float fShadowTerm = 0.0f;
               for( int i = 0; i < 9; i++ )
               {
                  float A = tex2Dproj( ShadowSampler, vTexCoords[i] ).r;
                  float B = (IN.fDepth - 0.1f);
             
                  // Texel is shadowed
                  fShadowTerms[i] = A < B ? 0.0f : 1.0f;
                  fShadowTerm     += fShadowTerms[i];
               }
               // Get the average
               fShadowTerm /= 9.0f;
               return fShadowTerm;
            }

            屏幕緩沖完成了,我們還需要進(jìn)行模糊工作。

            步驟三:對(duì)屏幕緩沖進(jìn)行模糊

            我們采用seperable gaussian filter模糊屏幕緩沖。但我們也可以用Poisson filter。這次的render targetsA8R8G8B8的紋理和相關(guān)的表面。我們需要兩個(gè)render targets,一個(gè)進(jìn)行水平階段,一個(gè)進(jìn)行垂直階段。

            // Create the blur maps
            for( int i = 0; i < 2; i++ )
            {
               if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH,
                                       SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET,
                                       D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                                       &g_pBlurMap[i], NULL ) ) )
               {
                  MessageBox( g_hWnd, "Unable to create blur map!",
                              "Error", MB_OK | MB_ICONERROR );
                  return E_FAIL;
               }
              // Grab the texture's surface
               g_pBlurMap[i]->GetSurfaceLevel( 0, & g_pBlurSurf[i] );
            }

            我們用下面的代碼生成15個(gè)高斯偏移量(Gaussian offsets)及他們的權(quán)重(corresponding weights)。

            float GetGaussianDistribution( float x, float y, float rho )
            {
               float g = 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho );
               return g * exp( -(x * x + y * y) / (2 * rho * rho) );
            }
             
            void GetGaussianOffsets( bool bHorizontal,
                                     D3DXVECTOR2 vViewportTexelSize,
                                     D3DXVECTOR2* vSampleOffsets,
                                     float* fSampleWeights )
            {
               // Get the center texel offset and weight
               fSampleWeights[0] = 1.0f * GetGaussianDistribution( 0, 0, 2.0f );
               vSampleOffsets[0] = D3DXVECTOR2( 0.0f, 0.0f );
               // Get the offsets and weights for the remaining taps
               if( bHorizontal )
               {
                  for( int i = 1; i < 15; i += 2 )
                  {
                     vSampleOffsets[i + 0] = D3DXVECTOR2( i * vViewportTexelSize.x, 0.0f );
                     vSampleOffsets[i + 1] = D3DXVECTOR2( -i * vViewportTexelSize.x, 0.0f );
                     fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( float(i + 0), 0.0f, 3.0f );
                     fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( float(i + 1), 0.0f, 3.0f );
                  }
               }
               else 
               {
                  for( int i = 1; i < 15; i += 2 )
                  {
                     vSampleOffsets[i + 0] = D3DXVECTOR2( 0.0f, i * vViewportTexelSize.y );
                     vSampleOffsets[i + 1] = D3DXVECTOR2( 0.0f, -i * vViewportTexelSize.y );
                     fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 0), 3.0f );
                     fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 1), 3.0f );
                  }
               }

            }

            為了模糊屏幕緩沖,我們將模糊映射圖(blur map)作為render target,使用下面的頂點(diǎn)渲染和像素渲染代碼渲染一個(gè)與屏幕等大的方塊。

            // 作者在程序中預(yù)先定義的屏幕大小是1024 * 768,而隨后定義的與屏幕等大的方塊為:

            // pVertices[0].p = D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f );

            //  pVertices[1].p = D3DXVECTOR4( 0.0f, 768 / 2, 0.0f, 1.0f );

            //  pVertices[2].p = D3DXVECTOR4( 1024 / 2, 0.0f, 0.0f, 1.0f );

            //  pVertices[3].p = D3DXVECTOR4( 1024 / 2, 768 / 2, 0.0f, 1.0f );

            //  這種方法與d3dsdkHDRLight中獲得render target width and height然后再構(gòu)造的//  方法不同

            // svQuad[0].p = D3DXVECTOR4(-0.5f, -0.5f, 0.5f, 1.0f);

            // svQuad[1].p = D3DXVECTOR4(Width-0.5f, -0.5f, 0.5f, 1.0f);

            // svQuad[2].p = D3DXVECTOR4(-0.5f, Height-0.5f, 0.5f, 1.0f);

            // svQuad[3].p = D3DXVECTOR4(Width-0.5f,fHeight-0.5f, 0.5f, 1.0f);

            // 而一般定義的窗口大小往往與從render target獲得的width and height不相同。

            // 而二者的fvf都是D3DFVF_XYZRHW。這兩種方法有什么區(qū)別我一直沒想通。

             

            // Gaussian filter vertex shader
            struct VSOUTPUT_BLUR
            {
               float4 vPosition    : POSITION;
               float2 vTexCoord    : TEXCOORD0;
            };
             
            VSOUTPUT_BLUR VS_Blur( float4 inPosition : POSITION, float2 inTexCoord : TEXCOORD0 )
            {
               // Output struct
               VSOUTPUT_BLUR OUT = (VSOUTPUT_BLUR)0;
               // Output the position
               OUT.vPosition = inPosition;
               // Output the texture coordinates
               OUT.vTexCoord = inTexCoord;
               return OUT;
            }
            // Horizontal blur pixel shader
            float4 PS_BlurH( VSOUTPUT_BLUR IN ): COLOR0
            {
               // Accumulated color
               float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f );
               // Sample the taps (g_vSampleOffsets holds the texel offsets
               // and g_fSampleWeights holds the texel weights)
               for(int i = 0; i < 15; i++ )
               {
                  vAccum += tex2D( ScreenSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i];
               }
               return vAccum;
            }
             
            // Vertical blur pixel shader
            float4 PS_BlurV( VSOUTPUT_BLUR IN ): COLOR0
            {
               // Accumulated color
               float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f );
               // Sample the taps (g_vSampleOffsets holds the texel offsets and
               // g_fSampleWeights holds the texel weights)
               for( int i = 0; i < 15; i++ )
               {
                  vAccum += tex2D( BlurHSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i];
               }
               return vAccum;
            }

            這里,模糊映射圖已經(jīng)完成了,為了增加陰影的模糊程度,增加了紋理上點(diǎn)的采樣距離。最后一步自然是將模糊后的紋理圖投射回屏幕空間使其顯示在屏幕上。

            After first Gaussian pass

            After second Gaussian pass

            步驟四:渲染帶陰影的場景

            為了將模糊后的紋理投射到屏幕上,我們像平常那樣渲染場景,但投影模糊后的紋理時(shí)要使用屏幕空間的坐標(biāo)。我們使用裁剪空間的坐標(biāo)和一些數(shù)學(xué)方法來產(chǎn)生屏幕空間的坐標(biāo)。下面的頂點(diǎn)渲染和像素渲染將完成這個(gè)工作:

            struct VSOUTPUT_SCENE
            {
               float4 vPosition      : POSITION;
               float2 vTexCoord      : TEXCOORD0;
               float4 vProjCoord     : TEXCOORD1;
               float4 vScreenCoord   : TEXCOORD2;
               float3 vNormal        : TEXCOORD3;
               float3 vLightVec      : TEXCOORD4;
               float3 vEyeVec        : TEXCOORD5;
            };
            // Scene vertex shader
            VSOUTPUT_SCENE VS_Scene( float4 inPosition : POSITION,
                                     float3 inNormal : NORMAL,
                                     float2 inTexCoord : TEXCOORD0 )
            {
               VSOUTPUT_SCENE OUT = (VSOUTPUT_SCENE)0;
               // Output the transformed position
               OUT.vPosition = mul( inPosition, g_matWorldViewProj );
             
               // Output the texture coordinates
               OUT.vTexCoord = inTexCoord;
             
               // Output the projective texture coordinates (we use this
               // to project the spot texture down onto the scene)
               // 這個(gè)是用來產(chǎn)生light map的紋理坐標(biāo)的。最終效果圖中地面上光照效果就是用
            //  這個(gè)坐標(biāo)配合上一幅這樣的light map實(shí)現(xiàn)的。
            
             
            
               OUT.vProjCoord = mul( inPosition, g_matTexture );
             
               // Output the screen-space texture coordinates
               // 這個(gè)就是將裁剪空間的坐標(biāo)轉(zhuǎn)換到屏幕空間的坐標(biāo),方法和裁剪空間的坐標(biāo)轉(zhuǎn)換
            //  紋理空間的坐標(biāo)的方法很相似。
               OUT.vScreenCoord.x = ( OUT.vPosition.x * 0.5 + OUT.vPosition.w * 0.5 );
               OUT.vScreenCoord.y = ( OUT.vPosition.w * 0.5 - OUT.vPosition.y * 0.5 );
               OUT.vScreenCoord.z = OUT.vPosition.w;
               OUT.vScreenCoord.w = OUT.vPosition.w;
             
               // Get the world space vertex position
               float4 vWorldPos = mul( inPosition, g_matWorld );
             
               // Output the world space normal
               OUT.vNormal = mul( inNormal, g_matWorldIT );
             
               // Move the light vector into tangent space
               OUT.vLightVec = g_vLightPos.xyz - vWorldPos.xyz;
             
               // Move the eye vector into tangent space
               OUT.vEyeVec = g_vEyePos.xyz - vWorldPos.xyz;
               return OUT;
            }
            float4 PS_Scene( VSOUTPUT_SCENE IN ) : COLOR0
            {
               // Normalize the normal, light and eye vectors
               IN.vNormal   = normalize( IN.vNormal );
               IN.vLightVec = normalize( IN.vLightVec );
               IN.vEyeVec   = normalize( IN.vEyeVec );
               // Sample the color and normal maps
               float4 vColor  = tex2D( ColorSampler, IN.vTexCoord );
               // Compute the ambient, diffuse and specular lighting terms
               float ambient  = 0.0f;
               float diffuse  = max( dot( IN.vNormal, IN.vLightVec ), 0 );
               float specular = pow(max(dot( 2 * dot( IN.vNormal, IN.vLightVec ) * IN.vNormal
                                             - IN.vLightVec, IN.vEyeVec ), 0 ), 8 );
               if( diffuse == 0 ) specular = 0;
               // Grab the shadow term
               float fShadowTerm = tex2Dproj( BlurVSampler, IN.vScreenCoord );
               // Grab the spot term
               float fSpotTerm = tex2Dproj( SpotSampler, IN.vProjCoord );
               // Compute the final color
               return (ambient * vColor) +
                      (diffuse * vColor * g_vLightColor * fShadowTerm * fSpotTerm) +
                      (specular * vColor * g_vLightColor.a * fShadowTerm * fSpotTerm);
            }

            終于完成了??瓷先ゲ诲e(cuò)。該技術(shù)的優(yōu)點(diǎn)一是解決了鋸齒問題,二是在多光源,低內(nèi)存下實(shí)現(xiàn)了軟陰影。另外該技術(shù)與陰影生成方法無關(guān),可以很容易的在shadow volumes技術(shù)中采用這項(xiàng)技術(shù)。缺點(diǎn)是由于進(jìn)行了模糊處理而需要一些填充率。

            下面是不同階段的效果比較圖:

            謝謝你閱讀這篇文章,如有疑問歡迎來信anidex@yahoo.com. (這是原作者的信箱)

             

            (汗,好多東西心里明白,但翻譯出來就是不滿意。各位要是看不明白就找出原文研究吧。)

            參考文獻(xiàn)

            • Hardware Shadow Mapping. Cass Everitt, Ashu Rege and Cem Cebenoyan.
            • Hardware-accelerated Rendering of Antialiased Shadows with Shadow Maps. Stefan Brabec and Hans-Peter Seidel.
            posted on 2005-12-23 02:19 苦行僧 閱讀(4675) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 轉(zhuǎn)載
            无码专区久久综合久中文字幕| 热久久国产欧美一区二区精品| 久久亚洲春色中文字幕久久久 | 国产成人精品久久| 久久亚洲AV无码西西人体| yy6080久久| 91精品国产91热久久久久福利 | 久久综合久久综合九色| 香蕉99久久国产综合精品宅男自| 亚洲αv久久久噜噜噜噜噜| 国产精品女同一区二区久久| 色青青草原桃花久久综合| 久久精品国产亚洲一区二区| 久久亚洲精品国产精品婷婷 | 蜜桃麻豆WWW久久囤产精品| 97久久超碰国产精品旧版| 久久久久久午夜精品| 99久久综合狠狠综合久久| 久久久久亚洲av无码专区| 性高湖久久久久久久久AAAAA| 亚洲AV无码1区2区久久| 99久久国产综合精品成人影院| 国产综合免费精品久久久| 精品综合久久久久久97超人| 欧美熟妇另类久久久久久不卡| 色婷婷狠狠久久综合五月| 精品久久人人做人人爽综合| 日本久久久久久中文字幕| 国产精品久久久久久吹潮| 久久午夜夜伦鲁鲁片免费无码影视| 一本色道久久88加勒比—综合| 久久99亚洲网美利坚合众国| 亚洲伊人久久大香线蕉综合图片| 怡红院日本一道日本久久 | 久久精品国产亚洲AV不卡| 日韩电影久久久被窝网| 久久精品综合一区二区三区| 久久国产一片免费观看| 韩国三级中文字幕hd久久精品| 国产高潮久久免费观看| 国产精品美女久久久久av爽|