目的:
移植Inigo Quilez的可视化三维Julia的算法到ue4。
本篇注重完善造型后模型表面的光照,软阴影。
参考:
1.Inigo Quilez在ShaderToy上的Julia,网址后缀MsfGRr
2.之前我讨论简单的Fresnel:Chango的数学Shader世界(七)
观察:
将上节的基本造型和Inigo Quilez的Julia对比(麦芽糖好想吃哇...)
我认为最影响画面效果的基本元素有3:
1.抗锯齿
2.软阴影
3.Blin-Phong光照模型
分析:
1.SSAA抗锯齿
仿照原shader,直接上无脑SSAA。因为Julia形状的特殊性,用SSAA没太大毛病,就是太耗。
for( int j=0; j<AA; j++ )
for( int i=0; i<AA; i++ )
{
float2 pos = -1.0 + 2.0 * (uv+float2(i/Size.x,j/Size.y)/AA);
rd = 获得Ray(pos);
...
col += render( ro, rd, ...);
}
col /= AA*AA;
2.基于距离场的RayTrace软阴影
float sha = softshadow( pos, lightDir, 0.001, shadowLight, c );
///
float softshadow( float3 ro, float3 rd, float mint, float k, float4 c )
{
float res = 1.0;
float t = mint;
for( int i=0; i<64; i++ )
{
float h = map(ro + rd*t, c);
res = min( res, k*h/t );
if( res<0.001 ) break;
t += clamp( h, 0.01, 0.5 );
}
return clamp(res,0.0,1.0);
}
softshadow里面就是我们原来慢慢Ray到Julia表面的那个操作,只是起点ro是当前位置,方向沿着lightDir。
那为什么这个操作能告诉关于软阴影的信息?
显然,即使是完全光照的地方,光sha=hk/t,sha永远不能达到1,所以乘上一个大数k,使得hk大到一定距离就溢出1,之后clamp。
还有,为什么不直接k*res,还要除以t呢?考虑一下那些十分贴近光源又不被遮挡的点。
3.Blin-Phong光照模型
float3 pos = ro + t*rd;
float3 nor = calcNormal( pos, c );
float sha = softshadow( pos, lightDir, 0.001, shadowLight, c );
//shadow*diff+ambient
re = sha*clamp(dot(nor,lightDir),0,1)*baseColor+ambientColor;
float3 hal = normalize( -rd+lightDir );
float spe = pow(clamp(dot(hal,nor), 0.0, 1.0 ), specularPower );
float fre = 0.04 + 0.96*pow(1.0-clamp(dot(-rd,nor),0,1),5.0);
re += specularMulti*fre*sha*spe*specularColor;
对高光加一个fresnel,自然些?因为我们这个材质像塑料?。注意现实中大多数不透明绝缘体材质也有明显的fresnel。
无fresnel
有fresnel
步骤
1.主Custom函数修改
float3 col = float3(0,0,0);
float3 rd;
for( int j=0; j<AA; j++ )
for( int i=0; i<AA; i++ )
{
float2 pos = -1.0 + 2.0 * (uv+float2(i/Size.x,j/Size.y)/AA);
rd = normalize(float3(pos.x, -pos.y*Size.y/Size.x, 1));
rd = mul(rd, (MaterialFloat3x3)(ResolvedView.CameraViewToTranslatedWorld));
rd = normalize(rd);
col += render( ro, rd, c, lightDir, baseColor, shadowLight, specularColor, specularPower , specularMulti, ambientColor);
}
col /= AA*AA;
return col;
2. usf修改部分
float softshadow( float3 ro, float3 rd, float mint, float k, float4 c )
{
float res = 1.0;
float t = mint;
for( int i=0; i<64; i++ )
{
float h = map(ro + rd*t, c);
res = min( res, k*h/t );
if( res<0.001 ) break;
t += clamp( h, 0.01, 0.5 );
}
return clamp(res,0.0,1.0);
}
float3 render(float3 ro,float3 rd,float4 c,float3 lightDir,float3 baseColor,float shadowLight,float3 specularColor,float specularPower,float specularMulti, float3 ambientColor)
{
float3 re=float3(0,0,0);
float t= intersectJulia(ro,rd,c);
if(t<0)
{
//not julia face
}
else
{
float3 pos = ro + t*rd;
float3 nor = calcNormal( pos, c );
float sha = softshadow( pos, lightDir, 0.001, shadowLight, c );
//shadow*diff+ambient
re = sha*clamp(dot(nor,lightDir),0,1)*baseColor+ambientColor;
float3 hal = normalize( -rd+lightDir );
float spe = pow(clamp(dot(hal,nor), 0.0, 1.0 ), specularPower );
float co = clamp( dot(hal,lightDir), 0.0, 1.0 );
float fre = 0.04 + 0.96*pow(1.0-clamp(dot(-rd,nor),0,1),5.0);
re += specularMulti*fre*sha*spe*specularColor;
}
return re;
3.调参
令c=0,0,0,0。回忆上篇动图,c=0时2维是单位圆,那么咱三维就是单位球。然后像看材质球一样调参。
结语
很可惜受限于机器无法在实时渲染中无限放大分形的细节。
我感觉到分形是有限走向无限的一条路。
分形艺术连接了最感性的艺术和最理性的科学,将两个“极端”连成一个环,告诉我们二位一体,我喜欢这样的东西。
它太美了。