文章目录

  • 制作步骤
    • 准备好水体网格
    • 扰动水体网格
    • 添加水体网格色调,纹理
    • 放置海上放哨点(一些随便放的立方体)
    • 添加水的深浅透视效果
    • 添加水光效
    • 重构水顶点法线
  • 正交的相机的深度需要注意
  • 改进
  • Project
  • References

简单的模拟水的效果(3A游戏效果请绕开哦)

效果:



制作步骤

  • 准备好水体网格(网格脚本生成的,参考:Unity Shader - Noise 噪点图 - 实现简单山脉)
  • 扰动水体网格顶点(参考:Unity Shader - 使用Noise噪点图生成简单山脉(使用tex2Dlod控制顶点高度))
  • 添加水体网格色调,纹理
  • 放置海上放哨点(一些随便放的立方体)
  • 添加水的深浅透视效果
  • 添加水光效
  • 重构水顶点法线(参考:Unity Shader - 简单山脉 - 顶点着色器重构法线)

准备好水体网格

网格脚本生成的,参考:Unity Shader - Noise 噪点图 - 实现简单山脉

扰动水体网格

参考:Unity Shader - 使用Noise噪点图生成简单山脉(使用tex2Dlod控制顶点高度)

我使用了:大波浪与小波浪的叠加

                float centerH = sin(_Time.y * _BigWaveLen + v.uv.x + v.uv.y) * _BigWaveAmplitude;centerH += tex2Dlod(_MainTex, float4(v.uv + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0)).r * _SmallWaveAmplitude;v.vertex.y = centerH;

添加水体网格色调,纹理

着了个色调,并对uv滚动动画

                fixed4 col = tex2D(_MainTex, i.uv + float2(_Time.y * _BigWaveLen * _UVSpeed, 0));col.rgb *= _MainColor.rgb;

放置海上放哨点(一些随便放的立方体)

添加水的深浅透视效果

这个参考了unity内置的shader中,处理软粒子的思路:。

  • 获取深度纹理的view spacebuffViewZ
  • 获取当前片段的view spacefragViewZ
  • delta = buffViewZ - fragViewZ的深度插值,来控制水体的alpha透视,或lerp的颜色过渡。
  • 还可以使用我们下面实现代码中的fade来控制浅水到深度的效果,我这里就控制海水原来的颜色(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2))到浅水着色变量的颜色来过渡(combined.rgb)。
    combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);
    如果需要你可以创建一个用个纹理:_ShallowToDeepTex的纹理然后:
var shallowToDeepCol = tex2D(_ShallowToDeepTex, i.uv);
combined.rgb = lerp(combined.rgb + shallowToDeepCol.rgb * (shallowToDeepCol.a * 2), combined.rgb, fade);

下面是参考的:unity内置的shader软粒子代码:

// Soft particles fragment function
#if defined(SOFTPARTICLES_ON) && defined(_FADING_ON)
#define fragSoftParticles(i) \if (SOFT_PARTICLE_NEAR_FADE > 0.0 || SOFT_PARTICLE_INV_FADE_DISTANCE > 0.0) \{ \float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projectedPosition))); \float fade = saturate (SOFT_PARTICLE_INV_FADE_DISTANCE * ((sceneZ - SOFT_PARTICLE_NEAR_FADE) - i.projectedPosition.z)); \ALBEDO_MUL *= fade; \}
#else
#define fragSoftParticles(i)
#endif

参考我手绘的一张图:

下面我们的实现代码

             // vert#if DEEP_EFFo.projPos = ComputeScreenPos (o.pos);COMPUTE_EYEDEPTH(o.projPos.z);#endif// frag#if DEEP_EFF    // 水深效果// 深度效果// 这里参考unity内置的shader中,实现软例子:Soft particle的写法// 与背景深度远时,alpha比较高,里的近则alha低// 先是获取深度图的view space下的z值: buffViewZfloat buffViewZ = LinearEyeD、epth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));// 再是获取片段的view space的z值:fragViewZfloat fragViewZ = i.projPos.z;// 然后去他们之间的距离值做alpha的控制,与颜色lerp的变化float fade = saturate ((buffViewZ-fragViewZ) * _DeepFactor);combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);combined.a *= fade;#endif

