how to improve this ocean shader for mobile

Hi there,

I’ve been changing and improving the ocean shader (I posted on that thread too), but I’m no shader expert. It has to run for mobile so it has to be as fast and simple as possible. This is what I have so far and I’m sure it can be hugely improved for mobile. It runs at about 30fps on a Samsung galaxy 2 and about 7 fps on a nexus one. It has a few #if defined so I can add/remove some of the effects but it makes no performance improvements at all.

So any shader experts wants to give it a try?
(I also attach a picture of what the shader generates on screen of my samsung galaxy s2 :slight_smile:

// Upgrade NOTE: now shader work on mobile

Shader "Mobile/OceanReflectionRefraction" 
{
	Properties 
	{
		_SurfaceColor ("SurfaceColor", Color) = (1,1,1,1)
		_WaterColor ("WaterColor", Color) = (1,1,1,1)
		_Refraction ("Refraction (RGB)", 2D) = "white" {}
		_Reflection ("Reflection (RGB)", 2D) = "white" {}
		_Fresnel ("Fresnel (A) ", 2D) = "gray" {}
		_Bump ("Bump (RGB)", 2D) = "bump" {}
		_Foam ("Foam (RGB)", 2D) = "white" {}
		_Size ("Size", Vector) = (1, 1, 1, 1)
		_SunDir ("SunDir", Vector) = (0.3, -0.6, -1, 0)
		_UseSpecular("Use Specular", Float) = 1.0
	}
	
	SubShader {
		  Tags {
		  	"RenderType"="Opaque"
		  }
		  Lighting Off
  
    Pass {
  
CGPROGRAM
#pragma multi_compile USE_REFRACTION USE_REFLECTION
#pragma exclude_renderers xbox360
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct v2f 
{
    float4 pos : SV_POSITION;
    float4  projTexCoord : TEXCOORD0;
    float2  bumpTexCoord : TEXCOORD1;
    float3  viewDir : TEXCOORD2;
    float3  objSpaceNormal : TEXCOORD3;
    float3  lightDir : TEXCOORD4;
    float2  foamStrengthAndDistance : TEXCOORD5;
};

float4 _Size;
float4 _SunDir;

v2f vert (appdata_tan v)
{
    v2f o;
    
    o.bumpTexCoord.xy = v.vertex.xz/float2(_Size.x, _Size.z)*10;
    
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    
    o.foamStrengthAndDistance.x = v.tangent.w;
    o.foamStrengthAndDistance.y = clamp(o.pos.z, 0, 1.0);
    
    
  	float4 projSource = float4(v.vertex.x, 0.0, v.vertex.z, 1.0);
    float4 tmpProj = mul( UNITY_MATRIX_MVP, projSource);
    o.projTexCoord = tmpProj;

    float3 objSpaceViewDir = ObjSpaceViewDir(v.vertex);
    float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) );
	float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal );
    
    o.objSpaceNormal = v.normal;
    o.viewDir = normalize(mul(rotation, objSpaceViewDir));
    
    o.lightDir = normalize(mul(rotation, float3(_SunDir.xyz)));
    
    return o;
}

uniform fixed _UseSpecular;
sampler2D _Refraction;
sampler2D _Reflection;
sampler2D _Fresnel;
sampler2D _Bump;
sampler2D _Foam;
half4 _SurfaceColor;
half4 _WaterColor;

half4 frag (v2f i) : COLOR
{
	// calculate the bump of the waves/water, i'm sure there is a better way to do it on the vertex section
	half4 buv = half4(i.bumpTexCoord.x + _Time.x * 0.03, i.bumpTexCoord.y + _SinTime.x * 0.2, i.bumpTexCoord.x + _Time.y * 0.04, i.bumpTexCoord.y + _SinTime.y * 0.5);
	half3 tangentNormal0 = (tex2D(_Bump, buv.xy).rgb * 2.0) - 1;
	half3 tangentNormal1 = (tex2D(_Bump, buv.zw).rgb * 2.0) - 1;
	half3 tangentNormal = normalize(tangentNormal0 + tangentNormal1);
	
	float2 projTexCoord = 0.5 * i.projTexCoord.xy * float2(1, _ProjectionParams.x) / i.projTexCoord.w + float2(0.5, 0.5);
	
	half4 result = half4(0, 0, 0, 1);
	
	float2 bumpSampleOffset = i.objSpaceNormal.xz * 0.05 + tangentNormal.xy * 0.05;
	
#if defined(USE_REFLECTION)
	half3 reflection = tex2D(_Reflection, projTexCoord.xy + bumpSampleOffset).rgb * _SurfaceColor.rgb;
#endif

#if defined(USE_REFRACTION)
	half3 refraction = tex2D(_Refraction, projTexCoord.xy + bumpSampleOffset).rgb * _WaterColor.rgb;
#endif


	
	// depending on the strengh (cresh of wave?) generate foam and the slowly fade it out
	float foamStrength = i.foamStrengthAndDistance.x * 1.8;
	half4 foam = clamp(tex2D(_Foam, i.bumpTexCoord.xy * 1.0)  - 0.5, 0.0, 1.0) * foamStrength;

if (_UseSpecular > 0) {
	float3 halfVec = normalize(i.viewDir - i.lightDir);
    float specular = pow(max(dot(halfVec, tangentNormal.xyz), 0.0), 250.0);
	#if defined(USE_REFLECTION)  defined(USE_REFRACTION) 
			float fresnelLookup = dot(tangentNormal, i.viewDir);
			float bias = 0.06;
			float power = 4.0;
			float fresnelTerm = bias + (1.0-bias)*pow(1.0 - fresnelLookup, power);
			result.rgb = lerp(refraction, reflection, fresnelTerm) + clamp(foam.r, 0.0, 1.0) + specular;
	#elif defined(USE_REFLECTION)
			result.rgb = reflection + clamp(foam.r, 0.0, 1.0) + specular;
	#elif defined(USE_REFRACTION)
			result.rgb = refraction + clamp(foam.r, 0.0, 1.0) + specular;
	#else
			result.rgb = _WaterColor.rgb + clamp(foam.r, 0.0, 1.0) + specular;
	#endif

} else {
	#if defined(USE_REFLECTION)  defined(USE_REFRACTION) 
		float fresnelLookup = dot(tangentNormal, i.viewDir);
		float bias = 0.06;
		float power = 4.0;
		float fresnelTerm = bias + (1.0-bias)*pow(1.0 - fresnelLookup, power);
		result.rgb = lerp(refraction, reflection, fresnelTerm) + clamp(foam.r, 0.0, 1.0);
	#elif defined(USE_REFLECTION)
		result.rgb = reflection + clamp(foam.r, 0.0, 1.0);
	#elif defined(USE_REFRACTION)
		result.rgb = refraction + clamp(foam.r, 0.0, 1.0);
	#else
		result.rgb = _WaterColor.rgb + clamp(foam.r, 0.0, 1.0);
	#endif

}
//	result.rgb = result.rgb;
//	result.a = 1.0;

    return result;
}
ENDCG

}
}

}

The Nexus One has an Adreno 200 GPU in it. Even though this supports pixel shader 2, for performance reasons you should really be treating it like an old fixed function GPU.
So, limit yourself to 2 texture lookups and about 4 ALU ops, don’t use dependant texture lookups, avoid floating point registers, don’t switch between floating point and fixed point registers, avoid any sort of branching.
For something as pixel intensive as a water shader this doesn’t leave you many options. My advice would be to bake out an animated water texture offline for use on really low end hardware.
Vertex shader performance isn’t great either so you may want to simplify that too.