Unity只在一个面片上实现真实水体渲染

上面是效果图

概述

因为前一版卡通水体制作人觉得效果一般,所以换成了《海岛纪元》这种风格的水体。

这次我使用截帧工具,这次使用RenderDoc来截帧,效果还不错。

本来打算复刻一版的,后来发现他们貌似不是用unity做的。代码风格有点不一样。

其实是因为我看不懂他们的代码。。下面我会分析实现的理论和讲述具体实现。最后给出demo下载。

具体实现

通过截帧工具可以看到:

生成浪花的贴图是由lightmap和一个Rays贴图来渲染的。

而且浪花是单独一个mesh,这波操作其实我是看不懂的。原本以为通过shader在一个面片上就可以实现类似效果。

他们浪花的渲染效果大概如下:

再看一下他们水的效果:

1、使用了深度值来区分水深颜色

2、使用了法线贴图数据扰动来做水波纹效果

3、冲向岸边的浪花基本是实心的白色线条

4、反射和折射,反射用了环境贴图(并非是cubemap)

5、有高光,为什么他们的高光这么柔和。

它们所使用的贴图如下:

大概知道原理,就可以做了。

首先采样法线贴图扰动来做波纹效果:


//采样法线贴图的uv坐标,和时间相加
o.bumpUv1.xy = v.uv + float2(_SinTime.x * _WaveSpeed.x, _SinTime.x * _WaveSpeed.y);
o.bumpUv1.zw = v.uv + float2(_CosTime.y * 1.2 * _WaveSpeed.z, _SinTime.y*0.5* _WaveSpeed.w);
//法线贴图
 half4 bump10 = (tex2D(_DumpTex, v.bumpUv1.xy / _NormalsScale) * 2) + (tex2D(_DumpTex, v.bumpUv1.zw / _NormalsScale) * 2) - 2;
 half3 oriOffset = UnpackNormal(bump10);
 oriOffset.xy = oriOffset.xy * _NormalsStrength;
 bump = normalize(oriOffset);
 half2 offset = oriOffset.xy;
//采样折射贴图(地面)
float4 refraCol = tex2D(_MainTexture, v.uv.xy + offset );

注意上面使用了不同的uv坐标采样两次法线贴图的数据,并各自乘以2后相加的结果减去2。

这样的目的是为了使得波纹有交叉扰动的感觉。

接着采样深度贴图,做出水浅,水深过渡的效果。

//深度贴图
float zdepth = tex2D(_DepthTexture, v.oriuv).r;
//把深度强的地方增强,弱的地方变化不大,这步可做可不做,具体看效果
 zdepth = pow(zdepth, 1.3);
float depthDifference = zdepth * _FoamDeP;
float waterDepthDifference01 = saturate(depthDifference);

waterDepthDifference01 = pow(waterDepthDifference01, 0.8);
 //根据深度值插值颜色
float4 lightColor = lerp(float4(1, 1, 1, 1),_DepthGradientShallow , smoothstep(0,0.5, waterDepthDifference01));
float4 waterColor = lerp(lightColor, _DepthGradientDeep, waterDepthDifference01);
float alpha = smoothstep(0, 0.1, zdepth);

再接着做实线的浪花

//浪花贴图
 float4 FoamCol = tex2D(_FoamTexture, v.foamuv);
//根据深度值来显示透明度
 float FoamAlpha = (FoamCol.r + (FoamCol.g * cSecondWeight));
FoamCol.a = FoamAlpha;
 half waveFaInner = _waveFaMax - _Time.y * _WaveSpeed.x * _waveFaSpeed % (_waveFaMax);
 waveFaInner = clamp(waveFaInner, _waveFaMin, _waveFaMax);
//是否显示浪花
 half borderFlag = step(zdepth.r, waveFaInner) * step(waveFaInner - _line, zdepth.r);
  half4 border1 = FoamCol.a * borderFlag * _specularColor * (alpha);

然后就可以得出水体的颜色了:

 float4 finalCol = float4(refraCol.rgb * waterColor.rgb, alpha);
half4 cc =  finalCol+ border1;

最后加上高光,

刚开始高光是想用blin-phong做的,效果不太好,感觉是一片片的生硬的色块,不仅有锯齿,还没过渡的感觉。

而海岛纪元的高光真的过渡很柔和,很真实。难道是PBR的高光渲染?

试了一下,果然是真的。

所以这里我用到了PBR的高光,也就是BRDF高光公式

 //高光,PBR的高光
 float roughness = 1.0 - _Smoothness;
roughness = max(roughness, 0.002);
float roughness2 = roughness * roughness;
//d项
 half dCol = BRDF_DTerm(NdotH, roughness2);
 ////G项
half gTerm = BRDF_GTerm(NdotL, NdotV, roughness2);
 ////F项 菲涅尔
 half3 frenCol = BRDF_FresnelTerm(_specularColor.rgb, LdotH);
float specularPBL = dCol * gTerm * UNITY_PI * frenCol;
 //不会为负数
specularPBL = max(0, specularPBL * NdotL);
 //any 参数里的任意一个元素不为零
  specularPBL *= any(_specularColor.rgb) ? 1.0 : 0.0;
//加上高光,得到最终的颜色
 float4 outCol = cc + float4(specularPBL * mainLight.color, 1) * alpha;
 outCol.r = clamp(outCol.r, 0, 1);
 outCol.g = clamp(outCol.g, 0, 1);
 outCol.b = clamp(outCol.b, 0, 1);
 return outCol;

高光的感觉自然多了!

demo下载,记得在URP环境下使用:

链接:https://pan.baidu.com/s/11D5koAJSypTpaw_7G-F7Fg 
提取码:tuf1 

猜你喜欢

转载自blog.csdn.net/zakerhero/article/details/106769979