【Unity Shader】卡通海洋

2023-10-26 17:37:42 浏览数 (3)

Shader刚入门没多久,尝试做了一个卡通的海洋效果。做的时候参考了很多大佬的文章,现在写个笔记记录一下。

先看几张图找找感觉:

最能体现二次元海洋的要素就是波形、白沫、折射与次表面散射。

先从波形开始,这里参照了这位大佬的文章:

https://zhuanlan.zhihu.com/p/95482541

下面开始填色,这里先叠了个双层菲涅尔:

代码语言:javascript复制
//双层菲涅尔
fixed fresnel = saturate(_FresnelScale   (1 - _FresnelScale) * pow(1 - dot(normal, viewDir), 4));
fresnel = fresnel < 0.5 ? 0.05 : 0.4;

half facing = saturate(dot(viewDir, normal));
facing = facing < 0.5 ? 0 : 1;
fixed3 oceanColor = lerp(_OceanColorShallow, _OceanColorDeep, facing);
fixed3 oceanDiffuse = oceanColor * _LightColor0.rgb;
fixed diffuseControl = saturate(dot(lightDir, normal));
oceanDiffuse *= diffuseControl < 0.7 ? 0.7 : 0.8;

根据FFT里传来的白沫RT画上白沫:

不过这样岸边会显得非常唐突,就像是模型莫名其妙就从中间穿过,过于生硬。所以岸边也不要忘记加上白沫。这里需要进行深度采样,并进行分阶。

代码语言:javascript复制
//屏幕深度
float sceneZ = max(0,
                   LinearEyeDepth(
                       UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))))
                   - _ProjectionParams.g);
float partZ = max(0, i.projPos.z - _ProjectionParams.g);
//深度差
float depthGap = sceneZ - partZ;
float shore = smoothstep(0.1, 0.5, saturate(1 - depthGap * _ShoreRange)) > 0.25
                  ? 1
                  : 0 * _ShoreTransparency;

再涂上颜色:

代码语言:javascript复制
//近岸泡沫
fixed foam = shore;
fixed4 foamCol = foam * _FoamColor;
foamCol.a *= _ShoreTransparency;

看起来好了一些。

下面加入反射,这里做的比较简单。

代码语言:javascript复制
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectDir, 0);
half3 sky = DecodeHDR(rgbm, unity_SpecCube0_HDR);
fixed3 halfDir = normalize(lightDir   viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb;
fixed specularControl = pow(max(0, dot(normal, halfDir)), _Gloss) < 0.2 ? 0.1 : 0.5;
specular *= specularControl;
specular *= sky * .6;
fixed3 diffuse = lerp(oceanDiffuse, bubblesDiffuse, bubbles);

感觉平静一点的水面才看得比较明显

下面水浅处加一些带有渐变的折射,这里用到了上面算过的深度差

代码语言:javascript复制
float linearShore = smoothstep(
    0.1, 0.65, saturate((1 - depthGap   _LinearShoreRange) / _LinearShoreGradient));

折射也是从GrabPass拿的:

代码语言:javascript复制
//折射
fixed4 fra = tex2D(
    _ScreenTex,
    (i.screenUV.xy   _FracIntensity * (saturate(1 - depthGap)   0.5) * sin(2 * _Time.y)) / (i.screenUV.w
    ));
fra.a = linearShore * _FracTransparency;

现在折射的通透有是有了,但是反而显得岸边特别空。于是我在这里加了个焦散效果:

代码语言:javascript复制
//焦散
float3 GetCaustics(float2 uv,fixed factor,fixed offset)
{
   
    fixed2 causticsUv = uv * _Caustics_ST.xy   _Caustics_ST.zw;
    causticsUv  = _CausticsSpeed * _Time.y * factor;
    // 分离RGB
    fixed s = 1.5 * offset;
    fixed r = tex2D(_Caustics, causticsUv   fixed2( s,  s)).r * _CausticsIntensity;
    fixed g = tex2D(_Caustics, causticsUv   fixed2( s, -s)).g * _CausticsIntensity;
    fixed b = tex2D(_Caustics, causticsUv   fixed2(-s, -s)).b * _CausticsIntensity;
    return fixed3(r, g, b);
}
/*......*/
fixed3 caustics = min(GetCaustics(i.uv, 1, 1), GetCaustics(i.uv, _CausticsGrow, _CausticsOffset));
caustics *= linearShore;

感觉好多了

最后再加上一点次表面散射,这里参照了这篇文章:

Unity海洋shader笔记①

这下海浪的通透感就出来了:

Shader刚入门没多久,恳请各位批评指正!

0 人点赞