目的:
在ue4中RayMarch雲層效果。
本篇只考慮雲層造型。
參考:
1.IQ在shadertoy上的Clouds和其引用
https://www.shadertoy.com/view/XslGRr
https://www.shadertoy.com/view/4sfGzS
2.shadertoy上的Simplex3d
https://www.shadertoy.com/view/XtBGDG
觀察:
目前大部分shader對於雲的形狀沒有物理層面上的推導,都使用三維噪聲然後利用fbm。
分析:
假設我們現在使用Simplex 3d噪聲,結合fbm造出雲的形狀,那麼很簡單。Simplex noise在此不說,網上都有。
float fbm(float3 p)
{
float f;
f = 0.50000*simplex3D( p ); p = p*2.01;
f += 0.25000*simplex3D( p ); p = p*2.02; //from iq
f += 0.12500*simplex3D( p ); p = p*2.03;
f += 0.06250*simplex3D( p ); p = p*2.04;
f += 0.03125*simplex3D( p );
return clamp(f,0,1);
}
float GainCloud(float3 pos,float CloudSample,float maxHeight,float a,float b)
{
if(pos.z>0&&pos.z<maxHeight)
{
return mfbm(pos/CloudSample,a,b);
}
else
{
return 0;
}
但問題很明顯:慢
在我的筆記本1070上,僅ray雲層就基本在20fps左右。更別說應用到複雜場景下。
我們在shader中實時手動計算出Simplex3d的操作,稱作Procedual的。
相反的,我們知道一個固定的3d pos對應了固定的simplex3dnoise。能否離線儲存下來,利用LUT(LookupTable)查詢之類的呢?
對於小型雲朵,顯然可以使用2d分層紋理,或使用3d紋理。
但如果我希望一個大場景,例如主角在穿梭雲層,這種就不適合了。大場景離線存儲,空間又是個問題。
然後,IQ大神的操作讓我看傻眼了:
儘管可能由於光照模型的原因,有些模糊,但穿梭在其中沒有明顯的重複感。
最重要的是,它在webGL上都能跑到40-60FPS,如果刪1,2個fbm,還能更快。
1.IQ的3d noise
從單張rand圖上生成。
float msimplex3D(float3 x,float a,float b)
{
float3 p = floor(x/a);
float3 f = frac(x/b);
//chango: cubic interpolation
f = f*f*(3.0-2.0*f);
float2 uv = (p.xy+float2(37.0,17.0)*p.z) + f.xy;
float2 rg = Texture2DSample(Material.Texture2D_0,Material.Texture2D_0Sampler,(uv+0.5)/256).yx;
return -1+2*clamp(lerp( rg.x, rg.y, f.z ),0,1);
}
其中關鍵在於:
1.
uv = (p.xy+float2(37.0,17.0)*p.z) + f.xy;
2.
(uv+0.5)/256
3.
.yx
4.
lerp( rg.x, rg.y, f.z )
而且,被採樣貼圖必須是一張256*256的rand貼圖,g通道爲r通道加上(37,17)。
原文和原文引用的shadertoy裏一大堆評論,但沒有一個真正說清楚這部分的,iq也沒有解釋。有些關於生成貼圖的算法的評論是錯誤的。網上也找不到解釋。
還希望知道原因的讀者賜教。
我將我用ue4材質系統畫的貼圖放上來,(讀者要是實現,追求精細最好還是得自己畫):
從大致的思路來講,單位立方體(縮放後)最底下和最上面是uv有偏移的noise圖,中間再通過f.z插值。最後*2-1來減少雲的密度。
無*2-1:
有*2-1:
至於爲什麼這個偏移是(37,17),因爲shadertoy系統提供噪聲圖就是這樣偏移來的。
至於爲什麼要保證r,g兩通道也保持同樣偏移。我沒思考出原因。
對於這個0.5,去掉雲會變得不連續。可能這個(0.5,0.5)+xxx跟Warp模式下紋理尋址有關。
應該是用來作邊界融合。因爲如果近從我們原來生成的rand圖生成noise,並不能保證上下左右無縫拼接。而像下圖一樣,將邊界包含在rand圖內後,由於生成noise圖時會融合,邊界連續起來。
我將0.5改爲0.1,0.3,效果也一樣,基本上證明了我的猜想。
講道理應該放在/256以外。我改成
(uv)/256+0.1
也不影響。因爲只要x,y分別都有一點偏移,橫邊界和豎邊界就能包含進來。所以IQ原來的代碼也沒錯,但是太蛋疼了。
--更新
我找到了一篇類似的,可以參考,並對比理解iq的代碼。
https://www.shadertoy.com/view/MtBGDt
對比之下,iq不需要二次tex2D,效率提升很多
對比之下,IQ的p.xy+f.xy就是上圖的p.xy,也就是未偏移UV。
而g相對r的偏移對應了b_offset的+1偏移
結語
總的來說,ray雲層造型,目前多數是fbm。從生成3d噪聲上而言,procedual式,或離線緩存式是正解,但分別有計算量太大和空間量太大的缺點。IQ的2d紋理方法是一種hack,目前還未完全理解。它在實時渲染上的表現是完美的。
下節完善雲層的光照模型。