標籤:z-fighting opengl es shadow mapping shadow 深度紋理
前段時間一直在弄一個室內情境,首先完成了render,效果還可以。然後給其加上shadow,使其更逼真。這裡主要記錄下在做的過程中遇到的問題。
1.是在匯入情境的時候,由於情境比較大(200M)左右,所以在ios上載入這麼大的情境會頻繁的memorywarning,然後就會被系統kill掉。這個問題的解決方案是通過改變資料類型來達到壓縮的目的。頂點的座標double是沒法改變的,如果改變會嚴重影響情境的準確度。這裡主要是改變normal和 uv的類型,其實在正常的精度範圍內,normal和 uv不需要double或者float來儲存,可以用一個short來儲存,這裡就要把normal 和 uv 加一個位移值(比如乘以10000),這個樣就可以節省大量的儲存空間,初步測試,載入完整個情境記憶體佔用大約為300M多點,現在的ipad完全可以承受。然後在shader中正真使用的時候只需要把資料還原就行,這樣幾乎沒有多少效率的損失但是卻極大的減少了記憶體的佔用。
2.情境內不同的mesh的材質均各不相同,載入的時候可以將同一材質的mesh歸在一起,這樣在繪製的時候,可以方便的賦予材質資訊。剛開始寫了載入情境和材質的類,但是最後為了可靠性以及後續支援不同格式的模型載入,選擇了使用開源庫assimp來負責模型載入這一塊。在github上找到源碼進行編譯,編譯出支援armv7,arm64的版本然後在ios中使用。使用的過程中需要匯入libz, libc++, libstdc++等等的架構。然後就可以在ios上使用該庫。
3.shadow mapping的原理:簡單的說就是先渲染情境的深度資訊,即從光源的位置放置相機進行一遍渲染,當然這裡只取得深度資訊即可,為了方便使用,將深度資訊渲染到一張深度紋理中,然後將該深度紋理傳入shader,在正常相機位置渲染,在shader中將每個點經過模型轉換後的深度值與深度紋理中的深度值作比較,若當前深度值大於深度紋理中的深度值,那麼很顯然該點處在shadow中,需要暗化處理,反之亦然。
4.產生紋理貼圖之後,將其傳入shader中,這裡遇到的問題是,開始時深度資訊一直錯誤,將深度紋理以正常紋理貼圖的方式呈現出來是完全亂掉的。後來在opengl官網找到下面的一句話:
If a texture has a depth or depth-stencilimage format and has the depth comparison activated, it cannot be used with anormal sampler. Attemptingto do so results in undefined behavior. Such textures must be used with ashadow sampler.
在windows平台上倒是沒有遇到這個問題,能正常的把包含depthimage format的texture正常的貼出來,但是在ios平台上使用OES2.0時不能正常工作,好像確實出現了未定義的現象。
5.將以上texture重新以sampler2DShadow的形式接受,並進行繪製時紋理仍然錯誤,這次錯誤的原因很好定位,主要是忘記將座標空間轉到紋理空間(0-1),通過shadowMVP矩陣轉換的座標的x和y值均在[-1, 1]區間,所以要使用一個bias矩陣進行轉換將[-1, 1]轉換到[0, 1]。
6.轉換完成之後可以使用shadow2DProj函數來完成深度值的比較,當然這裡也可以自己比較,但是自己比較的時候要注意將x和y的值除以w分量然後再取出深度值,這其實也是shadow2DProj中的實現方式,這個函數會先將x和y分量除以w分量然後再取出深度值與深度紋理中的值進行比較,若深度測試成功(即深度值小於深度紋理中的深度值)則返回1,若深度測試失敗則返回0。這樣我們就可以進行shadow的處理。
7.以上都能正確之後,shadow效果應該是可以做出來了,但是可能會出現z-fighting的問題,而且可能相當的嚴重,如果上面我們不是用shadow2DProj來進行深度值比較的話,那麼我們可以在比較深度值的時候加一個小的容錯誤差,當然這個值的大小要親自多次實驗看效果才能選定,因為對不同的情境,不同的機器這個值的差異可能很大,我這裡簡單試了一下,去0.006時的效果還是比較好的。但是如果我們使用了shadow2DProj的話,那麼比較函數不是我們自己實現的,那麼我們就沒辦法加上這個小的容錯誤差。這時候我們可以在繪製的時候使用polygonoffset來避免z-fighting的問題,試了下這樣的效果也的確是非常棒。
以上就是在shadowmapping實現過程中遇到的幾個問題。
雖然shadowmapping的原理很簡單,但是正真實現一個效果比較好的shadowmapping還是有點麻煩的。
下一步要打算實現SSAO。
shadow mapping實現動態shadow實現記錄