Combining Tessellation and the Water Shaders

I’ve been toying around with adding tessellation’s to the water shader’s including in Unity Pro.

I’m fairly close but have ran into one issue.

I can’t get the vertex location into the surface shader.

In the following shader it’s vertL, which is currently just the world position from the surface shader input.

Any help would be greatly appreciated,
Thanks.

Shader "Custom/HawkWater_Tesselated" {
	Properties {
		_Tess ("Tessellation", Range(1,32)) = 4
		_WaveScale ("Wave scale", Range (0.02,0.15)) = 0.063
		_ReflDistort ("Reflection distort", Range (0,1.5)) = 0.44
		_RefrDistort ("Refraction distort", Range (0,1.5)) = 0.40
		_RefrColor ("Refraction color", COLOR)  = ( .34, .85, .92, 1)
		_Fresnel ("Fresnel (A) ", 2D) = "gray" {}
		_BumpMap ("Normalmap ", 2D) = "bump" {}
		WaveSpeed ("Wave speed (map1 x,y; map2 x,y)", Vector) = (19,9,-16,-7)
		_ReflectiveColor ("Reflective color (RGB) fresnel (A) ", 2D) = "" {}
		_ReflectiveColorCube ("Reflective color cube (RGB) fresnel (A)", Cube) = "" { TexGen CubeReflect }
		_HorizonColor ("Simple water horizon color", COLOR)  = ( .172, .463, .435, 1)
		_MainTex ("Fallback texture", 2D) = "" {}
		_ReflectionTex ("Internal Reflection", 2D) = "" {}
		_RefractionTex ("Internal Refraction", 2D) = "" {}
	}
	SubShader {
		
		Tags { "RenderType"="Opaque" }
		LOD 300
		

		CGPROGRAM
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs.
#pragma exclude_renderers gles

		 #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessDistance nolightmap

        #pragma target 5.0
        #include "Tessellation.cginc"

		sampler2D _MainTex;
		sampler2D _ReflectionTex;
		sampler2D _ReflectiveColor;
		sampler2D _BumpMap;
		
		float _Tess;
		float4 _WaveScale4;
		float4 _WaveOffset;
		float T;
		float _ReflDistort;
		
		
		
		//////////////////////////////////////
		//float4 
		//////////////////////////////////////
        
        struct Input {
			float2 uv_MainTex;
			float3 worldPos;
			float3 viewDir;
			float4 screenPos;
			INTERNAL_DATA
		}; 
		
		struct appdata {
            float4 vertex : POSITION;
            float4 tangent : TANGENT;
            float3 normal : NORMAL;
            float2 texcoord : TEXCOORD0;
        };
        
        float4 tessDistance(appdata v0, appdata v1, appdata v2){
			float minDist = 0.0;
			float maxDist = 100.0;
			return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);
		}
		
		void disp (inout appdata v)
		{	
			// Just do a really simple Sine wave
			v.vertex.y += sin((T / 10) * v.vertex.z);
		}

		void surf (Input IN, inout SurfaceOutput o) {
			// scroll bump waves
			
			// The vertex location (This needs to be the actual vertex location)
			float4 vertL = float4(IN.worldPos,1);
			// Manually mulled the vert location with the world view matrix
			float4 secondaryLocation = mul (UNITY_MATRIX_MVP, vertL);
			
			// Scroll the bump waves
			float4 temp;
			temp.xyzw = vertL.xzxz * _WaveScale4 / unity_Scale.w + _WaveOffset;
			float2 bumpuv0 = temp.xy;
			float2 bumpuv1 = temp.wz;

			// Compute the screen positino
			float4 ref = ComputeScreenPos(secondaryLocation);
			
			// Normalize and calculate the view direction
			float3 viewDir = ObjSpaceViewDir(vertL);
			viewDir = normalize(viewDir);
	
			
			// combine two scrolling bumpmaps into one
			half3 bump1 = UnpackNormal(tex2D( _BumpMap, bumpuv0 )).rgb;
			half3 bump2 = UnpackNormal(tex2D( _BumpMap, bumpuv1 )).rgb;
			half3 bump = (bump1 + bump2) * 0.5;
			
			// fresnel factor
			half fresnelFac = dot( viewDir, bump );
			
			// Take all the info and actually generate the reflective surface
			
			float4 uv1 = ref; uv1.xy += bump * _ReflDistort;
			half4 refl = tex2Dproj( _ReflectionTex, UNITY_PROJ_COORD(uv1) );
			
			float4 c_holder;
			
			half4 water = tex2D( _ReflectiveColor, float2(fresnelFac,fresnelFac) );
			c_holder.rgb = lerp( water.rgb, refl.rgb, water.a );
			c_holder.a = refl.a * water.a;
			
			// Set the computed information
			o.Albedo = c_holder.rgb;
			o.Alpha = c_holder.a;
		}
		
		
		ENDCG
		
	
	}
	FallBack "Diffuse"
}

I’ve been trying to think of some alternatives and one keeps coming up. Can I do the tessellation on a compute shader? Or are the built in unity functions only usable in HLSL.

Have you seen how the pipeline works? (In this case, the DirectX 11 Graphics Pipeline)
There are a few steps which you can program, such as the vertex shader step, hull shader, pixel shader, etcetera. What happens between these steps is a lot more strict. Tessellation uses dedicated hardware. You can control how data goes into and out of these steps using semantics.

Similarly to tessellation, the output of a vertex is processed using hardware specifically designed for interpolating the results over the entire polygon, to each resulting fragment (possible pixel). A fragment gets data from multiple vertices, so it doesn’t really belong to one vertex.

The code you write in surface shader ends up in fragment shaders, so the rules are similar.

Anyways, I’m not going to try to give solutions specific to this problem, because I think it’s a problem you can avoid by taking a different approach, that is written more specific to the DirectX (and OpenGL) graphics pipeline.

What exactly do you want to achieve with the tessellation?

Well in my old surface shader I was able to create waves and pass the vertex point into the Surface shader. However when I added tesselation it changes the order in which the vertex shader operates so it’s not letting me output for some reason.

But the reason I want to add tessellation is because for large spans of water I don’t want the vertex to be high but i’d like the waves to be detailed up close, thus tessellation was the first idea that came to mind.

I got part of it working, I took out tesselation and added this tag at the top:

Tags { "WaterMode"="Refractive" "RenderType"="Opaque" }

It turns out the only thing really wrong with my shader above was this missing water tag.

But i’m still not sure how to go about tessellating water effectively.

Hi,
Is it possible to add some custom data to Input ?
I used to pass data from vertex to surface but it could be used the same way with SM5 and Tessellation. The vertex shader only seems to accept a void return and only one “inout appdata” parameter.