文章参考自: https://qiita.com/uynet/items/f8b087d47f5cf316eb7e

最终效果图:

这是一位日本友人的教程,按照他的方法制作出的水面虽然有着一些缺点,但是效果还是很出色的。

场景部分:

我们首先新建两个plane,然后让它们之间呈现一定的角度,这样我们就拥有了一片沙滩和一个水面。

Shader部分:

定义结构体

struct appdata{float4 vertex : POSITION;                           };struct v2f{UNITY_FOG_COORDS(3)float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD1;float3 worldPos : TEXCOORD2;};

首先要为水体上色,这里使用了自定义的使用cos的渐变色函数,作者使用了网站:https://sp4ghet.github.io/grad/ 来生成

fixed4 cosine_gradient(float x,  fixed4 phase, fixed4 amp, fixed4 freq, fixed4 offset){const float TAU = 2. * 3.14159265;phase *= TAU;x *= TAU;return fixed4(offset.r + amp.r * 0.5 * cos(x freq.r + phase.r) + 0.5,offset.g + amp.g * 0.5 * cos(x * freq.g + phase.g) + 0.5,offset.b + amp.b * 0.5 * cos(x * freq.b + phase.b) + 0.5,offset.a + amp.a * 0.5 * cos(x * freq.a + phase.a) + 0.5);}fixed3 toRGB(fixed3 grad){return grad.rgb;}

网站页面截图(通过手动调整各通道内各个参数可以生成不同的渐变色代码,代码为GLSL,我们简单调整为CG即可使用)

接下来我们依据相机深度值为水体上色,来形成从浅水到深水的绿色到蓝色的渐变

片元深度与场景中的深度差值为d2-d1

可以看到,水深由深到浅,我们要渲染的片元深度与场景中的深度差值将越来越小,将1减去这个数值作为我们刚刚渐变色函数cosine_gradient的x变量,就可以得到一个不错的渐变效果。代码如下: 首先我们需要开启相机深度,将以下脚本挂载在场景的摄像机上

using UnityEngine;[ExecuteInEditMode]
public class DepthOn : MonoBehaviour {void Start (){GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;}
}

在顶点着色器中,我们需要在顶点着色器中计算顶点深度并使其线性变化

v2f vert (appdata v){v2f o;...o.vertex = UnityObjectToClipPos(v.vertex);              o.screenPos = ComputeScreenPos(o.vertex);       COMPUTE_EYEDEPTH(o.screenPos.z);return o;}

在shader中声明深度纹理

UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

在片元着色器中,我们来获取到片元深度与场景中的深度差值并计算渐变色

fixed4 frag (v2f i) : SV_Target{fixed4 col = (1,1,1,1);float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));float partZ = i.screenPos.z;float diffZ = saturate( (sceneZ - partZ)/5.0f);//片元深度与场景深度的差值const fixed4 phases = fixed4(0.28, 0.50, 0.07, 0);//周期const fixed4 amplitudes = fixed4(4.02, 0.34, 0.65, 0);//振幅const fixed4 frequencies = fixed4(0.00, 0.48, 0.08, 0);//频率const fixed4 offsets = fixed4(0.00, 0.16, 0.00, 0);//相位//按照距离海滩远近叠加渐变色fixed4 cos_grad = cosine_gradient(saturate(1.5-diffZ), phases, amplitudes, frequencies, offsets);cos_grad = clamp(cos_grad, 0, 1);col.rgb = toRGB(cos_grad);...}

可以注意到,这里我使用的是1.5-diffZ而不是像原作者那样使用1-diffZ,这是由于我发现原神中的水色彩更绿,而1-diffZ得到的效果相交较之略有偏蓝。

现在的效果

接下来制作水面上的波纹,也就是海浪。原作者没有使用制作好的噪声贴图,而是使用了程序化生成Perlin噪声 噪声生成部分代码

