Animate water using Flow Maps

Discussion in 'Shaders' started by Alhaziel, Jul 22, 2011.

  1. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    Hey guys,

    I wrote a shader that should animate water using a flow map and a noise map, everything is based on the shader developed by Valve for LF4 and Graphics Runner DirectX Sample

    The shader itself is really simple, but for some reason a cannot animate the water. I would really appreciate any help, in fact would be cool having a water animated using a flow map in unity free version.

    this is the shader:

    Code (csharp):
    1.  
    2.  
    3. Shader "WaterTestShader1"
    4. {
    5.     Properties
    6.     {
    7.         _Color ("Main Color", Color) = (1,1,1,0.5)
    8.         _MainTex ("Base (RGB)", 2D) = "white" {}
    9.         _MainTex2 ("Base (RGB)", 2D) = "white" {}
    10.         _FlowMap   ("FlowMap  (RGB)", 2D) = "white" {}
    11.         _NoiseMap  ("NoiseMap (RGB)", 2D) = "white" {}
    12.        
    13.         _WaveScale ("Wave scale", Range (0.02,0.15)) = .07
    14.         _WaveSpeed ("Wave speed (map1 x,y; map2 x,y)", Vector) = (19,9,-16,-7)
    15.     }
    16.     SubShader
    17.     {
    18.    
    19.     Pass
    20.     {
    21.  
    22.     CGPROGRAM
    23.     #pragma vertex vert
    24.     #pragma fragment frag
    25.     #include "UnityCG.cginc"
    26.  
    27.     float4 _Color;
    28.     sampler2D _MainTex, _MainTex2, _FlowMap, _NoiseMap;
    29.     float4 _MainTex_ST;
    30.     float4 _MainTex2_ST;
    31.    
    32.     float3 _WaveSpeed;
    33.     float  _WaveScale;
    34.    
    35.     //Flow map offsets used to scroll the wave maps
    36.     float   flowMapOffset0;
    37.     float   flowMapOffset1;
    38.  
    39.     //scale used on the wave maps
    40.     float waveScale = 1.0f;
    41.     float halfCycle;
    42.  
    43. struct v2f
    44. {
    45.     float4  pos  : SV_POSITION;
    46.     float2  uv0  : TEXCOORD0;
    47.     float2  uv1  : TEXCOORD1;
    48.     float3  col  : COLOR0;
    49. };
    50.  
    51. v2f vert (appdata_base v)
    52. {
    53.     v2f o;
    54.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    55.     o.uv0 = TRANSFORM_TEX (v.texcoord, _MainTex);
    56.     o.uv1 = TRANSFORM_TEX (v.texcoord, _MainTex2);
    57.     o.col = v.normal * 0.5 + 0.5; //shift the colour in the range [0, 1]
    58.     return o;
    59. }
    60.  
    61. half4 frag (v2f i) : COLOR
    62. {
    63.     //get and uncompress the flow vector for this pixel
    64.     float2 flowmap = tex2D( _FlowMap, i.uv0 ).rg * 2.0f - 1.0f;
    65.     float cycleOffset = tex2D( _NoiseMap, i.uv0 ).r;
    66.    
    67.     float phase0 = cycleOffset * .5f + flowMapOffset0;
    68.     float phase1 = cycleOffset * .5f + flowMapOffset1;
    69.    
    70.     // Sample normal map.
    71.     float3 normalT0 = tex2D(_MainTex, ( i.uv0 * waveScale ) + flowmap * phase0 ).rgb;
    72.     float3 normalT1 = tex2D(_MainTex2, ( i.uv0 * waveScale ) + flowmap * phase1 ).rgb;
    73.    
    74.     float f = ( abs( halfCycle - flowMapOffset0 ) / halfCycle );
    75.    
    76.     float3 normalT = lerp( normalT0, normalT1, f );
    77.     return float4( normalT, 1.0f );
    78.  
    79. }
    80. ENDCG
    81.  
    82.     }
    83. }
    84. Fallback "VertexLit"
    85. }
    86.  
    87.  
    and this is the script I use to update overtime the scrolling textures:

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4.  
    5. [ExecuteInEditMode]
    6.  
    7.  
    8. public class UVScroller : MonoBehaviour
    9. {
    10.    
    11.    float m_fFlowMapOffset0 = 0.0f;
    12.    float m_fFlowMapOffset1 = 0.0f;
    13.    float m_fFlowSpeed = 0.05f;
    14.    float m_fCycle = 0.15f;
    15.    float m_fWaveMapScale = 2.0f;
    16.    
    17.    public void Update ()
    18.    {
    19.        //update the flow map offsets for both layers
    20.        m_fFlowMapOffset0 += m_fFlowSpeed * Time.deltaTime;
    21.        m_fFlowMapOffset1 += m_fFlowSpeed * Time.deltaTime;
    22.      
    23.        if ( m_fFlowMapOffset0 >= m_fCycle )
    24.             m_fFlowMapOffset0 = 0.0f;
    25.  
    26.        if ( m_fFlowMapOffset1 >= m_fCycle )
    27.             m_fFlowMapOffset1 = 0.0f;
    28.          
    29.        float _fHalfCycle = m_fCycle*0.5f;
    30.      
    31.        Shader.SetGlobalFloat("fFlowMapOffset0", m_fFlowMapOffset0);
    32.        Shader.SetGlobalFloat("fFlowMapOffset1", m_fFlowMapOffset1);
    33.        Shader.SetGlobalFloat("halfCycle", _fHalfCycle);
    34.        Shader.SetGlobalFloat("fWaveSpeed", m_fFlowSpeed);
    35.        Shader.SetGlobalFloat("fWaveScale", m_fWaveMapScale);
    36.   }
    37. }
    38.  
    39.  
    At the moment I am not considering the light I just want to make the flow working.

    The flow is scrolling,but there is a problem with normals, apparently I do not unpack normals properly and when the texture reached the endCycle is resetting itself

    What I want to do is scrolling normal maps and not the actual texture, this means unpack normals and?

    Thank you very much in advance

    Finally a pic as reference:
     

    Attached Files:

    Last edited: Jul 25, 2011
  2. Farfarer

    Farfarer

    Member

    Joined:
    Aug 17, 2010
    Messages:
    2,174
    For normal maps you want to use the function UnpackNormals() with your normal map texture sampler as an argument.
    e.g.
    Code (csharp):
    1.  
    2. float3 normalT0 = UnpackNormals(tex2D(_MainTex, ( i.uv0 * waveScale ) + flowmap * phase0 ));
    3.  
     
  3. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    yeah I know I fixed that, but I think I have still some problems, the water looks redish because of the colours of the textures, but usually normals look purple or green-ish... I do not know if this is due to the fact i do not have lighting calculations...

    Finally, the main problem is that once the scrolling texture reached the end of his cycle, it jumps back to the start and the visual effect is really ugly to watch.
     
  4. Farfarer

    Farfarer

    Member

    Joined:
    Aug 17, 2010
    Messages:
    2,174
    I kinda think it'd be worthwhile using the _Time variable in the shader itself to control the animation. Rather than scripting it all then passing variables in.

    But in...
    Code (csharp):
    1.  
    2. if ( m_fFlowMapOffset1 >= m_fCycle )
    3.             m_fFlowMapOffset1 = 0.0f;
    4.  
    m_fCycle is 0.15

    So the offset is hitting 15% of texture offset then jumping back to 0. That's not going to look graceful - it's going to keep snapping back.

    Perhaps use
    Code (csharp):
    1.  
    2. m_fFlowMapOffset1 = Mathf.Repeat(m_fFlowMapOffset1 + m_fFlowSpeed * Time.deltaTime, 1);
    3.  
    which will keep it going and wrap it back round to 0 once it's passed 1. And if you've put it, say 0.1 over 1.0, it'll wrap back round to 0.1. Keeps it running smoothly.
     
  5. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    First of all thanks very much for your help man.

    I have put that line of code, and yes it is working much better without jumping back to the start, but still there is something i do not understand completely.

    The flow seems working fine, it is cool, but first of all is redish, and after it started flowing it changes coloulors, but when the texture is scrolled completely the flow stops for a second a starts again... the effect is not bas as was before, but still not perfect.

    Finally I really would like to know why the normal maps look red ish, here is a screenshot:
     

    Attached Files:

  6. Farfarer

    Farfarer

    Member

    Joined:
    Aug 17, 2010
    Messages:
    2,174
    For a start, you've got two returns going on in your shader, which isn't right.

    You want to remove the last one - the i.col one - just to be sure it's not screwing things up.

    Secondly... are your normal maps tagged as normal maps in the inspector? If they're not, the UnpackNormals() function's going to screw up the colouring as it relies on them being DXT5nm compressed (which is what tagging them as normal maps in the inspector does).
     
  7. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    yea i have fixed the two returns, before opening the thread, i just didnt notice i posted the old code, but the second return should not be called anyway, sry.

    I just changed the two normal maps, they are set as normal maps this is the inspector window for them:

    The flow now looks much better (the textures are not even squared..btw but i do not think it s a problem right now), but still it is blue-ish now. It like is getting the ambient colour from this textures..now I have seen normal map rendered and I know how they should look like, imho there is something wrong.
     

    Attached Files:

    Last edited: Jul 22, 2011
  8. Farfarer

    Farfarer

    Member

    Joined:
    Aug 17, 2010
    Messages:
    2,174
    Untick generate from height. You're passing in proper normal maps, not height maps that need converting.
     
  9. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    ok man thx very much again!

    I think I have pretty much done the shader with your help, but just one problem left.
    I think I commit some mistakes when I try to animate the flow, because the areas that should be around objects in the final scene, are not moving at all. But they should move as you can see in the GraphicsRunner web site!

    here is the final polished shader code:

    Code (csharp):
    1.  
    2.  
    3. Shader "WaterTestShader1"
    4. {
    5.     Properties
    6.     {
    7.         //color
    8.         _Color ("Main Color", Color) = (1,1,1,0.5)
    9.        
    10.         //main water texture
    11.         _AmbientTex ("Water Base Texture", 2D) = "white" {}
    12.        
    13.         //first main normal map to scroll
    14.         _NrmTex1 ("First scrolling normal map (RGB)", 2D) = "bump" {}
    15.        
    16.         //second main normal map to scroll
    17.         _NrmTex2 ("Second scrolling normal map", 2D) = "bump" {}
    18.        
    19.         //flowmap
    20.         _FlowMap   ("FlowMap  (RGB)", 2D) = "white" {}
    21.        
    22.         //noise map
    23.          _NoiseMap  ("NoiseMap (RGB)", 2D) = "white" {}
    24.        
    25.         //colour textures overlay
    26.         _ColorTextureOverlay ("_ColorTextureOverlay", Range (0.0, 1.0)) = 0.75
    27.        
    28.         //fresnel power for reflections
    29.         _FresnelPower ("_FresnelPower", Range (0.1, 10.0)) = 2.0
    30.        
    31.         //ambient power light
    32.         _Ambient ("_Ambient", Range (0.0, 1.0)) = 0.8
    33.        
    34.         //world light direction
    35.         _WorldLightDir("_WorldLightDir", Vector) = (0,0,0,1)
    36.        
    37.         //specular shiness
    38.         _Shininess ("_Shininess", Range (0.1, 60.0)) = 1.0
    39.        
    40.         //spec colours
    41.         _SpecColor ("Spec Color", Color) = (0.5,0.5,0.5,0.5)
    42.        
    43.         _WaveScale ("Wave scale", Range (0.02,0.15)) = .07
    44.        
    45.        // _WaveSpeed ("Wave speed (map1 x,y; map2 x,y)", Vector) = (19,9,-16,-7)
    46.     }
    47.    
    48.     Category
    49.     {
    50.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    51.         Blend SrcAlpha OneMinusSrcAlpha
    52.         Cull Off
    53.         //ColorMask RGB
    54.         //Lighting off ZWrite Off
    55.        
    56.     SubShader
    57.     {
    58.    
    59.     Pass
    60.     {
    61.  
    62.     CGPROGRAM
    63.     #pragma target 3.0
    64.     #pragma vertex vert
    65.     #pragma fragment frag
    66.     #include "UnityCG.cginc"
    67.  
    68.     //main colour
    69.     float4 _Color;
    70.    
    71.     //texsampler
    72.     sampler2D _NrmTex1, _NrmTex2, _FlowMap, _NoiseMap, _AmbientTex;
    73.    
    74.     //texture's st
    75.     float4 _NrmTex1_ST;
    76.     float4 _NrmTex2_ST;
    77.     float4 _AmbientTex_ST;
    78.     float4 _FlowMap_ST;
    79.    
    80.     //wave speed
    81.     float3 fWaveSpeed;
    82.    
    83.     //wave scale
    84.     float  _WaveScale;
    85.    
    86.     //Flow map offsets used to scroll the wave maps
    87.     float   flowMapOffset0;
    88.     float   flowMapOffset1;
    89.  
    90.     //scale used on the wave maps
    91.     float fWaveScale = 1.0f;
    92.    
    93.     //the half cycle needed to blend the 2 normal maps when one reaches the top the other's contribuition should be 0
    94.     float halfCycle;
    95.    
    96.     //color texture overlay
    97.     float _ColorTextureOverlay;
    98.    
    99.     //reflection power
    100.     float _FresnelPower;
    101.    
    102.     //ambient cols
    103.     float _Ambient;
    104.    
    105.     //spec shiness
    106.     float _Shininess;
    107.    
    108.     //spec colour
    109.     float4 _SpecColor;
    110.    
    111.     //world light direction used to calculate the fresnel law
    112.     float4 _WorldLightDir;
    113.  
    114. //vertex structure from vertex shader to pixel
    115. struct v2f
    116. {
    117.     float4  pos         : SV_POSITION;
    118.     float2  uv0         : TEXCOORD0;
    119.     float2  uv1         : TEXCOORD1;
    120.     float3 viewDirWorld : TEXCOORD2;
    121.     float3 TtoW0        : TEXCOORD3;
    122.     float3 TtoW1        : TEXCOORD4;
    123.     float3 TtoW2        : TEXCOORD5;
    124.     float3  col         : COLOR0;
    125.     float2 uv2          : TEXCOORD6;
    126. };
    127.  
    128. v2f vert (appdata_full v)
    129. {
    130.     //declaration of the evrtex structure
    131.     v2f o;
    132.    
    133.     //determine the position in world space applying the model view projection transform
    134.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    135.    
    136.     //unpack texture coords for the scrolling normal maps (they are the same so there is no need to waste memory for another float2 texcoord variable)
    137.     o.uv0 = TRANSFORM_TEX (v.texcoord, _NrmTex1);
    138.    
    139.     //unpack texture coord for the flow map
    140.     o.uv1 = TRANSFORM_TEX (v.texcoord, _FlowMap);
    141.    
    142.     //unpack tex coords for ambient water base texture
    143.     o.uv2 = TRANSFORM_TEX (v.texcoord, _AmbientTex);
    144.    
    145.     //reverse view dir                                     
    146.     o.viewDirWorld = -WorldSpaceViewDir(v.vertex);
    147.        
    148.     //calculates the tangent normals for the fresnel law                                                   
    149.     TANGENT_SPACE_ROTATION;
    150.     o.TtoW0 = mul(rotation, _Object2World[0].xyz * unity_Scale.w);
    151.     o.TtoW1 = mul(rotation, _Object2World[1].xyz * unity_Scale.w);
    152.     o.TtoW2 = mul(rotation, _Object2World[2].xyz * unity_Scale.w);     
    153.      
    154.     //returns the output structure
    155.     return o;
    156. }
    157.  
    158. half4 frag (v2f i) : COLOR
    159. {
    160.     //get and uncompress the flow vector for this pixel
    161.     float2 flowmap = tex2D( _FlowMap, i.uv1 ).rg * 2.0f - 1.0f;
    162.    
    163.     //determines the noise clycle offset from the noise mask
    164.     float cycleOffset = tex2D( _NoiseMap, i.uv1 ).r;
    165.    
    166.     //determines the phase0
    167.     float phase0 = cycleOffset * .5f + flowMapOffset0;
    168.    
    169.     //determines the phase1
    170.     float phase1 = cycleOffset * .5f + flowMapOffset1;
    171.    
    172.     // Sample normal map1
    173.     float3 normalT0 = UnpackNormal(tex2D(_NrmTex1, ( i.uv0 * fWaveScale ) + flowmap * phase0 ));
    174.    
    175.     //Sample normal map2
    176.     float3 normalT1 = UnpackNormal(tex2D(_NrmTex2, ( i.uv0 * fWaveScale ) + flowmap * phase1 ));
    177.    
    178.     //determines the flow function
    179.     float f = ( abs( halfCycle - flowMapOffset0 ) / halfCycle );
    180.    
    181.     //unroll the normals retrieved from the normalmaps
    182.     normalT0.yz = normalT0.zy
    183.     normalT1.yz = normalT1.zy;
    184.    
    185.     normalT0 = 2.0f * normalT0 - 1.0f;
    186.     normalT1 = 2.0f * normalT1 - 1.0f;
    187.    
    188.     //determins the resulting normal
    189.     float3 normalT = lerp( normalT0, normalT1, f );
    190.    
    191.     // declare world normal
    192.     half3 worldNormal;
    193.  
    194.     //calculate the world normals
    195.     worldNormal.x = dot(i.TtoW0, normalT.xyz);
    196.     worldNormal.y = dot(i.TtoW1, normalT.xyz);
    197.     worldNormal.z = dot(i.TtoW2, normalT.xyz);     
    198.                    
    199.     // normalize
    200.     worldNormal = normalize(worldNormal);
    201.     i.viewDirWorld = normalize(i.viewDirWorld);
    202.    
    203.     // color
    204.     float4 color = tex2D(_AmbientTex, i.uv2);
    205.     color = lerp(half4(0.6, 0.6, 0.6, 0.6), color, _ColorTextureOverlay);  
    206.    
    207.     // REFLECTION
    208.     float3 reflectVector = normalize(reflect(i.viewDirWorld, worldNormal));
    209.     half4 reflColor = 0.75;
    210.    
    211.     // FRESNEL CALCS
    212.     float fcbias = 0.20373;
    213.     float facing = saturate(1.0 - max(dot(-i.viewDirWorld, worldNormal), 0.0));
    214.     float refl2Refr = max(fcbias + (1.0 - fcbias) * pow(facing, _FresnelPower), 0);
    215.    
    216.     color.rgba *= (lerp(half4(0.6,0.6,0.6, 0.6), half4(reflColor.rgb,1.0), refl2Refr));
    217.                      
    218.     // light
    219.     color.rgb = color.rgb * max(_Ambient, saturate(dot(_WorldLightDir.xyz, worldNormal)));
    220.    
    221.     // a little more spec in low quality to have at least something going on
    222.     color.rgb += _SpecColor.rgb * 2.0 *  pow(saturate(dot(_WorldLightDir.xyz, reflectVector)), _Shininess);
    223.    
    224.     //returns the light color computer modulated by the diffuse color      
    225.     return _Color * color;
    226.            
    227. }
    228. ENDCG
    229.  
    230. }
    231.  
    232.  
    233.     }
    234. }
    235. Fallback ""//"VertexLit"
    236. }
    237.  
    238.  
    and here is a pic as reference:
     

    Attached Files:

    Last edited: Jul 22, 2011
  10. Alhaziel

    Alhaziel

    New Member

    Joined:
    Jun 15, 2011
    Messages:
    16
    edit double post sry
     
    Last edited: Jul 22, 2011
  11. sadaszewski

    sadaszewski

    New Member

    Joined:
    Mar 22, 2012
    Messages:
    2
  12. LongMozart

    LongMozart

    New Member

    Joined:
    Jul 5, 2011
    Messages:
    26
    Hey I have just checked your page. Look really cool. Many thanks for the flow map editor, really handy.
     
  13. sadaszewski

    sadaszewski

    New Member

    Joined:
    Mar 22, 2012
    Messages:
    2
    I’ve introduced some useful changes:
    1) Fast preview/export using triangulated irregular network linear interpolation. This looks a bit worse than Natural Neighbor method but it’s a lot faster.
    2) Thanks to using OpenMP for Natural Neighbor high quality export, it should be approximately N times faster (where N is the number of CPU cores you have)
    3) When loading image, flowed automatically looks for corresponding .ff file and tries to load it
    4) Antialiasing of the drawing scene
    5) Added Repeat Last Export button
    6) Fixed bug in Natural Neighbor which used to cause artifacts near right border of the flow map

    http://algoholic.eu/flow-field-editor-update/
     
    Last edited: Mar 26, 2012