目的:
在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,目前还未完全理解。它在实时渲染上的表现是完美的。
下节完善云层的光照模型。