//Perlin噪声生成float2 rand(float2 st, int seed){float2 s = float2(dot(st, float2(127.1, 311.7)) + seed, dot(st, float2(269.5, 183.3)) + seed);return -1 + 2 * frac(sin(s) * 43758.5453123);}float noise(float2 st, int seed){st.y += _Time.y;//采样位置随时间变化float2 p = floor(st);float2 f = frac(st);float w00 = dot(rand(p, seed), f);float w10 = dot(rand(p + float2(1, 0), seed), f - float2(1, 0));float w01 = dot(rand(p + float2(0, 1), seed), f - float2(0, 1));float w11 = dot(rand(p + float2(1, 1), seed), f - float2(1, 1));float2 u = f * f * (3 - 2 * f);return lerp(lerp(w00, w10, u.x), lerp(w01, w11, u.x), u.y);}

在法线的处理上,原作者使用了ddx和ddy这两个函数来计算噪声干扰后的法线,以此计算出 x 方向,y 方向各自的梯度,然后取其外积,就可以得到干扰后的法线。(GPU在光栅化时,会在同一时刻并行运行很多Fragment Shader,但不会一个一个片元地去计算,而是将片元组织为2*2的组来并行进行,而ddx(x)会获取到屏幕空间下右边的片元的x值减去左边片元的x值,ddy类似)

