【Unity Shader】unity海边波浪效果的实现

2023-11-05

效果图如下(GIF因为为了把图压小所以删掉了一些帧导致后面速度突然很快,实际效果并不是这样~_~)

PS.对于移动端,参考该文章:http://www.lsngo.net/2018/03/22/unity_seawave_vertexcolor/

之前在玩很多游戏的时候,注意到里面的海水和陆地相交接的地方会产生海浪,比如《海岛奇兵》,以及水面会出现一个透明渐隐的过度,而不会在水面和陆地的交界处产生硬切边。其中海浪的效果考虑到可以使用单独的面片来制作,不过最近在试着通过深度比较的方式直接计算出水面和陆地相交接的位置来制作海边的浪花,这种方式很多效果都会用到,其中比如unity粒子shader的软粒子计算部分(当使用软粒子时粒子的面片和其它物体交叉时不会出现明显的切边,而是有一个过度),还有之前在网上看到的别人的热扭曲效果。

以下例子只讲如何实现浪花,水的反射和折射有时间另外写。另外我的水shader中从潜水处到深水处的颜色渐变也是通过深度比较来完成的。

首先来讲解一下深度比较的原理,其实很简单,就是比较水面的z深度和已写入缓存的陆地的深度(准确的说是渲染到深度图的深度值,也就是_CameraDepthTexture,若要在shader中能读到有效的深度图,摄像机的depthTextureMode要设置为DepthTextureMode.Depth),计算一个深度差,很明显,水面和陆地部分交接的深度差是0,(因为都已经交叉在一起了),而水越深的地方深度差显然越大,如下图


unity内置粒子shader中使用深度差实现软粒子的代码如下:

#ifdef SOFTPARTICLES_ON
float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float partZ = i.projPos.z;
float fade = saturate (_InvFade * (sceneZ-partZ));
i.color.a *= fade;
#endif

其中LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)))表示对深度图_CameraDepthTexture进行纹理投射,SAMPLE_DEPTH_TEXTURE_PROJ是定义在UnityCG.cginc文件中的,相当于tex2Dproj,事实上只是多了对当前的图形api做了判断而已。

i.projPos.z的值来自顶点函数,代码如下:

v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
#ifdef SOFTPARTICLES_ON
o.projPos = ComputeScreenPos (o.vertex);
COMPUTE_EYEDEPTH(o.projPos.z);
#endif
而这一段就是软粒子中计算深度差的部分:
float fade = saturate (_InvFade * (sceneZ-partZ));

我想读到这里你一定会产生一个疑问,既然深度差是计算水面深度和深度图也就是_CameraDepthTexture中的深度的差,那么为什么水的平面不会写入_CameraDepthTexture中呢,因为如果水的深度写入了_CameraDepthTexture,必然会出现水的深度和_CameraDepthTexture中自己的深度比较,此时任何一个像素位置的深度差都是0。这里我要讲一下Unity中深度图的渲染方式,如果有接触过unity中的材质替代渲染,应该知道材质替代渲染是替换掉场景中某个标签的shader,而unity中深度图的渲染方式实际上就是这样,而它替带渲染的标签就是"RenderType",其中"RenderType"="Opaque"的物体会被渲染到深度图中,而"RenderType"="Transparent"的shader则不会被渲染,而我这边的水就使用了这个标签,因此它是不会被渲染到深度图的。


接下来由于在顶点函数中我们使用了

COMPUTE_EYEDEPTH
来计算深度,因此深度图的深度也需要转换到视空间,即使用LinearEyeDepth。

此时如果直接输出深度差应该会得到这样的效果:


注意水面和陆地交叉的地方因为深度差最小所以最暗,而水较深的地方则表现出白色。

此时我们实际上只需要在shader中输入两个值,对应潜水处的深度差和较深处的深度差,并使用插值,就能产生水边海浪泡沫的效果了:


如上。

当然这一部分还只是产生还边海浪泡沫的部分,接下来需要讲的是能像真正的海浪一样不断冲刷海滩的浪花。

