音之国度的进入界面想搞点事情,想来想去,觉得点击滑动屏幕造成界面产生一个水波的效果还是非常理想的。
首先想想屏幕动画的原理,来自与纹理的偏移。简单的来说,屏幕任意一点的像素uv发生了偏移,导致取得颜色在逐帧更新。
其次我们再来想想如何做出水波的动画,比如我们如果要让运动物理做一个小鱼的移动动画,我们可以让他的轨迹满足正弦函数,显然,我们要做出屏幕水波特效,我们需要让屏幕像素的uv偏移量按照某种规律逐帧发生变化。
那么应该满足怎么的规律呢?我们想到了实际物理学中波的传递,也就是说,鼠标点击所在就是水波能量的源点,然后这个能量慢慢向四周传递,并且传递的过程中能量也逐渐递减。
接着我们再来说说程序实现的具体思路。
首先我们维护一个二维数组,这个数组保存着屏幕像素点当前帧的uv偏移量,初始为0。
一旦用户鼠标产生一个点击事件,我们就投入一个圆形区域的能量源,也就是给这个区域内的像素空间uv偏移量赋上值。
接着我们开一个用户线程,这个线程用于传递能量,也就是让二维数组中的每一个值,都等于以他为中心的正方形模板的偏移量的一个加权平均,接着再对这个值做一个衰减函数的操作。(当然这个过程中有一些数据的归一处理之类的)。
代码如下:
using UnityEngine; using System.Collections; using System.Threading; public class wavetexture : MonoBehaviour { public int wave_height; public int wave_width; public float xoffset; public float yoffset; float[,] Wave_last; float[,] Wave_next; Texture2D tex_uv; bool isRun=true; int sleeptime; Color[] colorbuffer; public Camera cam; void Start () { Wave_last = new float[wave_width, wave_height]; Wave_next = new float[wave_width, wave_height]; tex_uv = new Texture2D (wave_width, wave_height); colorbuffer = new Color[wave_width * wave_height]; GetComponent<Renderer> ().material.SetTexture ("_wavetex", tex_uv); Thread th = new Thread (new ThreadStart (ComputeWave)); th.Start (); } void Update () { sleeptime = (int)(Time.deltaTime * 1000); tex_uv.SetPixels (colorbuffer); tex_uv.Apply (); if(Input.GetMouseButton(0)) { RaycastHit hit; Ray ray = cam.ScreenPointToRay (Input.mousePosition); if(Physics.Raycast(ray,out hit,200.0f)) { if(hit.collider.transform.name=="sky") { Debug.Log ("flags"); Vector3 hitpoint=this.transform.worldToLocalMatrix.MultiplyPoint (hit.point); Debug.Log (hitpoint.x+" "+hitpoint.y); int xforu = (int)((hitpoint.x+xoffset) * wave_width); int yforv = (int)((hitpoint.y+yoffset) * wave_height); put (xforu, yforv); } } } } void put(int x,int y) { int riaus=8; float dist; for(int i=-riaus;i<=riaus;i++) { for (int j = -riaus; j <= riaus; j++) { if(x+i>=0&&x+i<=wave_width-1&&y+j>=0&&y+j<=wave_height-1) { dist = Mathf.Sqrt (i*i+j*j); if (dist < riaus) { Wave_last [x + i, y + j] = Mathf.Cos (dist * Mathf.PI / riaus); } } } } } void ComputeWave() { while (isRun) { for (int w = 1; w < wave_width - 1; w++) { for (int h = 1; h < wave_height - 1; h++) { Wave_next [w, h] = (Wave_last [w - 1, h] + Wave_last [w + 1, h] + Wave_last [w, h - 1] + Wave_last [w, h + 1] + Wave_last [w - 1, h - 1] + Wave_last [w + 1, h - 1] + Wave_last [w - 1, h + 1] + Wave_last [w + 1, h + 1]) / 4 - Wave_next [w, h]; if (Wave_next [w, h] >= 1) { Wave_next [w, h] = 1.0f; } if (Wave_next [w, h] <= -1) { Wave_next [w, h] = -1.0f; } float offset_u = (Wave_next [w - 1, h] - Wave_next [w + 1, h]) / 2; float offset_v = (Wave_next [w, h - 1] - Wave_next [w, h + 1]) / 2; float r = offset_u / 2 + 0.5f; float g = offset_v / 2 + 0.5f; colorbuffer [w + wave_width * h]=new Color(r,g,0); Wave_next [w, h] -= Wave_next [w, h] * 0.001f; } } float[,] Wave_l; Wave_l = Wave_next; Wave_next = Wave_last; Wave_last = Wave_l; Thread.Sleep (sleeptime); } } void OnDestroy() { isRun = false; } }
具体用于渲染的shader:
扫描二维码关注公众号,回复:
168829 查看本文章
Shader "Custom/texure_animation" { Properties { _MainTex ("Main", 2D) = "white" {} _SecondTex ("Second", 2D) = "white" {} _F("F",Range(0,12))=1 } SubShader { pass { CGPROGRAM #pragma vertex ver #pragma fragment frag #pragma target 3.0 #include "UnityCG.cginc" sampler2D _MainTex; sampler2D _SecondTex; sampler2D _wavetex; float _F; struct v2f { float4 pos:POSITION; float2 uv:TEXCOORD0; }; v2f ver(appdata_full v) { v2f o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.uv=v.texcoord.xy; return o; } fixed4 frag(v2f IN):COLOR { float2 uv=IN.uv; float2 uv_wave=tex2D(_wavetex,IN.uv).xy; uv_wave=uv_wave*2-1; uv_wave*=0.03f; IN.uv+=uv_wave; fixed4 Maincoloroftex=tex2D(_MainTex,IN.uv); float2 offset_uv=float2(0.01*_Time.y,0); uv=IN.uv+offset_uv; fixed4 color_1=tex2D(_SecondTex,uv); Maincoloroftex*=color_1.b; Maincoloroftex*=3.5f; //Maincoloroftex.rgb+=color_1.rgb; /*uv=IN.uv-offset_uv; fixed4 color_2=tex2D(_SecondTex,uv); Maincoloroftex.rgb*=color_2.rgb; Maincoloroftex.rgb*=2;*/ return Maincoloroftex; } ENDCG } } FallBack "Diffuse" }