当前位置:   article > 正文

[Unity URP]水体渲染_untiy3d urp 水系统吗

untiy3d urp 水系统吗

unity版本:2019.4.12f
urp版本:7.5.1

前言

  Unity URP有一个很好的官方示例项目Boat Attack,我最近参考该项目实现了下水体渲染,所以水篇文章记录下。

波形

  我是直接用了标准3d模型Plane作为水面,不过有用上曲面细分,关于曲面细分我有另写过文章(链接)。
Sine vs. Gerstner wave
  波形的实现则使用了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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

  获取到散射和吸收的颜色后,就可以渲染出有不错的水体着色效果了。

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);
  • 1
  • 2
  • 3

在这里插入图片描述

浮沫

  水在靠岸边的地方一般会有浮沫(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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

焦散

  焦散是布于水底的,所以需要根据深度图获取水底坐标,然后用水底的坐标作为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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  因为焦散布于水底,所以应该调整下diffuse的获取。

float3 diffuse = (opaueTex + caustics) * Absorption((depthDistance) * depthMulti) + Scattering(depthDistance * depthMulti);
  • 1

在这里插入图片描述

高光

  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);
  • 1
  • 2
  • 3
  • 4

  我们还可以用额外的噪声图来丰富法线的细节,使得更有波光粼粼的感觉。

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;
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

Reference

  1. Boat Attack 项目海水技术解析
  2. 真实感水体渲染技术总结
  3. https://github.com/Verasl/BoatAttack
  4. https://catlikecoding.com/unity/tutorials/flow/waves/
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号