找回密码
 立即注册
查看: 368|回复: 0

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

[复制链接]
发表于 2020-11-24 07:13 | 显示全部楼层 |阅读模式
上面是效果图,题目指的一个面片是指只有两个三角面组成的面片,只有四个顶点。
勘误:
我发现水面的高光效果在安卓手机上失去了效果,具体原因如下:
halfDirection 得用下面的Unity_SafeNormalize方法,而不是normalize,
否则在手机上高光会失去效果,修改如下:
float3 Unity_SafeNormalize(float3 inVec)
{
       float dp3 = max(0.001f, dot(inVec, inVec));
       return inVec * rsqrt(dp3);
}然后
float3 halfDirection = Unity_SafeNormalize(viewDirection+ lightDirection);概述
因为前一版卡通水体制作人觉得效果一般,所以换成了《海岛纪元》这种风格的水体。
这次我使用截帧工具,这次使用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.foamv);
//根据深度值来显示透明度  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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 06:27 , Processed in 0.201296 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表