Skip to content
Snippets Groups Projects
WaterShader.shader 5.4 KiB
Newer Older
Liam Robinson's avatar
Liam Robinson committed
{
    Properties
    {
		_DeepColor("Deep Color", Color) = (1,1,1,1)
		_ShallowColor("Shallow Color", Color) = (1,1,1,1)
		_ShallowThreshold("Shallow Threshold", Range(0,5)) = 0.5
		_ShallowTint("Shallow Tint", Range(0,1)) = 0.5
		_FoamColor("Foam Color", Color) = (1,1,1,1)
		_FoamTex("Foam Texture", 2D) = "white" {}
		_FoamSpeed("Foam Speed", Vector) = (0.005, 0.02, -0.04 ,0.07)
		_FoamThreshold("Foam Threshold", Range(-5,5)) = 0.5
		_FoamHardness("Foam Hardness", Range(0,25)) = 0.5
		_CausticsTex("Caustics Texture", 2D) = "white" {}
		_CausticsSpeed("Caustics Speed", Vector) = (0.005, 0.02, -0.04 ,0.07)
		_WaveSpeed("Wave Speed", Range(-5,5)) = 0.5
		_WaveA("Wave A (dir, steepness, wavelength)", Vector) = (1,0,0.5,10)
		_WaveB("Wave B", Vector) = (0,1,0.25,20)
		_WaveC("Wave C", Vector) = (1,1,0.15,10)
		_ReflectionStrength("Reflection Strength", Range(0,1)) = 0.5
		_ReflectionDeform("Reflection Deform", Range(-1,1)) = 0.5
		[HideInInspector]_ReflectionTex("Reflection", 2D) = "white" {}
		//Render during the transparrent pass so that opaque objects under the water will render first
		Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
        LOD 100
		//Don't write to the depth buffer so that we can get correct depth information for objects under the water
		//Grab colour from opaque screen pass behind water
		GrabPass { "_UnderWater" }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
			#include "GerstnerWave.cginc"
			
            struct v2f
            {
				float4 vertex : SV_POSITION;
				float3 normal : NORMAL;
				fixed2 uv : TEXCOORD0;
				float4 screenPos : TEXCOORD2;
				float4 screenDistortedPos : TEXCOORD3;
            };
			
			fixed4 _DeepColor, _ShallowColor, _FoamColor, _FoamSpeed, _CausticsSpeed, _WaveA, _WaveB, _WaveC;
			sampler2D _CausticsTex, _FoamTex, _ReflectionTex, _CameraDepthTexture, _UnderWater, _CameraNormalsTexture;
			float _WaveSpeed, _ShallowThreshold, _ShallowTint, _FoamThreshold, _ReflectionStrength, _ReflectionDeform, _FoamHardness;
			float4 _CausticsTex_ST, _FoamTex_ST;
            v2f vert (appdata_full v)
            {
                v2f o;
				//Generate Waves
				float3 gridPoint = v.vertex.xyz;
				float3 tangent = 0;
				float3 binormal = 0;
				float3 p = gridPoint;
				p += GerstnerWave(_WaveA, gridPoint, tangent, binormal, _WaveSpeed);
				p += GerstnerWave(_WaveB, gridPoint, tangent, binormal, _WaveSpeed);
				p += GerstnerWave(_WaveC, gridPoint, tangent, binormal, _WaveSpeed);
				v.vertex.xyz = p;
				o.normal = v.normal = normalize(cross(binormal, tangent));
				o.vertex = UnityObjectToClipPos(v.vertex);
				//Calculate screen position and pass to frag shader
				o.screenPos = ComputeScreenPos(o.vertex);
				//Prep distorted screen pos sampling position for reflection distortion
				o.screenDistortedPos = o.screenPos;
				o.screenDistortedPos.xyz += o.normal.xyz * _ReflectionDeform;
			/// <summary>
			/// Gets the distance that water must travel through the water at a given point to reach the eye
			/// </summary>
			float GetWaterDepth(float4 screenPos, float2 screenUV) {
				float backgroundDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenUV));
				float surfaceDepth = UNITY_Z_0_FAR_FROM_CLIPSPACE(screenPos.z);
				return backgroundDepth - surfaceDepth;
			}
			fixed3 frag(v2f i) : SV_Target
			{
				//Calculate screen space UV position
				float2 screenUV = i.screenPos.xy / i.screenPos.w;
				//Get opaque pass depth at current fragment
				float depth = GetWaterDepth(i.screenPos, screenUV);
				//Calculate screen space opaque pass normals for caustics warping (un-implemented)
				//float3 existingNormal = tex2Dproj(_CameraNormalsTexture, UNITY_PROJ_COORD(i.screenPos));
				//float3 normalDot = saturate(dot(existingNormal, i.normal));

				// Initialize the output color with colour grabbed from behind water screen pass
				fixed3 col = tex2D(_UnderWater, screenUV).rgb;
				//Calculate water caustics
				float2 caustUV1 = TRANSFORM_TEX(i.uv, _CausticsTex);
				float2 caustUV2 = caustUV1;
				caustUV1.xy = (caustUV1.xy + _CausticsSpeed.xy * _Time.y);
				caustUV2.xy = (caustUV2.xy + _CausticsSpeed.zw * _Time.y);
				fixed3 caustic1 = tex2D(_CausticsTex, caustUV1).rgb;
				fixed3 caustic2 = tex2D(_CausticsTex, caustUV2).rgb;
				fixed3 causticrgb = caustic1 * caustic2;

				//Calculate water depth fog and color tint
				float fogRatio = saturate(depth * _ShallowThreshold);
				col.xyz *= (1 - _ShallowTint);
				col.xyz += _ShallowColor * _ShallowTint;
				col.xyz *= (1 - fogRatio);
				col.xyz += _DeepColor * fogRatio;
				col.xyz += causticrgb;

				//Add reflections using distorted sampling based on normals
				col.xyz += tex2Dproj(_ReflectionTex, i.screenDistortedPos).rgb * _ReflectionStrength;
				//Add foam after reflections
				float2 foamUV1 = TRANSFORM_TEX(i.uv, _FoamTex);
				float2 foamUV2 = foamUV1 + _FoamSpeed.zw * _Time.y;
				foamUV1.xy += _FoamSpeed.xy * _Time.y;
				float foamAmt = 1 - saturate((depth - _FoamThreshold) * _FoamHardness);
				foamAmt *= saturate(tex2D(_FoamTex, foamUV1).r * tex2D(_FoamTex, foamUV2).r * _FoamHardness);
				col.xyz += _FoamColor * foamAmt;
				clamp(col, 0, 1);
				return col;
            }
            ENDCG