材质中暴露了一个DeepFactor参数,可以用于控制水深的系数。如下:

也暴露了一个浅水的颜色色调着色:

添加水光效

这个就是传统的经验光照模型:ambient + diffuse(HalfLambert) + specular(blinn phong)

             // frag#if LIGHTING_ON // 光照效果// ambientfixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;// diffusehalf3 L = normalize(_WorldSpaceLightPos0.xyz);half3 N = normalize(i.normal);half LdotN = dot(L, N) * 0.5 + 0.5;fixed3 diffuse = col.rgb * LdotN;// specularhalf3 specular = 0;half3 V = normalize(_WorldSpaceCameraPos.xyz - i.wPos);half3 H = normalize(L + V);half HdotN = max(0, dot(H, N)); // blinn-phonespecular = _LightColor0.rgb * pow(HdotN, _SpecularGlossy * 100) * _SpecularIntensity;// combined colorfixed4 combined = fixed4(ambient + diffuse + specular, col.a);#else // LIGHTING_OFFfixed4 combined = col;#endif

重构水顶点法线

参考:Unity Shader - 简单山脉 - 顶点着色器重构法线,这里不说了,参考的文章说得很清楚的。

                #if REBUILD_NORMAL // 重构法线,参考:https://blog.csdn.net/linjf520/article/details/104859710// reconstruct normals// 这个4x4数据也可以通过外部传入,可以节省顶点着色器ALU的计算量与L1 caches缓存量。const float4x4 offset_xz = {{+1,+0, /* gap **/ +1,-1},  // 右下{+0,-1, /* gap **/ -1,-1},  // 左下{-1,+0, /* gap **/ -1,+1},  // 左上{+0,+1, /* gap **/ +1,+1}   // 右上};// 默认向量也可以外部传入,因为上面的默认法线是可以调整的// 下面我讲默认法线初始化为:upfloat3 sumNormal = float3(0, 1, 0);float4 uv4 = 0;for (int i = 0; i < 4; i++) {// 获取偏移数据float4 uvs_offset = offset_xz[i];// 获取偏移数据分别的高度half h1 = sin(_Time.y * _BigWaveLen + v.uv.x + uvs_offset.x * _MainTex_TexelSize.x + v.uv.y + uvs_offset.y * _MainTex_TexelSize.y) * _BigWaveAmplitude;uv4 = float4(v.uv + uvs_offset.xy * _MainTex_TexelSize.xy + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0);h1 += tex2Dlod(_MainTex, uv4).r * _SmallWaveAmplitude;half h2 = sin(_Time.y * _BigWaveLen + v.uv.x + uvs_offset.z * _MainTex_TexelSize.x + v.uv.y + uvs_offset.w * _MainTex_TexelSize.y) * _BigWaveAmplitude;uv4 = float4(v.uv + uvs_offset.zw * _MainTex_TexelSize.xy + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0);h2 += tex2Dlod(_MainTex, uv4).r * _SmallWaveAmplitude;// 根据偏移方向,高度重构从当前顶点,指向附近偏移点的高度两个向量float3 dir1 = float3(uvs_offset.x * _GridGap, h1 - centerH, uvs_offset.y * _GridGap);float3 dir2 = float3(uvs_offset.z * _GridGap, h2 - centerH, uvs_offset.w * _GridGap);// 根据平面两个向量(两个向量可以确定一个平面,如同:TBN当中的TB两个切向量)// 叉乘得到平面的法线向量float3 newNormal = (cross(dir1, dir2));// 累加到混合向量中sumNormal += newNormal;}// 均值混合sumNormal /= 5;o.normal = UnityObjectToWorldNormal(sumNormal);#else // REBUILD_NORMAL offo.normal = UnityObjectToWorldNormal(v.normal);#endif

正交的相机的深度需要注意

unity底层在处理:相机的正交与透视投影模式下,分别对_CameraDepthTexture有不一样的编码过程(如果你使用的是_CameraDepthNormalsTexture就没这个问题),具体,可以参考我之前写过的一篇文章:Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据-只看:注意Unity正交相机中的深度纹理的编码 部分就好了。

