[UI special effects] Procedural water wave texture

        I wanted to do something about the entry interface of the Kingdom of Sound. After thinking about it, I felt that the effect of clicking and sliding the screen to create a water wave on the interface is still very ideal.

        First think about the principle of screen animation, from the offset from the texture. Simply put, the pixel uv at any point on the screen is offset, causing the color to be updated frame by frame.

        Next, let's think about how to animate water waves. For example, if we want to use motion physics to make a moving animation of a small fish, we can make his trajectory satisfy the sine function. Obviously, we want to make screen water wave special effects, we need to make The uv offset of screen pixels changes frame by frame according to a certain law.

        So what rules should be met? We think of the transmission of waves in actual physics, that is to say, the point where the mouse clicks is the source of the water wave energy, and then this energy is slowly transmitted around, and the energy gradually decreases during the transmission process.

       Next, let's talk about the specific ideas of the program implementation.

       First, we maintain a two-dimensional array, which holds the uv offset of the current frame of the screen pixel, which is initially 0.

        Once the user's mouse generates a click event, we put an energy source in a circular area, that is, assign a value to the pixel space uv offset in this area.

       Then we open a user thread, which is used to transfer energy, that is, to make each value in the two-dimensional array equal to a weighted average of the offset of the square template centered on him, and then do this value. An operation of a decay function. (Of course, there is some normalization of data in this process and the like).

         code show as below:

      

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;
	}
}

The shader specifically used for rendering:

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"
}



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325802567&siteId=291194637