16 Babylonjs基础入门 阴影
在本節中,我們將學習如何在Babylon.js中創建陰影。陰影現在實現的是動態陰影,它們會根據光源動態生成。你可能需要首先查看一個案例:點擊這里。
實現陰影
使用Babylon.js的ShadowGenerator對象很容易實現陰影。此功能使用到了陰影貼圖:從光源的角度生成場景的貼圖。
創建陰影生成器需要兩個參數:陰影貼圖的大小,以及用于陰影貼圖計算的光。
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
接下來,你必須要定義渲染的陰影。這里,我們需要渲染圓環的陰影,只需要將模型對象添加到渲染陰影的數組內即可:
shadowGenerator.getShadowMap().renderList.push(torus);
在Babylon.js v3.1版本中,又新增了兩個函數來添加刪除:
- addShadowCaster(mesh, includeDescendants):輔助函數,用于將模型及其后代添加到陰影渲染列表中。
- removeShadowCaster(mesh, includeDescendants):輔助函數,從陰影渲染列表中將模型刪除。
最后,你必須將模型的接收陰影參數設置為真來定義陰影的顯示位置:
ground.receiveShadows = true;
柔化陰影
如果你想更進一步,可以激活陰影過濾,柔化陰影邊緣創建更好看的陰影。
過濾的方式有三種:
泊松采樣 Poisson sampling
shadowGenerator.usePoissonSampling = true;
如果將此值設置為true,則將禁用方差陰影貼圖。此濾鏡使用泊松采樣來柔化陰影。結果更好,但速度更慢。
指數陰影貼圖 Exponential shadow map
shadowGenerator.useExponentialShadowMap = true;
默認的過濾方式,因為這種方式對混淆的陰影方式很管用。如果你想減少計算時間,可以隨意關閉它。你可以通過設置shadowGenerator.depthScale來更新陰影貼圖的深度的縮放值。默認值為50.0,但如果你的場景的深度比例(MinZ和MaxZ之間的距離)很小,你可能需要修改它。
模糊指數陰影貼圖 Blur exponential shadow map
shadowGenerator.useBlurExponentialShadowMap = true;
這是更好的柔化陰影濾鏡,但速度也較慢。它使用模糊的指數陰影貼圖。
模糊的質量由以下屬性定義:
- shadowGenerator.blurScale:在應用模糊后處理之前,定義用于縮小陰影貼圖的比例。默認情況下,該值為2
- shadowGenerator.blurBoxOffset:定義用于應用模糊陰影邊緣的偏移量。默認情況下,該值為1(這意味著該框將在兩個方向上從-1到1,從而導致模糊后處理讀取9個值。)
- shadowGenerator.useKernelBlur:設置使用內核模糊還是邊緣模糊。雖然使用內核模糊運算量更大,但陰影質量會好很多。你可以使用
shadowGenerator.blurKernel,默認值為1,來控制內核大小。
以下是模糊陰影的示例:點擊這里
近似指數陰影貼圖 Close exponential shadow map
從Babylon.js v3.0版本開始,我們引入了一種新的方法來解決指數陰影自身陰影問題:Close Exponential Shadow Map (CESM)。使用CESM,你可以準確的自陰影,但你需要定義它的參數:
- 你必須通過光源的
light.shadowMinZ屬性和light.shadowMaxZ設置光源的深度值范圍。范圍越小,陰影效果就越好。 - 你必須確保光源盡可能的靠近陰影。
你可以使用以下命令啟用CESM:
shadowGenerator.useCloseExponentialShadowMap = true;
或者你想要模糊的陰影:
shadowGenerator.useBlurCloseExponentialShadowMap = true;
以下是CESM的官方示例
更接近的百分比過濾 Percentage Closer Filtering(僅限WebGL2)
從Babylon.js v3.2版本開始,添加了一種處理陰影貼圖的新方法。這極大改善了陰影的性能和設置。
PCF(Percentage Closer Filtering)陰影受益于WebGL2中可用過濾功能的新特性,并產生更平滑的泊松采樣版本。當WebGL2不受支持時,它將回退到標準泊松采樣。
你可以啟用PCF:
shadowGenerator.usePercentageCloserFiltering = true;
以下是PCF的官方示例
由于PCF會在小型設備上占用過多資源,因此你需要通過設置filteringQuality屬性來在質量和性能之間進行權衡(質量越低,性能越好)。
shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_LOW;
PCF目前僅支持點光源和平行光。
聯系硬化陰影 Contact hardening shadow(僅限WebGL2)
從Babylon.js v3.2版本開始,引入了基于PCSS陰影的聯系硬化陰影。
PCSS被視為PCF的改進版,盡管看起來更好,但它們的代價也更昂貴,應在桌面端應用內使用。與PCF一樣,如果當前環境不支持WebGL2,它們將自動回退到泊松采樣(Poisson Sampling)。
在PCSS中,當陰影遠離物體投射時,陰影會變得更柔和,模擬現實生活中發生的情況。
為了獲得更準確的結果,你需要定義其它參數:
- 你必須通過光源的
light.shadowMinZ屬性和light.shadowMaxZ設置光源的深度值范圍。范圍越小,陰影效果就越好。 - 你還可以通過修改
contactHardeningLightSizeUVRatio來更改陰影的軟化速度(在0到1之間)。
通過以下方式啟用PCSS:
shadowGenerator.useContactHardeningShadow = true;
- 以下是官方的PCSS的示例
由于PCSS會在小型設備上占用過多資源,因此你需要通過設置filteringQuality屬性來在質量和性能之間進行權衡(質量越低,性能越好)。
shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_LOW;
- 以下案例能更清楚的了解陰影隨著距離變化的效果:點擊這里
PCSS目前僅支持點光源和平行光。
示例
你可以在這里找到一個示例:點擊這里
這里顯示了一些陰影與聚光燈的效果
沒有過濾
泊松采樣 Poisson sampling
指數陰影貼圖 Exponential shadow map
模糊指數陰影貼圖 Blur exponential shadow map
更接近的百分比過濾 Percentage Closer Filtering(僅限WebGL2)
聯系硬化陰影 Contact hardening shadow(僅限WebGL2)
光源
注意,一個陰影生成器只能和一個光源配合使用。如果需要另一個光源產生陰影效果,則需要另外創建一個陰影生成器。
只有點光源,聚光燈和方向光才能投射陰影。
點光源
點光源使用的是立方體貼圖渲染,因此啟用陰影時請務必小心,因為可能會導致一些性能問題。你可以點擊此處查看案例
此外,BlurExponentialShadowMap并CloseBlurExponentialShadowMap沒有被點光源支持(主要是因為模糊的六個面立方貼圖的過于昂貴)。
為了優化渲染,如果你確定所有的陰影都位于光源的一側,你還可以使用點光源就想無限的聚光燈一樣。要實現這一點,只需要指定光源的一個方向,Babylon.js就會自動為陰影貼圖使用一個簡單的紋理,而不是立方體紋理。
聚光燈
聚光燈光源使用透視投影來計算陰影貼圖。
平行光
平行光使用正交投影。燈光的位置會自動評估,以便您獲得盡可能最好的陰影貼圖。你可以通過設置light.autoUpdateExtends來關閉自動此行為。你還可以通過修改其中一個屬性來控制投影窗口的大小:
- light.shadowOrthoScale:默認值為0.1,表示投影窗口從最佳大小增加10%。
- light.shadowFrustumSize:默認情況下為關閉狀態,值為0 。你可以指定一個值,該值將用于定義使用的平截頭體的平方大小。
燈光的位置以及你添加到陰影渲染列表的模型決定了陰影的顯示位置。請注意,從該位置的光照點必須查看渲染器列表中的所有網格。否則不能渲染陰影。
自定義投影矩陣
所有的光源都需要給陰影生成器提供投影矩陣才能構建陰影貼圖。你可以通過設置light.customProjectionMatrixBuilder值來定義自己的需要:
light.customProjectionMatrixBuilder = function(viewMatrix: Matrix, renderList: Array<AbstractMesh>) {return BABYLON.Matrix.PerspectiveFovLH(angle, 1.0, activeCamera.minZ, this.shadowMaxZ);
}
故障排除
陰影貼圖是一項很棒的技術,但并不完美。可以調整幾個參數以幫助改進最終渲染。
偏差
你可能希望減少由于不精確的陰影貼圖導致的陰影痤瘡。為此,你可以定義偏差(默認為0.00005):
shadowGenerator.bias = 0.01;
陰影生成器將每個像素的深度與從光的角度看到的遮擋物(陰影輪廓)的深度進行比較。當我們處理低精度紋理時(當Babylon.js使用浮動紋理但底端設備僅支持整型紋理時),你可能希望增加遮擋物的深度,以方便生成自陰影。
背面渲染
你可以通過設置shadowGenerator.forceBackFacesOnly為true改善自陰影問題。這將強制陰影生成器將網格的背面渲染到陰影貼圖。這可以明顯提高整體精度并減少對偏差的需求。
提高投影矩陣精度
默認情況下,燈光的投影矩陣使用主攝像機的minZ和maxZ。但是你可能想控制它,以便通過減少minZ和maxZ之間的距離來獲得更精確的陰影貼圖。為此,您可以設置light.shadowMinZ和light.shadowMaxZ。
使用最優設置進行自陰影
如前面所說,如果你想要在自陰影上模糊陰影,最好的選擇可能是使用近似指數陰影貼圖( close exponential shadow map)。
截錐體邊緣衰減
根據你設置的陰影生成器,當對象靠近陰影貼圖的邊緣時,可能會生成一些奇怪的衰減。要優雅的解決此問題,你可以設置frustumEdgeFalloff的值
shadowGenerator.frustumEdgeFalloff = 1.0;
你可以在這里找到一個例子:點擊這里
此屬性控制陰影在截錐體邊緣淡出的程度。它僅用于平行光和聚光燈。默認情況下,該值設置為0(沒有衰減)和1.0(完全衰減)。
在靜態場景中凍結陰影
如果你有一個靜態游戲世界(帶有投射陰影)- 沒有必要每秒進行60次相同的陰影計算。僅創建一次陰影就足夠了。這大大提高了性能,并且可以設置更高的陰影分辨率。
陰影生成器可以改為靜態:
shadowGenerator.getShadowMap().refreshRate = BABYLON.RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
要求光源不再從新計算陰影位置
light.autoUpdateExtends = false;
清除骨骼矩陣權重
模型動畫的骨骼的權重錯誤或不精確可能會導致陰影不正確。在這種情況下,你可以使用以下代碼加載自動清理權重:
BABYLON.SceneLoader.CleanBoneMatrixWeights = true;
你應該在加載場景或模型之前設置它。
自陰影
在設置陰影中,我們需要著重注意自陰影的問題。讓我們嘗試在以下場景設置自陰影:https://playground.babylonjs.com/#FH3FM2#1
第一步是在陰影生成器中添加生成陰影的模型和設置它們都接收陰影(我們還將陰影偏差強制設置為0以突出效果)https://playground.babylonjs.com/#FH3FM2#4
你可以注意到,在自陰影物體的表面到處都是奇怪的圖案。這被稱為陰影痤瘡(更多相關信息)
好處在于,在Babylon.js中,我們確實有辦法解決這個問題。
偏差
如前面的OpenGL教程中所描述的那樣,你可以通過設置偏差值來避免產生偏差。https://playground.babylonjs.com/#FH3FM2#5
無法避免的是,這樣會產生一種叫peter panning(彼得平移)的副作用,陰影將不會附著在它們的物體上。
你可以從babylon.js v3.2版本中獲取正常偏差。
正常偏差(從v3.2版本開始)
首先我們先查看一下彼得平移效果的極限:https://playground.babylonjs.com/#FH3FM2#6
正如你所見,現在陰影生成的過渡處會出現一些斑點,表面與光線方向平行:
這是添加一些正常的偏差。基本上,在陰影圖的生成過程中,如果表面與光線平行將沿法向方向插入幾何體:https://playground.babylonjs.com/#FH3FM2#7
幾何體上面的痤瘡都不見了,接下來,讓我們的陰影變得好看一些。
軟化陰影
首先將陰影生成器改為聯系硬化陰影 Contact hardening shadow(僅限WebGL2):https://playground.babylonjs.com/#FH3FM2#8
首先,你無法看到聯系硬化效果,不僅如此,你還可以再次看到陰影痤瘡。注意到關于PCSS的部分,你會發現需要將陰影的生成最小值和最大值調的盡可能近:https://playground.babylonjs.com/#FH3FM2#10
現在的陰影有聯系硬化效果,但是幾何體上的痤瘡甚至更厲害了。不幸的是,偏差應用于標準化的坐標深度(0-1),因此更改燈光的近距和遠距值會影響偏差的大小。
所以,回到前面在看到彼得平移之前將陰影偏差值設置最大,然后應用一些正常的偏差來移除其余的痤瘡,產生了以下效果:https://playground.babylonjs.com/#FH3FM2#11
現在你的陰影很正常,并且沒有痤瘡和彼得平移效果。
自定義陰影貼圖著色器
從Babylon.js v4.0版本開始,你可以指定自己的著色器來渲染陰影貼圖。要定義該著色器,你可以通過shaddowGenerator.customShaderOption屬性來設置
shadowGenerator.customShaderOptions = { shaderName: "customShadowMap",uniforms: ["customWorld"]
}
除了shaderName是必須設置的意外。你可以設置的其它屬性為:
- attributes: 用于指定你在著色器里面用到的attributes
- uniforms: 用于指定你在著色器里面用到的uniforms
- samplers: 用于指定你在著色器里面用到的samplers
- defines: 用于指定你在著色器里面用到的defines
陰影貼圖生成是一項復雜的任務,你還需要考慮幾個定義(如陰影貼圖的類型是int還是float,或者是否需要alpha test)。建議在此處檢測默認著色器:
- 頂點著色器:https://github.com/BabylonJS/Babylon.js/blob/master/src/Shaders/shadowMap.vertex.fx
- 片元著色器: https://github.com/BabylonJS/Babylon.js/blob/master/src/Shaders/shadowMap.fragment.fx
為了更好的檢查你自己的著色器,你可以依靠shadowGenerator.onBeforeShadowMapRenderObservable觀察。每次渲染陰影貼圖時都會調用它,它將為您提供當前編譯的效果。
你可以在這里找到一個完整的例子:https://www.babylonjs-playground.com/#IJH4VG#0
基礎入門結語
到這里,Babylon.js的基礎入門也算翻譯完成了。由于自己能力有限,相關內容有很多問題,以及自己都沒有理解的內容。如果你有更好的解釋方式,請聯系我,希望我們共同進步。
總結
以上是生活随笔為你收集整理的16 Babylonjs基础入门 阴影的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多队列 部分队列没有包_记一次TCP全队
- 下一篇: 大数据分析如何使用pandas进行时间序