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): Shader "WaterTestShader1" { Properties { _Color ("Main Color", Color) = (1,1,1,0.5) _MainTex ("Base (RGB)", 2D) = "white" {} _MainTex2 ("Base (RGB)", 2D) = "white" {} _FlowMap ("FlowMap (RGB)", 2D) = "white" {} _NoiseMap ("NoiseMap (RGB)", 2D) = "white" {} _WaveScale ("Wave scale", Range (0.02,0.15)) = .07 _WaveSpeed ("Wave speed (map1 x,y; map2 x,y)", Vector) = (19,9,-16,-7) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float4 _Color; sampler2D _MainTex, _MainTex2, _FlowMap, _NoiseMap; float4 _MainTex_ST; float4 _MainTex2_ST; float3 _WaveSpeed; float _WaveScale; //Flow map offsets used to scroll the wave maps float flowMapOffset0; float flowMapOffset1; //scale used on the wave maps float waveScale = 1.0f; float halfCycle; struct v2f { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float2 uv1 : TEXCOORD1; float3 col : COLOR0; }; v2f vert (appdata_base v) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv0 = TRANSFORM_TEX (v.texcoord, _MainTex); o.uv1 = TRANSFORM_TEX (v.texcoord, _MainTex2); o.col = v.normal * 0.5 + 0.5; //shift the colour in the range [0, 1] return o; } half4 frag (v2f i) : COLOR { //get and uncompress the flow vector for this pixel float2 flowmap = tex2D( _FlowMap, i.uv0 ).rg * 2.0f - 1.0f; float cycleOffset = tex2D( _NoiseMap, i.uv0 ).r; float phase0 = cycleOffset * .5f + flowMapOffset0; float phase1 = cycleOffset * .5f + flowMapOffset1; // Sample normal map. float3 normalT0 = tex2D(_MainTex, ( i.uv0 * waveScale ) + flowmap * phase0 ).rgb; float3 normalT1 = tex2D(_MainTex2, ( i.uv0 * waveScale ) + flowmap * phase1 ).rgb; float f = ( abs( halfCycle - flowMapOffset0 ) / halfCycle ); float3 normalT = lerp( normalT0, normalT1, f ); return float4( normalT, 1.0f ); } ENDCG } } Fallback "VertexLit" } and this is the script I use to update overtime the scrolling textures: Code (csharp): using UnityEngine; [ExecuteInEditMode] public class UVScroller : MonoBehaviour { float m_fFlowMapOffset0 = 0.0f; float m_fFlowMapOffset1 = 0.0f; float m_fFlowSpeed = 0.05f; float m_fCycle = 0.15f; float m_fWaveMapScale = 2.0f; public void Update () { //update the flow map offsets for both layers m_fFlowMapOffset0 += m_fFlowSpeed * Time.deltaTime; m_fFlowMapOffset1 += m_fFlowSpeed * Time.deltaTime; if ( m_fFlowMapOffset0 >= m_fCycle ) m_fFlowMapOffset0 = 0.0f; if ( m_fFlowMapOffset1 >= m_fCycle ) m_fFlowMapOffset1 = 0.0f; float _fHalfCycle = m_fCycle*0.5f; Shader.SetGlobalFloat("fFlowMapOffset0", m_fFlowMapOffset0); Shader.SetGlobalFloat("fFlowMapOffset1", m_fFlowMapOffset1); Shader.SetGlobalFloat("halfCycle", _fHalfCycle); Shader.SetGlobalFloat("fWaveSpeed", m_fFlowSpeed); Shader.SetGlobalFloat("fWaveScale", m_fWaveMapScale); } } 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:
For normal maps you want to use the function UnpackNormals() with your normal map texture sampler as an argument. e.g. Code (csharp): float3 normalT0 = UnpackNormals(tex2D(_MainTex, ( i.uv0 * waveScale ) + flowmap * phase0 ));
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.
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): if ( m_fFlowMapOffset1 >= m_fCycle ) m_fFlowMapOffset1 = 0.0f; 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): m_fFlowMapOffset1 = Mathf.Repeat(m_fFlowMapOffset1 + m_fFlowSpeed * Time.deltaTime, 1); 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.
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:
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).
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.
Untick generate from height. You're passing in proper normal maps, not height maps that need converting.
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): Shader "WaterTestShader1" { Properties { //color _Color ("Main Color", Color) = (1,1,1,0.5) //main water texture _AmbientTex ("Water Base Texture", 2D) = "white" {} //first main normal map to scroll _NrmTex1 ("First scrolling normal map (RGB)", 2D) = "bump" {} //second main normal map to scroll _NrmTex2 ("Second scrolling normal map", 2D) = "bump" {} //flowmap _FlowMap ("FlowMap (RGB)", 2D) = "white" {} //noise map _NoiseMap ("NoiseMap (RGB)", 2D) = "white" {} //colour textures overlay _ColorTextureOverlay ("_ColorTextureOverlay", Range (0.0, 1.0)) = 0.75 //fresnel power for reflections _FresnelPower ("_FresnelPower", Range (0.1, 10.0)) = 2.0 //ambient power light _Ambient ("_Ambient", Range (0.0, 1.0)) = 0.8 //world light direction _WorldLightDir("_WorldLightDir", Vector) = (0,0,0,1) //specular shiness _Shininess ("_Shininess", Range (0.1, 60.0)) = 1.0 //spec colours _SpecColor ("Spec Color", Color) = (0.5,0.5,0.5,0.5) _WaveScale ("Wave scale", Range (0.02,0.15)) = .07 // _WaveSpeed ("Wave speed (map1 x,y; map2 x,y)", Vector) = (19,9,-16,-7) } Category { Tags { "Queue"="Transparent" "RenderType"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha Cull Off //ColorMask RGB //Lighting off ZWrite Off SubShader { Pass { CGPROGRAM #pragma target 3.0 #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //main colour float4 _Color; //texsampler sampler2D _NrmTex1, _NrmTex2, _FlowMap, _NoiseMap, _AmbientTex; //texture's st float4 _NrmTex1_ST; float4 _NrmTex2_ST; float4 _AmbientTex_ST; float4 _FlowMap_ST; //wave speed float3 fWaveSpeed; //wave scale float _WaveScale; //Flow map offsets used to scroll the wave maps float flowMapOffset0; float flowMapOffset1; //scale used on the wave maps float fWaveScale = 1.0f; //the half cycle needed to blend the 2 normal maps when one reaches the top the other's contribuition should be 0 float halfCycle; //color texture overlay float _ColorTextureOverlay; //reflection power float _FresnelPower; //ambient cols float _Ambient; //spec shiness float _Shininess; //spec colour float4 _SpecColor; //world light direction used to calculate the fresnel law float4 _WorldLightDir; //vertex structure from vertex shader to pixel struct v2f { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float2 uv1 : TEXCOORD1; float3 viewDirWorld : TEXCOORD2; float3 TtoW0 : TEXCOORD3; float3 TtoW1 : TEXCOORD4; float3 TtoW2 : TEXCOORD5; float3 col : COLOR0; float2 uv2 : TEXCOORD6; }; v2f vert (appdata_full v) { //declaration of the evrtex structure v2f o; //determine the position in world space applying the model view projection transform o.pos = mul (UNITY_MATRIX_MVP, v.vertex); //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) o.uv0 = TRANSFORM_TEX (v.texcoord, _NrmTex1); //unpack texture coord for the flow map o.uv1 = TRANSFORM_TEX (v.texcoord, _FlowMap); //unpack tex coords for ambient water base texture o.uv2 = TRANSFORM_TEX (v.texcoord, _AmbientTex); //reverse view dir o.viewDirWorld = -WorldSpaceViewDir(v.vertex); //calculates the tangent normals for the fresnel law TANGENT_SPACE_ROTATION; o.TtoW0 = mul(rotation, _Object2World[0].xyz * unity_Scale.w); o.TtoW1 = mul(rotation, _Object2World[1].xyz * unity_Scale.w); o.TtoW2 = mul(rotation, _Object2World[2].xyz * unity_Scale.w); //returns the output structure return o; } half4 frag (v2f i) : COLOR { //get and uncompress the flow vector for this pixel float2 flowmap = tex2D( _FlowMap, i.uv1 ).rg * 2.0f - 1.0f; //determines the noise clycle offset from the noise mask float cycleOffset = tex2D( _NoiseMap, i.uv1 ).r; //determines the phase0 float phase0 = cycleOffset * .5f + flowMapOffset0; //determines the phase1 float phase1 = cycleOffset * .5f + flowMapOffset1; // Sample normal map1 float3 normalT0 = UnpackNormal(tex2D(_NrmTex1, ( i.uv0 * fWaveScale ) + flowmap * phase0 )); //Sample normal map2 float3 normalT1 = UnpackNormal(tex2D(_NrmTex2, ( i.uv0 * fWaveScale ) + flowmap * phase1 )); //determines the flow function float f = ( abs( halfCycle - flowMapOffset0 ) / halfCycle ); //unroll the normals retrieved from the normalmaps normalT0.yz = normalT0.zy; normalT1.yz = normalT1.zy; normalT0 = 2.0f * normalT0 - 1.0f; normalT1 = 2.0f * normalT1 - 1.0f; //determins the resulting normal float3 normalT = lerp( normalT0, normalT1, f ); // declare world normal half3 worldNormal; //calculate the world normals worldNormal.x = dot(i.TtoW0, normalT.xyz); worldNormal.y = dot(i.TtoW1, normalT.xyz); worldNormal.z = dot(i.TtoW2, normalT.xyz); // normalize worldNormal = normalize(worldNormal); i.viewDirWorld = normalize(i.viewDirWorld); // color float4 color = tex2D(_AmbientTex, i.uv2); color = lerp(half4(0.6, 0.6, 0.6, 0.6), color, _ColorTextureOverlay); // REFLECTION float3 reflectVector = normalize(reflect(i.viewDirWorld, worldNormal)); half4 reflColor = 0.75; // FRESNEL CALCS float fcbias = 0.20373; float facing = saturate(1.0 - max(dot(-i.viewDirWorld, worldNormal), 0.0)); float refl2Refr = max(fcbias + (1.0 - fcbias) * pow(facing, _FresnelPower), 0); color.rgba *= (lerp(half4(0.6,0.6,0.6, 0.6), half4(reflColor.rgb,1.0), refl2Refr)); // light color.rgb = color.rgb * max(_Ambient, saturate(dot(_WorldLightDir.xyz, worldNormal))); // a little more spec in low quality to have at least something going on color.rgb += _SpecColor.rgb * 2.0 * pow(saturate(dot(_WorldLightDir.xyz, reflectVector)), _Shininess); //returns the light color computer modulated by the diffuse color return _Color * color; } ENDCG } } } Fallback ""//"VertexLit" } and here is a pic as reference:
I think you might be interested, I've created a flow editor - http://algoholic.eu/flow-shader-fix-flow-editor/ Cheers.
Hey I have just checked your page. Look really cool. Many thanks for the flow map editor, really handy.
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/