陰影貼圖

陰影貼圖Shadow mapping)是在三維計算機圖形中加入陰影的過程。陰影貼圖的概念最初是由 Lance Williams 於 1978年在「在曲面上投射陰影」這篇論文中提出的[來源請求]。從那時開始,這種方法就已經用於場景預渲染、實時甚至是許多遊戲設備以及高端電腦遊戲中[來源請求]。在PixarRenderMan 中就使用了陰影貼圖技術,同樣,在 玩具總動員 這樣的遊戲中也使用了這項技術。

有陰影貼圖的場景
沒有陰影的場景

像素與以紋理形式保存的光照深度緩衝區或者深度圖像比較,通過這種方式計算像素是否處於光源照射範圍之內,從而生成陰影。

陰影及陰影圖的原理

從光源位置看出去,所有能夠看到的物體都處在光照之中,但是這些物體後面的東西將處於陰影之中。這就是生成陰影圖的最基本的原理。光照場景進行渲染,保存能夠看到的物體表面深度,即為陰影圖。然後,正常的場景中的每個點都與這個深度圖進行比較,就好像判斷場景中的每個點能否被光線看到,從而進行正常場景的渲染。

對於實時陰影來說,這項技術要比陰影體的精度差一些,但是根據在特定應用中每種技術所需填充時間的不同,陰影圖有些情況下是一種速度比較快的選擇。並且,陰影圖不需要額外的模板緩存,並且可以經過修改生成柔和邊界的陰影。但是,與陰影體不同,陰影圖的精度受到解析度的限制[來源請求]

算法綜述

對於陰影場景的渲染需要兩個步驟來完成。第一步是產生陰影圖本身,第二步是將陰影圖應用到場景中。根據實現方式以及光源數目的不同,陰影場景渲染過程可能需要兩個或者更多的繪製過程。

生成陰影圖

 
從光源視角渲染的場景
 
光源視角的場景深度圖

第一步是從光源的視角渲染場景。對於點光源來說,視場是一個視角足夠寬的透視投影。對於象太陽光這樣的定向光來說,需要使用正投影

根據這個渲染結果提取、保存深度緩衝。因為只與深度信息相關,因此通常避免更改顏色緩衝區,並且不對光線及紋理進行計算以節省時間。這個深度圖經常在圖形卡的內存中保存為紋理。

一旦場景中的光源或者物體發生變動,就必須重新更新深度圖,但是在其它場合下也可以重複使用深度圖,例如只有照相機運動的場合。如果場景中有多個光源,必須針對每個光源分別生成深度圖。

在許多實現方法中為了節省重繪深度圖所花費的時間,可以只對場景中的一部分物體進行渲染來生成陰影圖。另外,為了解決當前深度值與下一步繪製的表面深度太接近產生的深度衝突問題(Z-fighting),也可能在陰影圖渲染過程中對物體的照明深度添加一個偏移。實現這種效果的另外一種可選方法是裁剪掉前面的物體,僅對後面的物體進行渲染生成陰影圖。

場景的著色

第二步就是使用陰影圖按照普通的透視投影繪製場景。這個過程分為三個主要的部分,第一是找出物體在光照視景中的坐標,第二是將這個坐標與深度圖中的對應值進行比較,最後就是根據處於陰影或者光照下的位置將物體繪製出來。

光照空間坐標

 
投影到場景的深度圖的可見性

為了將物體點與深度圖進行比較,物體在場景坐標系中的位置必須要轉換到光照空間中的對應位置,這個變換可以用矩陣乘法實現。物體在屏幕上的位置可以通過普通的坐標變換確定,但是必須要生成第二套坐標來定位物體在光照空間中的位置。

將世界坐標系變換到光照空間坐標系的變換矩陣與第一步中用於渲染陰影圖的變換矩陣相同,在OpenGL中它是模型視圖與投影矩陣之積。這樣就生成一組齊次坐標,如果可見的話經過透視除法(perspective division)轉換成歸一化設備坐標,每個分量(xyz)都落在 -1 到 1 之間。有許多的實現方法要進行另外的縮放及偏移矩陣乘法將 -1 到 1 之間的數值轉換到深度圖或紋理圖中更常用的 0 到 1 之間的數值。這個縮放過程也可以在透視除法之前完成,並且可以通過那個矩陣與下面的矩陣相乘很容易地將前面的變換計算融合到一起:  

如果使用shader或者其它的圖形硬體擴展完成這項工作的話,那麼通常在頂點級進行這樣的變換,生成的數值在其它定點之間進行插值,然後傳遞到片斷處理的部分。

深度圖檢驗

 
深度圖檢驗失敗

找到光照空間的坐標之後,xy 通常對應於深度圖紋理的位置,z 對應於相應的深度,這樣就可以與深度圖進行比較。

如果 z 大於深度圖中相應位置保存的數值,那麼就認為物體被其它物體所遮擋,這個點就標記為失敗,在繪製過程中將它描繪成陰影狀態;否則的話,就將它繪製成照亮狀態。

如果位置落在深度圖之外,那麼程序就需要確定表面要用預設的照亮或者陰影方式繪製,通常是繪製成照亮的狀態。

shader 的實現中,這個過程是在片斷層次完成的。另外,當選擇硬體所用的紋理圖存儲類型的時候需要注意:如果無法進行插值,那麼陰影將會出現明顯的鋸齒邊界,可以通過使用較高的陰影圖解析度來減少這種現象。

根據與陰影邊界的接近程度使用一個取值範圍而不是簡單的通過或者失敗,通過這樣的修改就可以實現更加平滑的陰影邊界。

陰影貼圖技術也可以經過修改用於在照亮區域生成紋理,這樣就可以模擬投影機的效果。上面標題為「投影到場景中的深度圖的可見性」的圖片就是這樣的一個例子。

繪製場景

 
帶有環境光的最終渲染場景

帶有陰影的場景的可以通過幾種不同的方法進行繪製。如果有可用的可程式 shader,深度圖檢驗可以用碎片 shader(fragment/pixel program) 完成。在最初的生成陰影圖之後,它一次就可以根據結果完成陰影或者被照亮物體的繪製。

如果沒有可以使用的陰影工具,通常必須使用一些硬體擴展如 GL_ARB_shadow頁面存檔備份,存於網際網路檔案館) 等進行陰影圖檢驗,它們通常不允許在兩個光照模型之間進行選擇,並且需要更多的渲染過程:

  1. 將整個場景渲染到陰影。對於大多數的光照模型(參見Phong反射模型)來說,在技術上僅僅使用光照中的環境光成分就可以完成,但是通常加入一個模糊的漫射光使得陰影中平坦的表面看起來有一定的弧度。
  2. 使用深度圖檢驗,然後渲染照亮的場景。深度圖檢驗失敗的區域不要覆蓋,仍然保持在陰影狀態。
  3. 對於每一個額外的光照都可能需要額外的處理過程,用加性混合方法它們的效果與已經繪製的效果合成在一起。每個這樣的過程都需要前面的生成相應陰影圖的過程。

這篇文章中的例圖是使用 OpenGL 的擴展 GL_ARB_shadow_ambient頁面存檔備份,存於網際網路檔案館) 完成陰影貼圖過程的。

參見

外部連結