Problem with own toon-water shader (lighting)

Hey guys,

I’ve written a simple water shader that holds two textures

  • one diffuse
  • one that’s responsible to get a wavy distortion.

The calculations work great and the water distorts exactly like I want to.

Now my problem is the lighting. I use the subpass “Diffuse/PPL” and this renders the diffuse texture on top without distortion so that it seems like I have two layers of water.

Here’s a screenshot with and without pixel lighting:

And here’s the shader code:

Shader "Game/Diffuse-Water"
{
	Properties
	{
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_NoiseTex ("Base (RGB)", 2D) = "white" {}
		_Speed ("Speed", Vector) = (0,0,1,1)
	}

	Category
	{
		Tags { "RenderType"="Opaque" }
		LOD 200
		Blend AppSrcAdd AppDstAdd
		Fog { Color [_AddFog] }
		
		SubShader
		{
			
			Pass
			{
				Name "BASE"
				Tags {"LightMode" = "Always"}
				
				CGPROGRAM
				#pragma fragment frag
				#include "UnityCG.cginc"
				
				struct v2f
				{
					float2	uv;
				};

				uniform sampler2D _MainTex : register(s0);
				uniform sampler2D _NoiseTex : register(s1);
				uniform float4 _Speed;
				
				float4 frag (v2f i) : COLOR
				{
					float2 noiseUV	= i.uv;
					
					noiseUV.x		+= _Time * _Speed.z;
					noiseUV.y		-= _Time * _Speed.w;
						
					half4 noiseTex	= tex2D( _NoiseTex, noiseUV );
					noiseTex.xy		= (noiseTex.xy * 2.0 - 1.0) * 0.035;
					
					return tex2D(_MainTex, i.uv + noiseTex.xy + _Time * _Speed.xy) * _PPLAmbient * 2.0;
				}
				ENDCG
			
				SetTexture [_MainTex] {constantColor [_Color] Combine texture * primary DOUBLE, texture * constant}
			}
			
			UsePass "Diffuse/PPL"
		
		}
	}

	Fallback "VertexLit", 1

}

Does somebody has any idea? I’ve tried for hours to fix this… :?

You’ll have to write your own, modified version of Diffuse/PPL if you want it to do the same distortion as your base pass. The source code is available in a sticky thread at the top of this forum.

Damn, I already did that and the only reason why it didn’t worked was that I needed two tex2D reads and so the shader fell back to VertexLit because I didn’t forced SM3.0.

I think there’s no way around to use a lower shader model since I need the two texture reads, or is it?

So here is what I have so far. The ambient shader works fine but the pixel shader doesn’t take fog into account and is way too dark.

Do I have some errors in the lighting calculations?

Shader "Game/Diffuse-Water"
{
	Properties
	{
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_NoiseTex ("Base (RGB)", 2D) = "white" {}
		_Speed ("Speed", Vector) = (0,0,1,1)
	}

	Category
	{
		Tags { "RenderType" = "Opaque" }
		LOD 300
		Blend AppSrcAdd AppDstAdd
		Fog { Color [_AddFog] }
		
		// ------------------------------------------------------------------
		// ARB fragment program
		
		SubShader
		{
			Pass
			{
				Name "BASE"
				Tags { "LightMode" = "None" }

				CGPROGRAM
				#pragma fragment frag
				#include "UnityCG.cginc"

				struct v2f
				{
					float2   uv;
				};

				uniform sampler2D _MainTex : register(s0);
				uniform sampler2D _NoiseTex : register(s1);
				uniform float4 _Speed;

				float4 frag (v2f i) : COLOR
				{
					float2 noiseUV   = i.uv;

					noiseUV.x      += _Time * _Speed.z;
					noiseUV.y      -= _Time * _Speed.w;

					half4 noiseTex   = tex2D( _NoiseTex, noiseUV );
					noiseTex.xy      = (noiseTex.xy * 2.0 - 1.0) * 0.035;

					return tex2D(_MainTex, i.uv + noiseTex.xy + _Time * _Speed.xy) * _PPLAmbient * 2.0;
				}
				ENDCG
			}
 
			// Pixel lights
			Pass
			{
				Name "PPL"
				Tags { "LightMode" = "Pixel" }
				
				CGPROGRAM
				#pragma target 3.0
				#pragma profileoption MaxTexIndirections=1024
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_builtin
				#pragma fragmentoption ARB_fog_exp2
				#pragma fragmentoption ARB_precision_hint_fastest
				#include "UnityCG.cginc"
				#include "AutoLight.cginc"


				struct v2f
				{
					V2F_POS_FOG;
					LIGHTING_COORDS
					float2	uv;
					float3	normal;
					float3	lightDir;
				};

				uniform float4 _MainTex_ST;

				v2f vert (appdata_base v)
				{
					v2f o;
					PositionFog( v.vertex, o.pos, o.fog );
					o.normal = v.normal;
					o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.lightDir = ObjSpaceLightDir( v.vertex );
					TRANSFER_VERTEX_TO_FRAGMENT(o);
					return o;
				}


				uniform sampler2D _MainTex;
				uniform sampler2D _NoiseTex;
				uniform float4 _Speed;

				float4 frag (v2f i) : COLOR
				{
					// The eternal tradeoff: do we normalize the normal?
					//float3 normal = normalize(i.normal);
					float3 normal = i.normal;
						
					float2 noiseUV	= i.uv;
					
					noiseUV.x		+= _Time * _Speed.z;
					noiseUV.y		-= _Time * _Speed.w;
						
					half4 noiseTex	= tex2D( _NoiseTex, noiseUV );
					noiseTex.xy		= (noiseTex.xy * 2.0 - 1.0) * 0.035;
					
					half4 texcol	= tex2D(_MainTex, i.uv + noiseTex.xy + _Time * _Speed.xy) * _PPLAmbient * 2.0;
					
					return DiffuseLight( i.lightDir, normal, texcol, LIGHT_ATTENUATION(i) );
				}
				ENDCG
			}
		}
	}

	Fallback "VertexLit", 2

}

I’ve got a shader I wrote with nine texture samples from the same texture, so I think the only reason for SM3 would be because your second read is dependent on the results of your first. I thought that was possible in SM2, but perhaps I’m wrong.

Even in ATIs broken world of shader standards (they ignore what they don’t like), up to 4 chained texture lookups should work.

Chances are that it does not work in your case due to the cg generated dx shader that goes overboard with the texture registers (using new temp registers instead of reusing the old one, really going up to 9 textures instead of remaining on 2) which would break the SM2 part

So what’s the progress on this one, Really interested in this exact style myself.