【UnityShader】利用平面反射技术制作水面效果

前言

本文章仅记录自己学习当中遇到的问题,以及自己在制作效果时实现的思路,如有不正确欢迎大佬们指正。

以下为实现的最终效果图:

目录

一、平面反射Planar Reflection

二、噪声图扰动制作水波纹效果

三、BlinnPhong高光模拟阳光打在水面上的效果

四、菲涅尔效应制作水底效果

五、提取水面扰动的高亮部分制作水面反光


一、平面反射Planar Reflection

要实现水面上的倒影的效果可以用平面反射原理实现,相关的文章多到数不胜数,此处推荐两个比较好的文章,如果对其背后的数学原理感兴趣,可以移步以下文章:

[Unity/URP学习]平面反射(PlanarReflection)_unity 平面反射-优快云博客

 不得不看的Unity小魔术——平面反射【有点难,但很有用!】_哔哩哔哩_bilibili

 简单简述一下平面反射的实现思路,我们现实里要看到一个物体,就如上图一样,需要通过反射等一系列方法,使得光线传播到眼睛(摄像机)位置。

但如此实现会比较困难,此时我们可以以平面为对称线,在平面对面实例化一个完全对称的相机,用于观看平面反射之后的结果,并将看到的画面打印成一张RenderTexture,就可以实现类似的效果了。

此处需要注意一下的是,得到的RT图需要使用屏幕空间坐标对其进行采样才可以得到正确的反色和结果。

实现效果如下,我们还可以给反射相机打印的RenderTexure做一个高斯模糊的效果,模拟水面折射产生的模糊:

图1(不加高斯模糊) 图2(加高斯模糊)

二、噪声图扰动制作水波纹效果

我们可以通过噪声图扰动现在的反射结果,来模拟水中的波纹效果(如上图)。这里讲几个扰动效果的小TIPS,可以使得扰动得到的波纹效果更加真实:

1、可以通过采样两张噪声图或者采样一张噪声图给不同属性叠加的方式,使得其随机性更高,不会让波纹看起来呈现出一定规律

2、仔细观察地平线处,可以发现我们的水波纹会有一个比较不自然的接线;并且所有波纹都是同一频率进行扰动,这样会导致结果看上去非常不自然。

我们可以通过给噪声偏移值,除以 当前点到屏幕距离 的值,这样就可以让波纹从近到远呈现一种较为平滑的过渡,并且地平线的接线问题也得以解决,得到的效果如下图:

三、BlinnPhong高光模拟阳光打在水面上的效果

为水面添加一个BlinnPhong高光效果,可以模拟出阳光打在水面上使得水面产生一定颜色变化的效果,但仍然有一些需要注意到的地方,如果我们直接为水面添加BlinnPhong高光,为高光乘以一个颜色值,就会得到以下效果:

咋一看好像效果挺好,但与噪声扰动一样,在地平线的接线处产生了非常生硬的过渡,为了消除这些过渡,我们采用与解决噪声图扰动瑕疵类似的方法,使用Shader入门精要当中计算线性雾的公式来消除最边缘的高光(最后还要把得到的值remap到(0,1)的范围)。

f = \frac{Hmax - Distance}{Hmax-Hmin}

 公式得到的效果大概如下,在距离屏幕较近的地方的值接近1,较边缘的地方则为0。

两者相乘即可得到较为平滑的高光过渡效果:

四、菲涅尔效应制作水底效果

制作水底效果前我们需要先得到水下的画面,此处我们可以通过两种方式得到水下的画面:

1、GrabPass去捕捉屏幕纹理

2、手动添加一个Texture来模拟水底的效果

此处由于我们水底下啥也没有,我们就用一张贴图来当作我们的水底,我们可以在法线空间下给视线空间一些偏移量,来制作地面与水面的偏移效果。

在现实生活当中,我们直线观察水下的场景,可以比较清楚的观察到水下的画面,而在视线与水面角度相差较小的时候,看着就没那么清晰了,这就是菲涅尔效应。

菲涅尔效应的实现网上也有较全的资料,简述一下实际上就是1-nDotv的结果。 

在得到水下画面之后,我们直接将得到的Color与菲涅尔的结果相乘,就可以得到一个受菲涅尔效应影响的水底效果了。

五、提取水面扰动的高亮部分制作水面反光

太阳打在水面上会产生一些反光的效果,虽然反射天空盒加上bloom效果已经达成了类似的效果,不过我们可以通过手动实现并操纵一些属性使得它产生更好的效果。

我们将噪声图采样结果提取出,并通过power或者减去一个阈值的方式,去提取出高亮的部分,再乘以一个强度就可以得到水面反光的效果。