因为我们使用的是_CameraDepthTexture,所以我们需要处理一下读取深度值的逻辑:

             // frag// jave.lin 2020.03.15#if DEEP_EFF || FOAM// unity_OrthoParams变量在:UnityShaderVariables.cginc有定义:// x = orthographic camera's width// y = orthographic camera's height// z = unused// w = 1.0 if camera is ortho, 0.0 if perspective// float4 unity_OrthoParams;float linearEyeDepth = 0;float linear01Depth = 0;// ortho - 处理正交的if (unity_OrthoParams.w == 1) {float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos));#if defined(UNITY_REVERSED_Z) // 正交需要处理这个宏定义,透视不用,估计后面unity版本升级后会处理正交的这个宏定义处理吧depth = 1 - depth;#endif/* Linear01Depth的逆运算// Z buffer to linear 0..1 depthinline float Linear01Depth( float z ){return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);}Linear01Depth = 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);(_ZBufferParams.x * z + _ZBufferParams.y) * Linear01Depth = 1(_ZBufferParams.x * z + _ZBufferParams.y) = 1/Linear01Depth(_ZBufferParams.x * z) = 1/Linear01Depth - _ZBufferParams.yz = (1/Linear01Depth - _ZBufferParams.y) / _ZBufferParams.x Linear01Depth == ndcZz = (1/ndcZ - _ZBufferParams.y) / _ZBufferParams.x */linearEyeDepth = LinearEyeDepth (/*to ndcZ**/(1.0/depth - _ZBufferParams.y) /_ZBufferParams.x);linear01Depth = (depth);} else { // perspective - 处理透视的float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos));linearEyeDepth = LinearEyeDepth (depth);linear01Depth = Linear01Depth(depth);}#endif#if DEEP_EFF    // 水深效果// 深度效果// 这里参考unity内置的shader中,实现软例子:Soft particle的写法// 与背景深度远时,alpha比较高,里的近则alha低// 先是获取深度图的view space下的z值: buffViewZ// float buffViewZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));float buffViewZ = linearEyeDepth;// 再是获取片段的view space的z值:fragViewZfloat fragViewZ = i.projPos.z;// 然后去他们之间的距离值做alpha的控制,与颜色lerp的变化float fade = saturate ((buffViewZ-fragViewZ) * _DeepFactor);combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);combined.a *= fade;#endif

注意上面代码中,i.projPos.z,可以改写为使用i.vertex.w即可,因为在project matrix中,m[4,3] == -1,就将model view space下的pos.w=-pos.z了。

正交下图效果:

改进

  • 添加水边Fresnel 反射
  • 添加透视
  • 添加折射
  • 添加水体背光透视
  • 水体网格面细分(Tessellation不用CPU端生成的方式)
  • 水体顶点分形、DFT快速傅里叶波动

后面有空再处理

Project

backup : UnityShader_SimpleWater_ShallowToDeepEffect_2018.3.0f2

References

  • Simple Water Shader in Unity
  • 网格脚本生成的,参考:Unity Shader - Noise 噪点图 - 实现简单山脉。
  • 扰动水体网格顶点,参考:Unity Shader - 使用Noise噪点图生成简单山脉(使用tex2Dlod控制顶点高度)。
  • 重构水顶点法线,参考:Unity Shader - 简单山脉 - 顶点着色器重构法线。
  • 【Unity Shader学习笔记】实现反射与折射模拟水面、使用grabPass与环境贴图 - 写完文章后,法线有个博主写得挺好的,推荐阅读。
  • Unity法线水,顺便利用CommandBuffer实现廉价的深度和截屏 - 这个后面可以尝试学习。
  • Unity海洋shader笔记①