一般想到浪花,其实应该很容易想到使用类似正余弦函数来模拟的方式,(当然顶点水波只靠普通的正余弦还是达不到很好的效果的,不过这里不讨论)这是一种很简单的方式,而我的浪花就使用了正弦函数。我使用了这样一张贴图:


用这张贴图来模拟反复冲刷的海浪,并通过sin函数和_Time来偏移uv来实现,那么现在的问题是如何知道海浪的方向。

其实可以回顾上面讲的,我们知道海水和陆地的交叉处深度差是0,而深水处的深度差则比较大,不妨把深度差绘制成一条轴:


那么以深度差和时间作为sin函数的参数既可以计算出海浪纹理的uv偏移量了

大致为

deltaDepth+sin(_Time.x*_WaveSpeed)

当然直接这样产生的效果,各部分海浪的相位都一致,会导致海浪效果像平滑的光圈一样放大缩小,所以我用了一张躁波贴图来扰乱海浪的相位

最后效果如下:


大致就是这样,最后附上完整代码和资源


Shader "Water/SeaWave" {
	Properties {
		_WaterTex ("WaterTex", 2D) = "black" {} 
		_WaveTex ("WaveTex", 2D) = "black" {} //海浪
		_BumpTex ("BumpTex", 2D) = "bump" {} 
		_GTex ("Gradient", 2D) = "white" {} //海水渐变
		_NoiseTex ("Noise", 2D) = "white" {} //海浪躁波
		_WaterSpeed ("WaterSpeed", float) = 0.74  //海水速度
		_WaveSpeed ("WaveSpeed", float) = -12.64 //海浪速度
		_WaveRange ("WaveRange", float) = 0.3 
		_NoiseRange ("NoiseRange", float) = 6.43
		_WaveDelta ("WaveDelta", float) = 2.43
		_Refract ("Refract", float) = 0.07
		_Specular ("Specular", float) = 1.86
		_Gloss ("Gloss", float) = 0.71
		_SpecColor ("SpecColor", color) = (1, 1, 1, 1)
		_Range ("Range", vector) = (0.13, 1.53, 0.37, 0.78)
	}
	CGINCLUDE 
	fixed4 LightingWaterLight(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) {
		half3 halfVector = normalize(lightDir + viewDir);
		float diffFactor = max(0, dot(lightDir, s.Normal)) * 0.8 + 0.2;
		float nh = max(0, dot(halfVector, s.Normal));
		float spec = pow(nh, s.Specular * 128.0) * s.Gloss;
		fixed4 c;
		c.rgb = (s.Albedo * _LightColor0.rgb * diffFactor + _SpecColor.rgb * spec * _LightColor0.rgb) * (atten * 2);
		c.a = s.Alpha + spec * _SpecColor.a;
		return c;
	}
	ENDCG
	SubShader {
		Tags { "RenderType"="Transparent" "Queue"="Transparent"}
		LOD 200

		GrabPass{}
		zwrite off
		
		CGPROGRAM
		#pragma surface surf WaterLight vertex:vert alpha
		#pragma target 3.0

		sampler2D _GTex;

		sampler2D _WaterTex;
		sampler2D _BumpTex;
		sampler2D _CameraDepthTexture;
		sampler2D _GrabTexture;
		half4 _GrabTexture_TexelSize;
		
		sampler2D _NoiseTex;
		sampler2D _WaveTex;

		float4 _Range;

		half _WaterSpeed;
		
		half _WaveSpeed;
		fixed _WaveDelta;
		half _WaveRange;
		fixed _Refract;
		half _Specular;
		fixed _Gloss;

		half _NoiseRange;

		struct Input {
			float2 uv_WaterTex;
			float2 uv_NoiseTex;
			float4 proj;
			float3 viewDir;
		};

		void vert (inout appdata_full v, out Input i) {
			UNITY_INITIALIZE_OUTPUT(Input, i);

			i.proj = ComputeScreenPos(mul(UNITY_MATRIX_MVP, v.vertex));
			COMPUTE_EYEDEPTH(i.proj.z);
		}

		void surf (Input IN, inout SurfaceOutput o) {
			float2 uv = IN.proj.xy/IN.proj.w;
			#if UNITY_UV_STARTS_AT_TOP
			uv.y = 1 - uv.y;
			#endif
			fixed4 water = (tex2D(_WaterTex, IN.uv_WaterTex + float2(_WaterSpeed*_Time.x,0))+tex2D(_WaterTex, float2(1-IN.uv_WaterTex.y,IN.uv_WaterTex.x) + float2(_WaterSpeed*_Time.x,0)))/2;
			float4 offsetColor = (tex2D(_BumpTex, IN.uv_WaterTex + float2(_WaterSpeed*_Time.x,0))+tex2D(_BumpTex, float2(1-IN.uv_WaterTex.y,IN.uv_WaterTex.x) + float2(_WaterSpeed*_Time.x,0)))/2;
			half2 offset = UnpackNormal(offsetColor).xy * _Refract;//用于折射的uv偏移量
			half m_depth = LinearEyeDepth(tex2Dproj (_CameraDepthTexture, IN.proj).r);
			half deltaDepth = m_depth - IN.proj.z;//计算深度差

			fixed4 noiseColor = tex2D(_NoiseTex, IN.uv_NoiseTex);

			half4 bott = tex2D(_GrabTexture, uv+offset);
			fixed4 waterColor = tex2D(_GTex, float2(min(_Range.y, deltaDepth)/_Range.y,1));
			
			fixed4 waveColor = tex2D(_WaveTex, float2(1-min(_Range.z, deltaDepth)/_Range.z+_WaveRange*sin(_Time.x*_WaveSpeed+noiseColor.r*_NoiseRange),1)+offset);
			waveColor.rgb *= (1-(sin(_Time.x*_WaveSpeed+noiseColor.r*_NoiseRange)+1)/2)*noiseColor.r;
			fixed4 waveColor2 = tex2D(_WaveTex, float2(1-min(_Range.z, deltaDepth)/_Range.z+_WaveRange*sin(_Time.x*_WaveSpeed+_WaveDelta+noiseColor.r*_NoiseRange),1)+offset);//这里计算了两个海浪,其中第二个海浪和第一个海浪存在相位差
			waveColor2.rgb *= (1-(sin(_Time.x*_WaveSpeed+_WaveDelta+noiseColor.r*_NoiseRange)+1)/2)*noiseColor.r;
			
			half water_A = 1-min(_Range.z, deltaDepth)/_Range.z;
			half water_B = min(_Range.w, deltaDepth)/_Range.w;
			float4 bumpColor = (tex2D(_BumpTex, IN.uv_WaterTex+offset + float2(_WaterSpeed*_Time.x,0))+tex2D(_BumpTex, float2(1-IN.uv_WaterTex.y,IN.uv_WaterTex.x)+offset + float2(_WaterSpeed*_Time.x,0)))/2;

			o.Normal = UnpackNormal(bumpColor).xyz;
			
			o.Specular = _Specular;
			o.Gloss = _Gloss;
			o.Albedo = bott.rgb * (1 - water_B) + waterColor.rgb * water_B;
			o.Albedo = o.Albedo * (1 - water.a*water_A) + water.rgb * water.a*water_A;
			o.Albedo += (waveColor.rgb+waveColor2.rgb) * water_A; 
			
			o.Alpha = min(_Range.x, deltaDepth)/_Range.x;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}



Demo下载地址:http://www.lsngo.net/2017/10/04/unityshader_seawave/最底下有git链接

更多内容http://www.lsngo.net

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Unity Shader】unity海边波浪效果的实现 的相关文章

  • 游戏策划:游戏开发中的关键环节

    在数字游戏的世界里 游戏策划是构建一个成功游戏的基石 游戏策划不仅仅是一个创意过程 它涉及从故事构建到技术实现的各个方面 以下是游戏策划中需要重点关注的几个重要内容 1 故事情节与世界观构建 一款游戏的魅力很大程度上取决于其故事情节和世界观
  • 修复 Nvidia 和 AMD 的 GLSL 着色器

    我在让 GLSL 着色器在 AMD 和 Nvidia 硬件上工作时遇到问题 我并不是在寻求修复特定着色器的帮助 而是寻求如何避免出现这些问题 是否可以检查着色器是否可以在 AMD Nvidia 驱动程序上编译 而无需在具有相应硬件的计算机上
  • Three.js - 将飞机缩放至全屏

    我将一架飞机添加到场景中 如下所示 Camera this three camera new THREE PerspectiveCamera 45 window innerWidth window innerHeight 0 1 60 Pl
  • 将阴影添加到视差遮挡贴图

    我已经通过实现视差遮挡贴图学习OpenGL https learnopengl com Advanced Lighting Parallax Mapping 现在我想添加自阴影 以便片段挤压在表面上投射阴影 我读过一些关于这个主题的论文 但
  • 带有 OpenGL 的 Qt MDI 应用程序:如何获取有效的屏幕截图?

    我有一个MDI http en wikipedia org wiki Multiple document interface用 Qt 编写的应用程序 一些子窗口包括QGLWidgets 即 OpenGL 上下文 其中最突出的是使用 Open
  • openGL:带有着色器的线条

    如何使用着色器创建一条线 可能是彩色的 我正在使用可编程管道 并且我是 openGL 的初学者 我找不到有关如何使用着色器绘制线条的示例 我想我必须将 VAO 顶点数组对象 加载到着色器中 但是然后呢 我应该使用哪些功能以及如何使用 首先
  • 游戏开发创建操作之玩家信息系统的建立

    游戏一般都需要玩家信息系统 那么我们应该如何搭建玩家信息系统 接下来我将展示一种简单的方法 完整代码如下 using System Collections using System Collections Generic using Uni
  • 游戏开发常见操作系列之敌人系统的开发一(U3D)

    在开发游戏的过程中 我们常常会出现一些敌人攻击我们玩家 并且实现掉血以及死亡的现象 敌人还会源源不断地生成 这是怎么制作的呢 接下来为大家提供方法 其中使用了NGUI 后续会更新其它方法 敬请期待 使用HUDText实现扣血时显示文本 直接
  • 着色器使立方体的边缘倾斜?

    这个问题涉及使用着色器 可能在 Unity3D 环境中 但 Metal 或 OpenGL 都可以 以在网格最小立方体上实现圆角边缘 我希望只使用 12 个三角形的简约网格立方体 然后通过着色器 使每个块的边缘 角 稍微倾斜 其实这样可以吗带
  • 处理中点/笔划的景深着色器

    最近我一直在使用下面的景深着色器 最初来自ofx后处理 https github com neilmendoza ofxPostProcessing blob master src DofPass cppOpenFrameworks 库 用
  • 用于在基于着色器的游戏中进行渲染的 OO 架构

    在构建游戏引擎时 我一直遇到这个问题 我的类希望看起来像这样 interface Entity draw class World draw for e in entities e draw 这只是伪代码 大致展示了绘图是如何发生的 每个实体
  • 使用 texelFetch() 进行纹理化

    当我将非最大值传递到纹理缓冲区时 在渲染时它会绘制具有最大值颜色的几何图形 我在使用 glTexBuffer API 时发现了这个问题 例如 假设我的纹理数据是GLubyte 当我传递任何小于255的值时 那么颜色与用255绘制的颜色相同
  • OpenGL 定向光着色器

    我想使用 OpenGL 和 GLSL 将定向光添加到我的场景中 问题在于 理论上正确的方法会产生错误的结果 在顶点着色器中我执行以下操作 光线的方向以世界坐标给出 并使用 viewMatrix 转换为相机坐标 使用法线矩阵将顶点的法线转换为
  • GLSL 纹理立方体和纹理2D 在同一着色器中

    我似乎无法两者兼得texture2D and textureCube 在一个着色器中 当我这样做时 什么也没有显示 也没有错误 我用我自己的着色器加载器和 Apple GLSL 着色器生成器尝试了这一点 并且发生了同样的事情 即使我有也会发
  • 如何将shadershop公式转换成glsl

    我最近一直在学习着色器的一些基础知识 并且想出了一个很棒的视觉工具 着色器商店 http www cdglabs org Shadershop 但我无法将我在此站点中创建的公式转换为 glsl 一个简单的例子 我在此网站中创建了一个公式 我
  • 在Unity中如何使两个精灵的重叠区域透明?

    在Unity中如何使两个精灵的重叠区域透明 你能写一个关于它的着色器吗 经过一些研究 我了解到我应该使用模板缓冲区 但我不知道如何使用 这对我来说至关重要 我必须在 6 天内完成这个学校项目 请帮忙 示例图片 就这样 请记住这是我第一次使用
  • 对数/线性 Z 缓冲区阴影贴图

    问题在于 伪影出现在距离很远的阴影中 我想尝试制作一个对数深度缓冲区 但我不明白应该在哪里完成以及如何完成 我对全向阴影贴图使用点光源方法 顶点着色器 version 460 core layout location 0 in vec3 a
  • 对 VBO 中的特定三角形使用不同的纹理

    我有 9 个由三角形组成的四边形 如下所示 我在用着VBO存储有关它们的数据 它们的位置和纹理坐标 我的问题是 是否可以仅使用一个来使四边形 5 具有与其余四边形不同的纹理VBO and shader 绿色代表纹理 1 黄色代表纹理 2 到
  • LibGDX - 着色器适用于桌面但不适用于 Android

    我编写了一个简单的程序 可以在 3D 环境中渲染球体 并根据球体周围的四个光源为其着色 当我在桌面上运行该程序时 它工作得很好 但在 Android 设备上 球体只是纯色的 下面是一些图片来说明我正在谈论的内容 gt Desktop gt
  • 在 QML 中控制纹理 3D 对象的不透明度

    我对 QML 中的 Qt 3D 有点陌生 我正在尝试控制 Qt 3D 的不透明度textured3D 对象 我正在使用简单qml3d https github com tripolskypetr simpleqml3d测试项目来做到这一点

随机推荐

  • 二叉树(构造篇)

    二叉树 纲领篇 先复述一下前文总结的二叉树解题总纲 是否可以通过遍历一遍二叉树得到答案 如果可以 用一个 traverse 函数配合外部变量来实现 这叫 遍历 的思维模式 是否可以定义一个递归函数 通过子问题 子树 的答案推导出原问题的答案
  • 华为OD机试 - 判断一组不等式是否满足约束并输出最大差(Java)

    题目描述 给定一组不等式 判断是否成立并输出不等式的最大差 输出浮点数的整数部分 要求 不等式系数为 double类型 是一个二维数组 不等式的变量为 int类型 是一维数组 不等式的目标值为 double类型 是一维数组 不等式约束为字符
  • sublime添加直接运行语言的方法

    Tools Build system New Build System 添加新的编译文件 添加lua编译环境 cmd usr local bin lua file file regex lua t 0 9 0 9 selector sour
  • js检索关键字

    var i str indexOf 关键字 formi 查找str中formi的位置之后的下一个关键字的下标值 如果省略第二个关键字 则默认从0开始查找 如果没有找到 则返回 1 var i str lastIndexOf 关键字 form
  • title=“{{item.id}}“: Interpolation inside attributes has been removed. Use v-bind or the colon short

    title item id Interpolation inside attributes has been removed Use v bind or the colon shorthand instead v for列表渲染中给a ca
  • python中codecs模块_python自然语言编码转换模块codecs介绍

    python对多国语言的处理是支持的很好的 它可以处理现在任意编码的字符 这里深入的研究一下python对多种不同语言的处理 有一点需要清楚的是 当python要做编码转换的时候 会借助于内部的编码 转换过程是这样的 原有编码 gt 内部编
  • Linux环境下安装JDK

    安装jdk有两种方法 手动安装 yum安装 yum安装如下 1 查询要安装jdk的版本 yum y list java 2 安装jdk1 8 yum install y java 1 8 0 openjdk x86 64 3 查询jdk版本
  • Windows 系统中安装 MySQL 5.6 zip 步骤并配置 root 密码

    说明 最早我安装 MySQL 还是使用安装版的进行安装 但是作为一名程序员 MySQL 公司既然提供了 zip 版本的安装 我们当然要使用这种绿色的安装方式 MySQL 5 6 版本和 5 7 版本的安装步骤有很大不同 本文记录了 5 6
  • ultraiso制作u盘启动盘教程图文详解纯净-U盘启动教程

    制作u盘启动盘用软碟通ultraiso轻松制作纯净windows7 u盘装系统 网友们除了知道的u大师u盘启动盘制作工具 u启动 u深度 老毛桃 大白菜u盘启动盘制作工具外 还有量产 fbinstTool 我这再介绍一种u盘启动盘的制作方式
  • maven子依赖版本覆盖父依赖

    比如父依赖定义了jaskson version为2 13 3 在
  • 01-Java基础-变量

    一 变量介绍 变量就是向操作系统申请内存来存储值 也就是说 当创建变量的时候 需要在内存中申请空间 内存管理系统根据变量的类型为变量分配存储空间 分配的空间只能用来储存该类型数据 简单理解 类似数学中的 设 x 1 在程序中就表示声明了一个
  • 移动级处理芯片岁末盘点

    时间过得真快 不知不觉间又到了年关 这就说明一年一度做盘点汇总的时候也要到了 作为即将踏入这个科技行业快有三个年头的媒体人 笔者也这在这段时间内跟随新兴的移动互联网市场一起成长着 同时也看尽了这三年来行业里无情的变迁 感叹身在同一个行业里厂
  • 层次分析法(多准则决策方法)

    这是介于定量分析与定性分析的一种方法 运用层次分析法建模 大体上可按下面四个步骤进行 建立递阶层次结构模型 构造出各层次中的所有判断矩阵 层次单排序及一致性检验 层次总排序及一致性检验 建立递阶层次结构模型 每一层次中各元素所支配的元素一般
  • 笔试题13:采用UDP协议,编写一个简单发送字符串的程序(源码)

    UDP协议是一种无须建立连接的网络通信协议 采用Java来编写 一般有以下几个步骤 包括接收端和发送端 1 创建数据Socket 指定一个端口号 2 对于接收消息的一端来说 提供一个byte数组进行数据的存储 而对于发送消息一端 除此之外还
  • keras 对于大数据的训练,无法一次性载入内存,使用迭代器

    说明 我是在keras的官方demo上进行修改https github com fchollet keras blob master examples imdb cnn py 1 几点说明 从文件中读入数据 会降低GPU的使用率 如果能够直
  • C++【侯捷】——— 成员模板、模板特化、模板模板参数

    C 侯捷 成员模板 模板特化 模板模板参数 成员模板 模板特化 偏特化 模版模板参数 自己写的程序最好用namespace包起来 不要直接使用全局的using namespace 尤其是代码量比较多的大程序 成员模板 类模板在使用的时候 后
  • Vim的NerdTree插件

    在vundle插件管理的方式 直接在 vimrc中的Plugin段落中加入 Plugin scrooloose nerdtree 然后重启Vim并输入PluginInstall 即可完成安装 然后输入 NERDTreeToggle即可打开文
  • vue-property-decorator的简单介绍,一看就会

    参考 https github com kaorun343 vue property decorator 怎么使vue支持ts写法呢 我们需要用到vue property decorator 这个组件完全依赖于vue class compo
  • Layui学习笔记,隔壁都馋哭了

    添加一个Tab 切换用户管理 删除商品管理 删除所有tab 简单风格的Tab 网站设置 用户管理 权限分配 商品管理 订单管理 高度默认自适应 也可以随意固宽 2 Tab进行了响应式处理 所以无需担心数量多少 内容2 内容3 内容4 内容5
  • 【Unity Shader】unity海边波浪效果的实现

    效果图如下 GIF因为为了把图压小所以删掉了一些帧导致后面速度突然很快 实际效果并不是这样 PS 对于移动端 参考该文章 http www lsngo net 2018 03 22 unity seawave vertexcolor 之前在