最后将得到的结果叠加即可,高光部分单独提取的效果如下:

  

<think>好的,用户想了解如何在Unity中实现水面效果。首先,我需要回忆一下相关知识点,并参考提供的引用内容。根据引用[1],水面效果主要包括动态波浪、水深颜色、浪涌和折射效果。接下来,我需要将这些步骤结构化,确保每个部分都解释清楚。 第一步是动态波浪。使用正弦波和法线贴图是比较基础的方法,可能用户需要具体的代码示例。需要提到如何调整参数,比如波速和振幅,以及如何结合法线贴图来增强效果。同时,Gerstner波作为更高级的选项也应该提及,但可能不需要深入代码。 第二步是水深颜色。这里需要解释如何根据顶点深度差值来计算颜色渐变,可能用深度图或者顶点着色器中的计算。要说明如何将深度值映射到颜色,可能使用Lerp函数插值两种颜色。 第三步是浪涌效果,类似水深颜色,但需要叠加一层流动的纹理来模拟浪花。可能需要UV动画,即随时间偏移纹理坐标。 第四步是折射效果,使用GrabPass获取屏幕内容,然后根据法线扰动进行偏移。这里要提到GrabPass的用法和如何在片段着色器中处理偏移后的颜色。 另外,引用[3]提到ShaderGraph需要图形学知识,所以可能需要分传统Shader和ShaderGraph两种方法。但用户请求的是教程,可能更倾向于传统Shader的代码实现,因为引用[1]提供了代码示例的结构。 需要确保代码部分正确,比如顶点和片段着色器的结构,法线贴图的采样,以及GrabPass的应用。同时,要提醒用户调整参数以获得最佳效果,并可能推荐使用更复杂的波函数或后期处理优化。 最后,生成相关问题,帮助用户进一步学习,比如优化方法、性能分析或ShaderGraph的使用。这些问题需要与水面效果相关,并覆盖用户可能感兴趣的扩展话题。 检查是否有遗漏的要点,比如水面反射或菲涅尔效应,但根据引用内容,可能不在当前范围内。保持回答基于提供的引用,避免添加未提及的内容。确保使用正确的中文术语,比如“着色器”、“法线贴图”等,并保持语言流畅自然。</think>在Unity中实现水面效果主要涉及着色器编程和材质参数调整,以下分步骤说明实现方法: ### 一、动态波浪效果 1. 使用正弦波模拟基础波动 在顶点着色器中添加垂直方向偏移: ```glsl float wave = _Amplitude * sin(_Time.y * _WaveSpeed + v.vertex.x * _Frequency); v.vertex.y += wave; ``` 其中`_Amplitude`控制波高,`_Frequency`决定波长,`_WaveSpeed`调节波动速度[^1] 2. 增强法线细节 叠加两层法线贴图实现动态波纹: ```glsl float2 uv1 = TRANSFORM_TEX(i.uv, _NormalMap) + _Time.x * _FlowSpeed; float2 uv2 = TRANSFORM_TEX(i.uv, _NormalMap) * 0.8 - _Time.x * _FlowSpeed; half3 normal = UnpackNormal(tex2D(_NormalMap, uv1)) + UnpackNormal(tex2D(_NormalMap, uv2)); ``` ### 二、水深颜色渐变 1. 计算顶点深度差 ```glsl float depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))); float waterDepth = saturate((depth - i.projPos.w) / _DepthMaxDistance); ``` 2. 颜色插值 ```glsl fixed4 waterColor = lerp(_ShallowColor, _DeepColor, waterDepth); ``` ### 三、浪涌效果实现 叠加流动噪声贴图: ```glsl float2 foamUV = i.uv * _FoamTiling + _Time.x * _FoamSpeed; fixed4 foam = tex2D(_FoamTex, foamUV) * _FoamIntensity; ``` ### 四、折射效果 使用GrabPass获取屏幕内容: ```glsl GrabPass { "_RefractionTex" } // 片段着色器中 float2 offset = normal.xy * _Distortion; fixed4 refrCol = tex2Dproj(_RefractionTex, UNITY_PROJ_COORD(i.scrPos + float4(offset,0,0))); ``` ### 完整Shader结构示例: ```glsl Shader "Custom/Water" { Properties { _NormalMap ("Normal Map", 2D) = "bump" {} _WaveSpeed ("Wave Speed", Float) = 1.0 //...其他属性 } SubShader { Tags { "Queue"="Transparent" } GrabPass {} Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag //...着色器核心代码 ENDCG } } } ``` 建议通过以下参数调整效果: - 波峰尖锐度:调整Gerstner波的陡度参数 - 折射强度:控制`_Distortion`值 - 深度过渡:修改`_DepthMaxDistance`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值