首先用Terrain在场景中随便做个地形,当作海底


上面加个Plane作为海面

实现海水效果要考虑海水深度对颜色的影响,法线移动形成波浪,菲涅尔,高光等效果

深度

海水深的地方颜色深,浅的地方颜色浅,所以海边和礁石附近的颜色应该比较浅。在shader中申明_CameraDepthTexture即可获得相机看到的深度图

因为海面会用Transparent来渲染,不会写入深度,所以这张深度图就是相机到海底的距离,在相机空间下,用海底的深度(红色箭头)减去海面的深度(蓝色箭头)就得到海面到海底的深度,关键代码

// 非线性深度
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, i.scrPos).r;// 转为线性深度,这个深度是相机看到的深度,也就是海底不透明物体深度,海面透明不会写入深度
depth = LinearEyeDepth(depth);// 海面的深度
float seaSurfaceDepth = LinearEyeDepth(i.pos.z);// 海面距离海底的深度
float biasDepth = depth - seaSurfaceDepth;


根据这个深度就可以在了两个颜色之间做插值

法线移动,菲涅尔

根据时间和X,Y两个方向的速度计算法线纹理的偏移量,关键代码

float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
// 对法线纹理进行两次采样(这是为了模拟两层交叉的水面波动的效果)
float3 bumpOffset1 = UnpackNormal(tex2D(_WaterNormalMap, i.uv.zw + speed)).rgb;
float3 bumpOffset2 = UnpackNormal(tex2D(_WaterNormalMap, i.uv.zw - speed)).rgb;
// 两次结果相加并归一化后得到切线空间下的法线方向
float3 tangentNormal = normalize(bumpOffset1 + bumpOffset2);

把法线从切线空间变换到世间空间,就可以计算反射和折射,然后用菲涅尔系数混合,这部分代码和玻璃效果的实现类似,参考 玻璃效果


添加法线移动和菲涅尔后的效果

高光

使用Blinn-Phong计算高光

float3 halfDir = normalize(worldLightDir + worldViewDir);
float hdotn = max(0, dot(halfDir, worldNormal));
float3 specular = pow(hdotn, _SpecularPower) * _SpecularIntensity;
specular *= _LightColor0.rgb * _SpecularColor;

此外,可以在场景中添加一个反射探针ReflectionProbe,Bake下环境信息

添加高光后的效果

完整的shader