//海浪的涌起法线计算
float3 swell( float3 pos ){float3 normal;float height = noise(pos.xz * 0.1,0);normal = normalize(cross ( float3(0,ddy(height),1),float3(1,ddx(height),0))//两片元间高度差值得到梯度,叉乘获得法向量);return normal;}

这样制作出的海面法线在地平线处的观感不是很好,这是由于屏幕部分区域内的海浪密度过高造成的

地平线处海浪的密度过高

解决方法是将地平线附近的海浪高度降低

原作者的配图,意思是要把这部分降低

可以看到,附近的波浪(红色)在屏幕上显得很大,而远处的波浪(蓝色)在屏幕上显得很小。

因此,我们可以通过获取相机中朝向水面上点的矢量 v 的水平面上的射影 Vp大小的微分,使用ddy(length ( v.xz ))来区分地平线上的部分,可以得到如下图的效果

将ddy(length ( v.xz ))输出得到的效果

我们在原函数的基础上增加上anisotropy变量来控制海浪在地平线部分的高度

float3 swell( float3 pos , float anisotropy){float3 normal;float height = noise(pos.xz * 0.1,0);height *= anisotropy ;//使距离地平线近的区域的海浪高度降低normal = normalize(cross ( float3(0,ddy(height),1),float3(1,ddx(height),0))//两片元间高度差值得到梯度);return normal;}

接着我们就可以在片元着色器中计算法线

float anisotropy = saturate(1/ddy(length(v.xz))/10);//通过临近像素点间摄像机到片元位置差值来计算哪里是接近地平线的部分float3 swelledNormal = swell( i.worldPos , anisotropy);

这些工序完成后,我们在片元着色器中添加对天空盒的反射

half3 reflDir = reflect(-worldViewDir, swelledNormal);
fixed4 reflectionColor = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflDir);

如果要加入对场景中物体的反射,可以考虑使用反射探针 接下来,我们在片元着色器中使用Fresnel-Schlick近似模型计算菲涅尔效果

// 菲涅尔反射float f0 = 0.02;float vReflect = f0 + (1-f0) * pow(1 - dot(worldViewDir,swelledNormal),5);vReflect = saturate(vReflect * 2.0);                col = lerp(col , reflectionColor , vReflect);

最后一步,我们让靠近岸边的地方更加透明,呈现出从浅水逐渐变为深水的感觉,为实现这个效果,我们只需要根据之前计算出的片元深度与场景中的深度差值来修改透明度

//接近海滩部分更透明float alpha = saturate(diffZ);          col.a = alpha;

完整shader

Shader "Unlit/sea"{SubShader{Tags { "RenderType"="Transparent" }LOD 100Blend SrcAlpha OneMinusSrcAlpha Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;                          };struct v2f{UNITY_FOG_COORDS(3)float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD1;float3 worldPos : TEXCOORD2;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);               UNITY_TRANSFER_FOG(o,o.vertex);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;    o.screenPos = ComputeScreenPos(o.vertex);      COMPUTE_EYEDEPTH(o.screenPos.z);return o;}//利用cos生成的渐变色,使用网站:https://sp4ghet.github.io/grad/fixed4 cosine_gradient(float x,  fixed4 phase, fixed4 amp, fixed4 freq, fixed4 offset){const float TAU = 2. * 3.14159265;phase *= TAU;x *= TAU;return fixed4(offset.r + amp.r * 0.5 * cos(x * freq.r + phase.r) + 0.5,offset.g + amp.g * 0.5 * cos(x * freq.g + phase.g) + 0.5,offset.b + amp.b * 0.5 * cos(x * freq.b + phase.b) + 0.5,offset.a + amp.a * 0.5 * cos(x * freq.a + phase.a) + 0.5);}fixed3 toRGB(fixed3 grad){return grad.rgb;}//噪声图生成float2 rand(float2 st, int seed){float2 s = float2(dot(st, float2(127.1, 311.7)) + seed, dot(st, float2(269.5, 183.3)) + seed);return -1 + 2 * frac(sin(s) * 43758.5453123);}float noise(float2 st, int seed){st.y += _Time.y;float2 p = floor(st);float2 f = frac(st);float w00 = dot(rand(p, seed), f);float w10 = dot(rand(p + float2(1, 0), seed), f - float2(1, 0));float w01 = dot(rand(p + float2(0, 1), seed), f - float2(0, 1));float w11 = dot(rand(p + float2(1, 1), seed), f - float2(1, 1));float2 u = f * f * (3 - 2 * f);return lerp(lerp(w00, w10, u.x), lerp(w01, w11, u.x), u.y);}//海浪的涌起法线计算float3 swell( float3 pos , float anisotropy){float3 normal;float height = noise(pos.xz * 0.1,0);height *= anisotropy ;//使距离地平线近的区域的海浪高度降低normal = normalize(cross ( float3(0,ddy(height),1),float3(1,ddx(height),0))//两片元间高度差值得到梯度);return normal;}UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);fixed4 frag (v2f i) : SV_Target{fixed4 col = (1,1,1,1);float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));float partZ = i.screenPos.z;float diffZ = saturate( (sceneZ - partZ)/5.0f);//片元深度与场景深度的差值const fixed4 phases = fixed4(0.28, 0.50, 0.07, 0);//周期const fixed4 amplitudes = fixed4(4.02, 0.34, 0.65, 0);//振幅const fixed4 frequencies = fixed4(0.00, 0.48, 0.08, 0);//频率const fixed4 offsets = fixed4(0.00, 0.16, 0.00, 0);//相位//按照距离海滩远近叠加渐变色fixed4 cos_grad = cosine_gradient(saturate(1.5-diffZ), phases, amplitudes, frequencies, offsets);cos_grad = clamp(cos_grad, 0, 1);col.rgb = toRGB(cos_grad);   //海浪波动half3 worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);float3 v = i.worldPos - _WorldSpaceCameraPos;float anisotropy = saturate(1/ddy(length(v.xz))/10);//通过临近像素点间摄像机到片元位置差值来计算哪里是接近地平线的部分float3 swelledNormal = swell( i.worldPos , anisotropy);// 只反射天空盒half3 reflDir = reflect(-worldViewDir, swelledNormal);fixed4 reflectionColor = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflDir);// 菲涅尔反射float f0 = 0.02;float vReflect = f0 + (1-f0) * pow(1 - dot(worldViewDir,swelledNormal),5);vReflect = saturate(vReflect * 2.0);               col = lerp(col , reflectionColor , vReflect);//接近海滩部分更透明float alpha = saturate(diffZ);            col.a = alpha;return col;}ENDCG}}
}

最后附上原作者GitHub项目链接:https://github.com/Uynet/Gensin-Sea

这样就完成了对原神海面的仿照制作,与实际原神的海面还具有一定差距,且具有众多不完善的地方,例如高光、反射、色散等效果都没有包含,且效果的体现很依赖天空盒的样式。但其完成的效果还差强人意且足够简单,在此做出学习分享,如有错误还望各位大佬不吝赐教 !

unity制作仿原神水面(1)——上色、造浪相关推荐

  1. 使用leaflet仿原神提瓦特大地图制作日记

    使用leaflet仿原神提瓦特大地图制作日记 项目初体验:学习并熟悉leaflet(maker和popup) leaflet的学习(L-control-zoom)和优化(marker,popup) 左 ...

  2. UE5开发仿原神风格MMOARPG大型网络demo演示

    哈喽大家好,我叫人宅,这次为大家带来一套ARPG网络战斗系统制作,我们将会用全新的UE5引擎来打造仿原神风格的游戏. 虽然是MMOARPG但是我们的核心是讲解UE5的网络战斗系统. 战斗系统包含:UE ...

  3. 原神—薄樱初绽时(html+css+js仿原神2.5首页,前端课设)

    文章目录 一.运行效果图 二.项目结构 三.html 四.css 五.JS 六.总结 链接:https://pan.baidu.com/s/1uaRCJXyIrY56NXabau4wjw?pwd=LD ...

  4. 【Unity3D小工具】Unity3D中实现仿真时钟、表盘、仿原神时钟

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦. 一.前言 ...

  5. 原神服务器服务端多人联机教程

    原神服务器服务端多人联机教程 大家好,我是艾西在上一篇文章中我们说了win系统服务器怎么搭建原神服务端,在最后结尾时有带一嘴怎么改为多人联机但不是很详细.哪么这篇文章艾西会给小伙伴们说清楚原神服务端怎 ...

  6. HTML+CSS+JavaScript实现原神登录框

    登录界面 样图 成品图 登录界面过程 去找一张登录框做样图,用PS模拟出盒模型的样子(不同的人有不同的盒模型设计方案).  1.先写一个大盒子,再把他一分为二,上半部分20%,下半部分80%.  2. ...

  7. 用CSS+JS+HTNL仿原神官网

    简介 CSS (Cascading Style Sheets,层叠样式表),是一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体.间距和颜色等)的计算机语言,CSS 文件扩展名为 ...

  8. 原神抽卡模拟器,unity制作(由于没有获得作者的视频授权,不会发布软件,只展示算法与开发等,效果图在个人主页类有资源下载,不会上传视频)

    五星效果图 以上为展示,没做优化 using System.Collections; using System.Collections.Generic; using UnityEngine; usin ...

  9. 原神-孤云秘宝(unity实训案例)(一)——模型的下载和导入

    这是我的大学实训课程项目,在此我将利用原神的开源模型制作一个小游戏,我将过程分享出来,希望可以对大家学习unity有一定的帮助 首先准备原神的建模,我这里已经准备好了原神角色模型的集合 链接:http ...

