900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Unity shader实现水波效果

Unity shader实现水波效果

时间:2023-07-01 07:51:37

相关推荐

Unity shader实现水波效果

先放效果

只想要代码可以直接拉到3.0

后处理基类

后处理效果的基类,提供Shader参数来生成material的方法

using System.Collections;using System.Collections.Generic;using UnityEngine;[ExecuteInEditMode][RequireComponent(typeof(Camera))]public class PostEffectBase : MonoBehaviour{protected Material CheckShaderAndCreateMaterial(Shader shader, Material material){if(shader == null){return null;}if(shader.isSupported && material && material.shader == shader){return material;}if (!shader.isSupported){return null;} else{material = new Material(shader);//如果导入的材质的shader不是我们指定的shader,创建一个新的使用该shader的材质,并且不保存material.hideFlags = HideFlags.DontSave;if (material) return material;else return null;}}}

原理

把波产生的点作为圆心,往外的半径方向作为波的传输方向,半径长度作为s波的函数变量,通过波的公式偏移uv,从而模拟波。

1.0

一个从平面中心不断波动的效果

ps:不是后处理,直接作为material赋给平面使用,为了防止波变成椭圆,需要在变量中更新平面的长宽比。

Shader "MyShader/waterWave"{Properties{_MainTex ("Texture", 2D) = "white" {}_Strength("WaveStrength", Float) = 0.01_Speed("WaveSpeed", Float) = 10//waveLength越大波长越小_WaveLength("WaveLength", Float) = 10_PlaneWidth("PlaneWidth", Float) = 10_PlaneLength("PlaneLength", Float) = 10_WaveLengthChangeFactor("waveLengthChangeFactor" , Float) = 0.2}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _Strength;float _Speed;float _WaveLength;float _PlaneWidth;float _PlaneLength;float _WaveLengthChangeFactor;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed2 center = fixed2(0.5, 0.5);fixed2 offset = i.uv - center;//保存水波形状为圆offset *= float2(_PlaneLength / _PlaneWidth, 1);//uv点与中心点的距离float dist = sqrt(offset.x * offset.x + offset.y * offset.y);fixed2 offsetDir = normalize(offset);//以距离为函数扭曲屏幕,时间控制波往后传播,dist决定波形,实现的是不断往外波动的水波fixed2 newUV = i.uv + _Strength * offsetDir * sin(_Time.y * _Speed + dist * _WaveLength * (1 - dist) * _WaveLengthChangeFactor);return tex2D(_MainTex, newUV);}ENDCG}}}

2.0

改为后处理,在鼠标点击的任意地方都会产生水波,并且会逐渐扩散消失。

原理是点击时Csharp脚本更新当前的时间差为0,随着时间差的不断增大,将时间差*时间差因子看成是截取水波的半径,在此半径外一定范围的地方才保留uv的偏移。缺点是所有的uv由统一的时间差控制,即同一时刻只有一个水波。

修改:

为了提升观感,在每一帧都线性增大水波扩散的速度并且线性增大前文提到的半径的范围。查阅资料发现水波从里到外波长和频率不变振幅变小,并且和半径成反比(1/r)

using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// 后处理实现水波,并且可以同时在多个点点击产生/// </summary>public class WaterWavePostEffect : PostEffectBase{public Shader m_Shader;private Material m_Material;public Material material{get{m_Material = CheckShaderAndCreateMaterial(m_Shader, m_Material);return m_Material;}}/// <summary>/// 水波的振幅/// </summary>[SerializeField]private float WaveStrength = 0.01f;/// <summary>/// 水波往外的速度/// </summary>[SerializeField]private float WaveSpeed = 15;/// <summary>/// 水波的波长(越大波长越短)/// </summary>[SerializeField]private float WaveLength = 70;/// <summary>/// 水波从里到外波长减弱的因子/// </summary>[SerializeField]private float _WaveStrengthFallFactor = 1;/// <summary>/// 截取水波的半径增大的速度/// </summary>[SerializeField]private float WaveDistanceFactor = 1;/// <summary>/// 截取水波的宽度/// </summary>[SerializeField]private float WaveWidth = 0.1f;//水波往外扩散的距离,在此范围内的uv坐标才会收到影响private float curWaveDistance = 0;private float waveStartTime = float.MinValue;private float curWaveDistanceFactor;private Vector4 wavePos = new Vector4(0.5f, 0.5f, 0, 0);private void Awake(){curWaveDistanceFactor = WaveDistanceFactor;}private void OnRenderImage(RenderTexture source, RenderTexture destination){if(material != null){curWaveDistance = (Time.time - waveStartTime) * curWaveDistanceFactor;m_Material.SetFloat("_WaveSpeed", WaveSpeed);m_Material.SetFloat("_WaveStrength", WaveStrength);m_Material.SetFloat("_WaveLength", WaveLength);m_Material.SetFloat("_CurWaveDistance", curWaveDistance);m_Material.SetFloat("_WaveStrengthFallFactor", _WaveStrengthFallFactor);m_Material.SetFloat("_WaveWidth", WaveWidth);m_Material.SetVector("_WaveStartPos", wavePos);Graphics.Blit(source, destination, m_Material);}}private void Update(){if(Input.GetMouseButtonDown(0)){float timer = 0;curWaveDistanceFactor = WaveDistanceFactor;//Input.mousePostion 获得鼠标点击位置的屏幕像素空间坐标Vector2 mousePos = Input.mousePosition;wavePos = new Vector4(mousePos.x / Screen.width, mousePos.y / Screen.height ,0, 0);//水波产生waveStartTime = Time.time;while(timer <= 2){curWaveDistanceFactor += 0.2f * Time.deltaTime;timer += Time.deltaTime;}}//Debug.Log(waveStartTime);}}

Shader "MyShader/waterWavePostEffect"{Properties{_MainTex("Texture", 2D) = "white" {}}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _WaveStrength;float _WaveSpeed;float _WaveLength;float _WaveStrengthFallFactor;//不断延伸的半径距离,在离这个距离的_WaveWidth内才能产生水波float _CurWaveDistance;float _WaveWidth;float4 _WaveStartPos;v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{//鼠标点击的地方为中心点fixed2 center = fixed2(_WaveStartPos.x, _WaveStartPos.y);fixed2 offset = i.uv - center;//保存水波形状为圆offset *= float2(_ScreenParams.x / _ScreenParams.y, 1);//uv点与中心点的距离float dist = sqrt(offset.x * offset.x + offset.y * offset.y);//圆心往外的方向fixed2 offsetDir = normalize(offset);//在_CurWaveDistance的_WaveWidth距离内才会产生效果,Step(a,x),x<a返回0,x >= a 返回1float _CurWaveWidth = _WaveWidth * (1 + dist);float discardFactor = step(abs(dist - _CurWaveDistance), _CurWaveWidth);//以距离为函数扭曲屏幕,时间控制波往后传播,dist决定波形,实现的是不断往外波动的水波fixed2 offsetFactor = sin(_Time.y * _WaveSpeed + dist * _WaveLength);//水波从里到外波长不变振幅以1/r衰减float _CurWaveStrength = _WaveStrength * (1 / dist * _WaveStrengthFallFactor);fixed2 newUV = i.uv + _CurWaveStrength * offsetDir * offsetFactor * discardFactor;return tex2D(_MainTex, newUV);}ENDCG}}}

3.0

通过数组的存储使得同一时刻多个水波能够实现,逻辑将水波产生点和时间差改为数组,在片元着色器里面for循环分别每个产生水波产生点带来的uv偏移进行叠加,for循环结束后再赋值颜色。速度和半径范围的线性增大程度只和uv位置有关无需Csharp脚本传递。

using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// 后处理实现水波,并且可以同时在多个点点击产生/// </summary>public class WaterWavePostEffectPlus : PostEffectBase{public Shader m_Shader;private Material m_Material;private Vector4[] wavPoses = new Vector4[10];public Material material{get{m_Material = CheckShaderAndCreateMaterial(m_Shader, m_Material);return m_Material;}}/// <summary>/// 水波的振幅/// </summary>[SerializeField]private float WaveStrength = 0.03f;/// <summary>/// 水波往外的速度/// </summary>[SerializeField]private float WaveSpeed = 15;/// <summary>/// 水波的波长(越大波长越短)/// </summary>[SerializeField]private float WaveLength = 70;/// <summary>/// 水波从里到外波长减弱的因子/// </summary>[SerializeField]private float _WaveStrengthFallFactor = 0.2f;/// <summary>/// 截取水波的半径增大的速度/// </summary>[SerializeField]private float WaveDistanceFactor = 0.6f;/// <summary>/// 截取水波的宽度/// </summary>[SerializeField]private float WaveWidth = 0.1f;//水波往外扩散的距离,在此范围内的uv坐标才会收到影响//for循环下每个点都是独立的,需要记录其自己的开始时间,传播距离和距离变化因子private float[] curWaveDistance = new float[10];private float[] waveStartTime = new float[10];private float[] curWaveDistanceFactor = new float[10];private Vector4 wavePos = new Vector4(0.5f, 0.5f, 0, 0);//用于指定当前赋值点的下标private int index = 0;private void Awake(){for(int i = 0; i < curWaveDistanceFactor.Length; i++){curWaveDistanceFactor[i] = WaveDistanceFactor;}for (int i = 0; i < curWaveDistance.Length; i++){curWaveDistance[i] = 0;}for (int i = 0; i < curWaveDistanceFactor.Length; i++){curWaveDistanceFactor[i] = float.MinValue;}index = 0;}private void OnRenderImage(RenderTexture source, RenderTexture destination){if (material != null){for(int i = 0; i< wavPoses.Length; i++){curWaveDistance[i] = (Time.time - waveStartTime[i]) * curWaveDistanceFactor[i];}m_Material.SetFloatArray("_CurWaveDistance", curWaveDistance);m_Material.SetVectorArray("_WaveStartPoses", wavPoses);m_Material.SetFloat("_WaveSpeed", WaveSpeed);m_Material.SetFloat("_WaveStrength", WaveStrength);m_Material.SetFloat("_WaveLength", WaveLength);m_Material.SetFloat("_WaveStrengthFallFactor", _WaveStrengthFallFactor);m_Material.SetFloat("_WaveWidth", WaveWidth);//m_Material.SetVector("_WaveStartPos", wavePos);Graphics.Blit(source, destination, m_Material);}else{Graphics.Blit(source, destination);}}private void Update(){if (Input.GetMouseButtonDown(0)){if(index == 9){index = 0;}AddWavePos(index);index++;}//Debug.Log(waveStartTime);}/// <summary>/// 点击屏幕增加一个水波产生的原点/// </summary>/// <param name="i">水波点的下标</param>void AddWavePos(int i){Debug.Log("点击屏幕一次");float timer = 0;curWaveDistanceFactor[i] = WaveDistanceFactor;//Input.mousePostion 获得鼠标点击位置的屏幕像素空间坐标Vector2 mousePos = Input.mousePosition;wavePos = new Vector4(mousePos.x / Screen.width, mousePos.y / Screen.height, 0, 0);wavPoses[i] = wavePos;//水波产生waveStartTime[i] = Time.time;while (timer <= 2){curWaveDistanceFactor[i] += 0.2f * Time.deltaTime;timer += Time.deltaTime;}}}

Shader "MyShader/waterWavePostEffectPlus"{Properties{_MainTex("Texture", 2D) = "white" {}}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _WaveStrength;float _WaveSpeed;float _WaveLength;float _WaveStrengthFallFactor;//不断延伸的半径距离,在离这个距离的_WaveWidth内才能产生水波float _CurWaveDistance[10];float _WaveWidth;float4 _WaveStartPoses[10];v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{for (int j = 0; j < 10; j++) {//鼠标点击的地方为中心点fixed2 center = fixed2(_WaveStartPoses[j].x, _WaveStartPoses[j].y);fixed2 offset = i.uv - center;//保持水波形状为圆offset *= float2(_ScreenParams.x / _ScreenParams.y, 1);//uv点与中心点的距离float dist = sqrt(offset.x * offset.x + offset.y * offset.y);//圆心往外的方向fixed2 offsetDir = normalize(offset);//在_CurWaveDistance的_WaveWidth距离内才会产生效果,Step(a,x),x<a返回0,x >= a 返回1float _CurWaveWidth = _WaveWidth * (1 + dist);float discardFactor = step(abs(dist - _CurWaveDistance[j]), _CurWaveWidth);//以距离为函数扭曲屏幕,时间控制波往后传播,dist决定波形,实现的是不断往外波动的水波fixed2 offsetFactor = sin(_Time.y * _WaveSpeed + dist * _WaveLength);//水波从里到外波长不变振幅以1/r衰减float _CurWaveStrength = _WaveStrength * (1 / dist * _WaveStrengthFallFactor);i.uv = i.uv + _CurWaveStrength * offsetDir * offsetFactor * discardFactor;}return tex2D(_MainTex, i.uv);}ENDCG}}}

调一调参数就能产生一些比较好的效果,代码并没有非常斟酌的去写但是注释写了挺多,看懂了之后大家可以自行优化。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。