Unity Shader - 实现简单水体 - 浅水到深水颜色控制相关推荐

  1. 【Unity Shader实例】 水体WaterEffect(二) 用贴图和uv动画模拟水效

    Unity Shader实现简单水体效果 效果展示 原理 用贴图和uv动画模拟水效实现"假"水. 设计 找一张水波的贴图,处理它的uv值,让贴图流动起来.这样就用静态纹理和uv动画 ...

  2. Unity Shader 之 简单 护盾Shield 效果的实现

    Unity Shader 之 简单 护盾Shield 效果的实现 目录 Unity Shader 之 简单 护盾Shield 效果的实现 一.简单介绍 二.实现原理

  3. Unity Shader 之 简单实现物体被压扁(top顶点的逐渐与bottom顶点重合)的效果

    Unity Shader 之 简单实现物体被压扁(top顶点的逐渐与bottom顶点重合)的效果 目录 Unity

  4. Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果

    Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果 目录

  5. Unity Shader 之 简单实现折叠平面(翻书)的效果

    Unity Shader 之 简单实现折叠平面(翻书)的效果 目录 Unity Shader 之 简单实现折叠平面(翻书)的效果 一.简单介绍 二.实现原理 三.注意事项 四.效果预览 五.实现步骤 ...

  6. 【Unity Shader实例】 水体WaterEffect(一) 设计

    Unity Shader 水体效果实现的设计 在设计水体效果的实现方案之前,我们先参考一下大神们写好的精彩的例子,比如DCG Water Shader的效果,这也是我们努力的目标. 好!~ 现在开始实 ...

  7. Unity Shader 实现简单的压扁效果

    有点累啊,一个CoverMap搞了一周多,还是太嫩了,还有好多东西等着我去学呢,今天就写个简单的东西吧--一个把模型压扁的效果,参考博客Unity Shader - 一些玩具Shader.话不多说,先 ...

  8. 【Unity 工具,简单学习】DoTween,脚本控制动画工具

    DOTween:脚本控制动画工具 简单介绍 安装 简单使用 eg.1 简单移动,跳动 eg2. 缓动 Easing(动画插值) eg3. 循环 Looping eg4. 变换颜色,变换大小 eg5. ...

  9. Unity Shader 实现简单的宝石渲染

    入职实习的第一周过去了,感觉还是相当不错的,找到工作了也就不用再顶着高压去学习了,快乐. 闲话少说,先上效果图啦. 效果还是很棒的,但这不是我写的,哈哈,这个代码可以完全说就是抄Unity的案例.As ...

最新文章

  1. 专访云知声黄伟:场景定义芯片,未来所有场景都需要AI | AI名人堂
  2. mysql数据导库常用操作
  3. 我的2008年(上)-《走出软件作坊》是怎样炼成的
  4. 让数据库变快的10个建议
  5. Java 技术篇-IntelliJ IDEA修改java、jdk版本实例演示
  6. Fliptile(状压+思维)
  7. 精通 RPM 之查询篇
  8. 加拿大计算机专业学什么,加拿大哥伦比亚大学计算机专业课程
  9. 文件I/O实践(2) --文件stat
  10. HTML5 Canvas 绘图
  11. 20145326蔡馨熤《计算机病毒》——代码的动静态分析结合
  12. 【亲测有效】Ubuntu18.04 sudo apt update无法解析域名的解决方案
  13. CephOpenstack结合说明操作
  14. 项目如何开始:怎样和客户谈需求(转)
  15. python读取文件夹下所有图像_Python 读取指定文件夹下的所有图像方法
  16. 微型计算机工作适宜的温度,应用电脑(1)第一章 第一节 计算机的基本组成...
  17. 服装计算机辅助设计论文,服装设计计算机辅助设计应用探索-服装设计论文-设计论文.docx...
  18. python右对齐输出乘法表_python输出九九乘法表
  19. 中国大陆手机号码如何注册谷歌账号?完美解决收不到验证码的问题
  20. 知识图谱与KBQA——槽填充

热门文章

  1. 处女座像处女座学习人生经验
  2. C++【认知系列】鼠标连点
  3. Tektronix泰克示波器MSO46规格一览
  4. 中专是计算机专业毕业论文,中专计算机专业毕业论文怎么写
  5. 强制卸载docker脚本
  6. Java新生管理系统的设计与实现
  7. AC-DC开关电源种类
  8. 使用VS2012 的15个技巧
  9. NVIDIA Mellanox 网卡驱动下载地址
  10. 三种定时器Timer的使用