Newer
Older
_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(-0.2,0.2)) = -0.02
_RefractionDeform("Refraction Deform", Range(-0.2,0.2)) = 0.02
_Caustics("Caustics", Range(0,5)) = 1
[HideInInspector]_ReflectionTex("Reflection", 2D) = "white" {}
SubShader
//Render during the transparrent pass so that opaque objects under the water will render first
Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "LightMode"="ForwardBase"}
LOD 100
//Don't write to the depth buffer so that we can get correct depth information for objects under the water
ZWrite Off
//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 distortionNormal : NORMAL0;
float3 viewDir : NORMAL2;
fixed2 uv : TEXCOORD0;
float4 screenPos : TEXCOORD1;
half3 tspace0 : TEXCOORD2; // tangent.x, binormal.x, normal.x
half3 tspace1 : TEXCOORD3; // tangent.y, binormal.y, normal.y
half3 tspace2 : TEXCOORD4; // tangent.z, binormal.z, normal.z
};
fixed4 _DeepColor, _ShallowColor, _FoamColor, _FoamSpeed, _CausticsSpeed, _WaveA, _WaveB, _WaveC;
sampler2D _CausticsTex, _FoamTex, _NormalTex, _ReflectionTex, _CameraDepthTexture, _UnderWater, _CameraNormalsTexture;
float _WaveSpeed, _WaveHeight, _ShallowThreshold, _ShallowTint, _FoamThreshold, _ReflectionStrength, _ReflectionDeform, _RefractionDeform, _FoamHardness, _Caustics;
float4 _CausticsTex_ST, _FoamTex_ST, _NormalTex_ST, _CameraDepthTexture_TexelSize;
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, _WaveHeight);
p += GerstnerWave(_WaveB, gridPoint, tangent, binormal, _WaveSpeed, _WaveHeight);
p += GerstnerWave(_WaveC, gridPoint, tangent, binormal, _WaveSpeed, _WaveHeight);
//Calculate without binormal and tangent plane setup for water distortion to prevent shifting
o.distortionNormal = normalize(cross(binormal, tangent));
binormal.z +=1;
tangent.x+=1;
//Calculate with binormal and tangent plane setup for proper lighting calculation
float3 normal = normalize(cross(binormal, tangent));
o.tspace0 = half3(tangent.x, binormal.x, normal.x);
o.tspace1 = half3(tangent.y, binormal.y, normal.y);
o.tspace2 = half3(tangent.z, binormal.z, normal.z);
v.vertex.xyz = p;
o.viewDir = normalize(ObjSpaceViewDir ( v.vertex ));
o.vertex = UnityObjectToClipPos(v.vertex);
//Calculate screen position and pass to frag shader
o.screenPos = ComputeScreenPos(o.vertex);
o.uv = v.texcoord;
return o;
}
/// <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;
}
{
//Prep caustics UVs with proper scaling from material
float2 caustUV1 = TRANSFORM_TEX(i.uv, _CausticsTex);
float2 caustUV2 = caustUV1;
//Distort caustics based on depth texture texel size to make caustics look "underwater"
caustUV1.y += _CameraDepthTexture_TexelSize.z * abs(_CameraDepthTexture_TexelSize.y);
caustUV2.y += _CameraDepthTexture_TexelSize.z * abs(_CameraDepthTexture_TexelSize.y);
//Pan caustics sampling UVs across one another to produce realistic looking caustics patterns
caustUV1.xy = (caustUV1.xy + _CausticsSpeed.xy * _Time.y);
caustUV2.xy = (caustUV2.xy + _CausticsSpeed.zw * _Time.y);
//Rotate Caustic UV 2 so it can't line up perfectly and break immersion
float tmp = caustUV2.x;
caustUV2.x = caustUV2.y;
caustUV2.y = tmp;
//Combine geometry and normal map normals
fixed3 waveNorms1 = UnpackNormal(tex2D(_NormalTex, caustUV1));
fixed3 waveNorms2 = UnpackNormal(tex2D(_NormalTex, caustUV2));
fixed3 waveNormsCombined = normalize(waveNorms1 + waveNorms2);
//Calculate world space normals for lighting
fixed3 worldNormal;
worldNormal.x = dot(i.tspace0, waveNormsCombined);
worldNormal.y = dot(i.tspace1, waveNormsCombined);
worldNormal.z = dot(i.tspace2, waveNormsCombined);
//Sample caustics textures with normal distortion
fixed3 caustic1 = tex2D(_CausticsTex, caustUV1 - normalize(waveNormsCombined + i.distortionNormal)*_RefractionDeform).rgb;
fixed3 caustic2 = tex2D(_CausticsTex, caustUV2 - normalize(waveNormsCombined + i.distortionNormal)*_RefractionDeform).rgb;
//Combine samples
fixed3 causticrgb = caustic1 * caustic2;
// dot product between normal and light direction for
// standard diffuse (Lambert) lighting
half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
// factor in the light color
half lightingcol = nl * _LightColor0;
//Calculate refraction and reflection UV offsets
float3 refractionOffset = normalize(waveNormsCombined + i.distortionNormal) * _RefractionDeform * abs(_WaveHeight);
float3 reflectionOffset = normalize(waveNormsCombined + i.distortionNormal) * _ReflectionDeform * abs(_WaveHeight);
//Calculate screen space UV position
float2 screenUV = i.screenPos.xy / i.screenPos.w;
//Get opaque pass depth at current fragment with water distortion for refraction and without for foam
float depth = GetWaterDepth(i.screenPos, screenUV);
float distortedDepth = GetWaterDepth(i.screenPos, screenUV + refractionOffset);
// Initialize the output color with colour grabbed from behind water screen pass with water distortion
fixed3 col = tex2D(_UnderWater, screenUV + refractionOffset).rgb;
//Calculate water depth fog and color tint
float fogRatio = saturate(distortedDepth * _ShallowThreshold);
col.rgb *= (1 - _ShallowTint);
col.rgb += _ShallowColor * _ShallowTint;
col.rgb *= (1 - fogRatio);
col.rgb += _DeepColor * fogRatio;
//Apply caustics with a bias to the opposite of light direction
col.rgb += causticrgb * (1-lightingcol) * _Caustics;
//Calculate fresnel effect
float fresnel = 1 - saturate ( dot (worldNormal, i.viewDir ) );
//Add reflections using distorted sampling based on normals and blending based on fresnel
col.rgb *= 1 - (_ReflectionStrength * fresnel);
col.rgb += tex2D(_ReflectionTex, screenUV + reflectionOffset).rgb * _ReflectionStrength * fresnel;
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);
clamp(col, 0, 1);
return col;
}
ENDCG