Shader "MyCustom/Sea"
{Properties{_MainTex ("Texture", 2D) = "white" {}_ShallowColor     ("[浅水区颜色]ShallowColor",         Color)             = (1, 1, 1, 1)_DeepColor          ("[深水区颜色]DeepColor",        Color)             = (1, 1, 1, 1)_DepthRange         ("[调节深度范围]DepthRange",          Range(0,1))        = 0.5_Cubemap         ("Environment Cubemap",             Cube)              = "_Skybox" {}// 一个由噪声纹理生成的法线纹理_WaterNormalMap  ("WaterNormalMap",                  2D)                = "bump" {}_WaveXSpeed          ("Wave Horizontal Speed",       Range(-0.1, 0.1))  = 0.01    _WaveYSpeed         ("Wave Vertical Speed",             Range(-0.1, 0.1))  = 0.01_Distortion         ("[折射时图像的扭曲程度]Distortion", Range(0, 100))  = 10_NormalScale      ("NormalScale",                     Float)             = 4_SpecularColor     ("SpecularColor",               Color)             = (1, 1, 1, 1)_SpecularPower      ("SpecularPower",                   Range(0, 150))     = 100_SpecularIntensity  ("SpecularIntensity",            Range(0, 10))      = 0.6}SubShader{Tags { "RenderType"="Transparent" "Queue"="Transparent"}ZWrite OffGrabPass{"_GrabTexture"}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"struct appdata{float4 vertex  : POSITION;float3 normal  : NORMAL;float4 tangent : TANGENT; float2 uv      : TEXCOORD0;};struct v2f{float4 pos      : SV_POSITION;float4 uv       : TEXCOORD0;float4 scrPos   : TEXCOORD1;float4 TtoW0    : TEXCOORD2;  float4 TtoW1    : TEXCOORD3;  float4 TtoW2    : TEXCOORD4;};sampler2D _MainTex;float4 _MainTex_ST;sampler2D _GrabTexture;float4 _GrabTexture_TexelSize;// sampler2D_float 精度更高sampler2D_float _CameraDepthTexture;float3 _ShallowColor;float3 _DeepColor;float _DepthRange;samplerCUBE _Cubemap;sampler2D _WaterNormalMap;float4 _WaterNormalMap_ST;float _WaveXSpeed;float _WaveYSpeed;float _Distortion;float _NormalScale;float3 _SpecularColor;float _SpecularPower;float _SpecularIntensity;v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);o.uv.zw = TRANSFORM_TEX(v.uv, _WaterNormalMap);o.scrPos = ComputeScreenPos(o.pos);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  float3 worldNormal = UnityObjectToWorldNormal(v.normal);  float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;// 切线空间到世界空间的变换矩阵o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 albedo = tex2D(_MainTex, i.uv.xy);float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));// 非线性深度float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, i.scrPos).r;// 等价于下面写法// float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.scrPos.xy / i.scrPos.w).r;// 转为线性深度,这个深度是相机看到的深度,也就是海底不透明物体深度,海面透明不会写入深度depth = LinearEyeDepth(depth);// 海面的深度float seaSurfaceDepth = LinearEyeDepth(i.pos.z);// 海面距离海底的深度float biasDepth = depth - seaSurfaceDepth;// 调节深度范围,指数部分为负数,所以是一条倾斜向下的曲线,深度越大越接近0biasDepth = exp(-_DepthRange * biasDepth);fixed3 baseColor = lerp(_DeepColor, _ShallowColor, biasDepth);float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);// // 对法线纹理进行两次采样(这是为了模拟两层交叉的水面波动的效果)float3 bumpOffset1 = UnpackNormal(tex2D(_WaterNormalMap, i.uv.zw * _NormalScale + speed)).rgb;float3 bumpOffset2 = UnpackNormal(tex2D(_WaterNormalMap, i.uv.zw * _NormalScale - speed)).rgb;// 两次结果相加并归一化后得到切线空间下的法线方向float3 tangentNormal = normalize(bumpOffset1 + bumpOffset2);// 将法线转换为世界空间float3 worldNormal = normalize(half3(dot(i.TtoW0.xyz, tangentNormal), dot(i.TtoW1.xyz, tangentNormal), dot(i.TtoW2.xyz, tangentNormal)));// 反射fixed3 reflectDir = reflect(-worldViewDir, worldNormal);fixed3 reflectCol = texCUBE(_Cubemap, reflectDir).rgb * albedo.rgb;// 对切线空间下的法线进行偏移float2 offset = tangentNormal.xy * _Distortion * _GrabTexture_TexelSize.xy;// 把偏移量和屏幕坐标的z分量相乘,这是为了模拟深度越大、折射程度越大的效果i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;// 对scrPos进行了透视除法,使用该坐标对抓取的屏幕图像进行采样,得到模拟的折射颜色fixed3 refractCol = tex2D(_GrabTexture, i.scrPos.xy / i.scrPos.w).rgb;// 计算菲涅耳系数,混合折射和反射fixed fresnel = pow(1 - saturate(dot(worldViewDir, worldNormal)), 4);fixed3 fresnelColor = reflectCol * fresnel + refractCol * (1 - fresnel);// 使用Blinn-Phong计算高光float3 halfDir = normalize(worldLightDir + worldViewDir);float hdotn = max(0, dot(halfDir, worldNormal));float3 specular = pow(hdotn, _SpecularPower) * _SpecularIntensity;specular *= _LightColor0.rgb * _SpecularColor;fixed4 finalCol = 1;finalCol.rgb = baseColor + fresnelColor + specular;return finalCol;}ENDCG}}
}


调节的参数

参考

《Shader 入门精要》

Shader 海面/水面相关推荐

  1. shader 反射 水面_2D水面波光效果,以及一些2D常用shader的实现

    水面模拟是游戏行业的一个常见问题.在3D领域,水面模拟技术已经非常成熟,无论是unity也好,ue4也好,都有很多现成的代码可以直接拿来使用.不过在2D游戏中,水面模拟的开源实现还不是太多,跟3D完全 ...

  2. shader 反射 水面_【Unity Shader】模拟水面包含折射与反射与波浪动画

    最近研究了一下Unity官方的BoatAttack案例,他们模拟的海浪效果很厉害,用的是Gerstner波来模拟水面起伏和波峰的白浪还有浮力系统,还做加入了焦散效果(Caustics)和平面反射(Pl ...

  3. shader 反射 水面_【博物纳新】水面涟漪反射效果开源库测评

    [博物纳新]是UWA重磅推出的全新栏目,旨在为开发者推荐新颖.易用.有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目.前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性.很多时 ...

  4. Shader Graph 水面制作个人总结

    首先我们要打开unity urp 管线的深度设置 1.水面首先要判断深浅位置我们就需要用到Scene Depth 节点和Screen Position节点.Scene Depth调整成Eye,以及Sc ...

  5. shader 反射 水面_unity水面波浪光照反射折射物理渲染着色器Lux Water 1.01

    unity水面波浪光照反射折射物理渲染着色器Lux Water 1.01 Lux Water 是一款简单而强大的水面渲染工具,可为你呈现最佳的水面折射.反射和光照效果. 渲染功能 • 基于物理的着色, ...

  6. shader 反射 水面_shader实例(二十六)水(反射,法线,透明)

    unity自带的资源中有水的例子,导入步骤为:Assets-Import Package-Water (Pro Only),熟悉相关参数就可以调出很漂亮的水效果,但是作为程序员不能只停留在使用层,了解 ...

  7. shader 反射 水面_UnityShader-菲涅尔反射(Fresnel Reflection)

    菲涅耳公式(或菲涅耳方程),由奥古斯丁·让·菲涅耳导出.用来描述光在不同折射率的介质之间的行为.由公式推导出的光的反射称之为"菲涅尔反射".菲涅尔公式是光学中的重要公式,用它能解释 ...

  8. ShaderJoy —— “水面波纹消散” 的实现 【GLSL】

    视频效果: bilibili: Shader 特效--水面波纹消散 youku: Shader 特效--水面波纹消散 图片效果: 原理详解: 算法的核心就是模拟水波纹,对此我们使用以下数学公式

  9. unity升级版本后华为手机特效问题

    最近参与的一个旧项目由5.5升级到了2020,遇到了一些问题,主要是特效的,稍微记录一下. 一.材质球丢失问题 这个很经典,就不讲了,找美术重新拖一下就行. 二.场景变暗了 这个查了下网上说是因为un ...

最新文章

  1. QIIME 2教程. 11元数据Metadata(2020.11)
  2. CentOS7配置防火墙
  3. 豪华奢侈的中国作家代表大会
  4. IDEA连接mysql出现时区错误_idea连接数据库时区错误
  5. python 数字类型和字符串类型的相互转换_python 数字类型和字符串类型的相互转换...
  6. Tomcat配置多个Service,多个同名的应用运行在一个tomcat下
  7. Django中类视图的几实现方式
  8. 吴恩达深度学习 —— 3.2 神经网络表示
  9. (64)SPI外设驱动用户发送模块(三)(第13天)
  10. 如何从函数中获取指向并调用该函数的函数指针的地址?
  11. 朋友圈发送照片泄露位置?微信:P 完再发!
  12. 小白该如何学习Linux操作系统(1)
  13. MATLAB--数字图像处理 图像直方图规定化
  14. 基于单片机的红外检测及语音响应系统
  15. 想知道电脑上怎么压缩图片?用这3个方法实现快速压缩
  16. MySQL事务之脏读问题
  17. 测试数据科学家聚类技术的40个问题(附答案和分析)
  18. sort函数的用法(C++排序库函数的调用)对数组进行排序,在c++中有库函数帮我们实现,这们就不需要我们自己来编程进行排序了。
  19. VSCode使用04--C/C++开发环境的搭建(含googletest测试框架)
  20. 开发框架——横版格斗——动作游戏教程

热门文章

  1. 解决!适用黑苹果解决充电和使用电池中黑苹果自动睡眠
  2. 安装Ubuntu 20.04后要做的几件事(换源,精简,定制)
  3. 每日一题:299. 猜数字游戏
  4. 谷歌正式推出 “密钥登录”,逐步取代传统密码登录
  5. Option3X 5G 全网部署(基于 IUV_5G 软件)
  6. 客户关系管理(CRM)是什么?
  7. 云和恩墨数据库人才招聘
  8. python自带库zlib_python中的zlib解压
  9. tail -f与tail -F的区别
  10. TS在vue中的应用