赞
踩
unity版本:2019.4.12f
urp版本:7.5.1
Unity URP有一个很好的官方示例项目Boat Attack,我最近参考该项目实现了下水体渲染,所以水篇文章记录下。
我是直接用了标准3d模型Plane作为水面,不过有用上曲面细分,关于曲面细分我有另写过文章(链接)。
波形的实现则使用了Gerstner波来进行叠加。如上图所示,Gerstner相较于正弦波波峰更尖锐而波谷更宽阔。关于Gerstner的相关文章很多,比如这一篇,这里就不详细展开了。
对于水体的着色,则是生成两张Ramp图,表示不同深度下的散射(Scattering)和吸收(Absorption)的颜色。这里的深度指的是深度图在水面下的部分。
float UnderwaterDepthDistance(float2 screenPos, float3 positionVS) { float depthTex = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_ScreenTextures_linear_clamp, screenPos); float depthCamera2End = LinearEyeDepth(depthTex, _ZBufferParams); return depthCamera2End + positionVS.z; } half3 Scattering(half depth) { return SAMPLE_TEXTURE2D(_WaterRampMap, sampler_WaterRampMap, half2(depth, 0.375h)).rgb; } half3 Absorption(half depth) { return SAMPLE_TEXTURE2D(_WaterRampMap, sampler_WaterRampMap, half2(depth, 0.0h)).rgb; }
获取到散射和吸收的颜色后,就可以渲染出有不错的水体着色效果了。
float depthDistance = UnderwaterDepthDistance(screenPos, positionVS);
half3 opaueTex = SAMPLE_TEXTURE2D_LOD(_CameraOpaqueTexture, sampler_CameraOpaqueTexture_linear_clamp, distortion, depthDistance * 0.25).rgb;
float3 diffuse = opaueTex * Absorption((depthDistance) * depthMulti) + Scattering(depthDistance * depthMulti);
水在靠岸边的地方一般会有浮沫(Foam)。Boat Attack里,是专门用了一个摄像机自上而下的来拍摄水底的深度。不过因为浮沫本身也要随波浪不停变化,所以我仍采用的是前面得到的depthDistance来进行深度判断,也暂未发现这样得到的浮沫有什么问题。
float2 uv2 = positionWS.xz * 0.1 + _Time.y * 0.05;
half3 foamMap = SAMPLE_TEXTURE2D(_FoamMap, sampler_FoamMap, uv2).rgb;
half edgeFoam = saturate(1 - depthDistance * 0.5 - 0.25);
half foamBlendMask = max(0, edgeFoam);
half3 foamBlend = SAMPLE_TEXTURE2D(_FoamRamp, sampler_FoamRamp, half2(foamBlendMask, 0.66)).rgb;
half foamMask = saturate(length(foamMap * foamBlend) * 1.5 - 0.1 + saturate(1 - depthDistance * 4) * 0.5);
half3 foam = foamMask.xxx * (mainLight.shadowAttenuation * mainLight.color);
diffuse = lerp(diffuse, foam, foamMask);
焦散是布于水底的,所以需要根据深度图获取水底坐标,然后用水底的坐标作为uv去采样焦散图。
float3 ReconstructWorldPos(half2 screenPos)
{
// World Pos reconstriction
float depthTex = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_ScreenTextures_linear_clamp, screenPos);
float4 raw = mul(UNITY_MATRIX_I_VP, float4(screenPos * 2 - 1, depthTex, 1));
float3 worldPos = raw.rgb / raw.a;
return worldPos;
}
float3 Caustics(half3 depthPositionWS)
{
float2 uv = depthPositionWS.xz * 0.1 + float2(_Time.y, _Time.x) * 0.1;
return SAMPLE_TEXTURE2D(_CausticsMap, sampler_CausticsMap, uv);
}
因为焦散布于水底,所以应该调整下diffuse的获取。
float3 diffuse = (opaueTex + caustics) * Absorption((depthDistance) * depthMulti) + Scattering(depthDistance * depthMulti);
Urp本身就有自带的高光实现,就在ShaderLibrary/Lighting.hlsl内,采用的是简版CookTorrance模型的BRDF。
BRDFData brdfData;
InitializeBRDFData(half3(0, 0, 0), 0, half3(1, 1, 1), 0.9, 1, brdfData);
half3 spec = DirectBDRF(brdfData, normalWS, mainLight.direction, viewDirWS) * mainLight.shadowAttenuation * mainLight.color;
return float4(diffuse + spec, 1);
我们还可以用额外的噪声图来丰富法线的细节,使得更有波光粼粼的感觉。
half2 detailBump1 = SAMPLE_TEXTURE2D(_SurfaceMap, sampler_SurfaceMap, uv2).xy * 2 - 1;
half2 detailBump2 = SAMPLE_TEXTURE2D(_SurfaceMap, sampler_SurfaceMap, uv1).xy * 2 - 1;
half2 detailBump = (detailBump1 + detailBump2 * 0.5) * saturate(depthDistance * 0.25 + 0.25);
normalWS += half3(detailBump.x, 0, detailBump.y) * _BumpScale;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。