最新文章

  1. Ubuntu解压缩zip,tar,tar.gz,tar.bz2
  2. 计算机任务驱动法教学应用,任务驱动教学法在计算机教学中的应用
  3. “References to generic type List should be parameterized”
  4. 解决 Angular 官网下载的库 Schematics 执行 npm run build 时遇到的编译错误
  5. 可视化编码_编码:可视化位图
  6. python内建函数调用,Python 内建函数
  7. DMAR(DMA remapping)与 IOMMU
  8. 修改10g RAC public or private or virtual IP [Oracle]
  9. Linux中如何调整pe的大小,关于LVM PE大小影响VG容量
  10. 华为/华三IS-IS单区域配置
  11. 前端开源项目周报0109
  12. 北大中文核心期刊目录2021年 电工技术
  13. word2013、word2016、word2019标题序号变黑色竖线解决方法
  14. python爬取加密qq空间_python+selenium+requests爬取qq空间相册时遇到的问题及解决思路...
  15. 高速公路联网收费二义性路径识别系统原理及开发
  16. python的缩进规则具体是什么_python缩进规则叫什么
  17. 再见 Logstash,是时候拥抱下一代开源日志收集系统 Fluentd 了
  18. 剖析云计算技术及架构(2 云存储)
  19. 说说个人量化交易怎么办理开户和获取交易接口
  20. php通过curl上传文件

热门文章

  1. linux内存映射原理,Linux内存管理实践-使用fault()实现内存映射
  2. 洛伦兹函数(Lorentzian Function)
  3. Vue:获取当前定位城市名
  4. QCefView + QWebChannel + Vue 项目开发
  5. 二极管专题:二极管性质及伏安特性
  6. JVM老生代增长过快问题排查
  7. mxnitro浏览器 v1.0.0.500 官方版
  8. 066-PHP通过函数名调用函数
  9. Google Wave: There Will Be Backlash
  10. 替代Notepad++,可以试下notepad--,专门针对Notepad++替代而开发的简洁编辑器