Advertisement
ensiferum888

TiledFlowShader

Feb 23rd, 2014
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.69 KB | None | 0 0
  1.  
  2. //
  3. // Fragment shader, Tiled Directional Flow
  4. //
  5. // (c) 2010 frans van hoesel, university of groningen
  6. //
  7. //
  8. // this shader creates animated water by transforming normalmaps
  9. // the scaling and rotation of the normalmaps is done per tile
  10. // and is constant per tile. Each tile can have its own parameters
  11. // for rotation, scaling, and speed of translation
  12. // To hide the seams between the tiles, all seams have another tile
  13. // centered over the seam. The opacity of the tiles decreases towards the
  14. // edge of the tiles, so the edge isn't visible at all.
  15. // Basically, all points have four tiles (A,B,C and D), mixed together
  16. // (although at the edges the contribution of a tile to this mix is
  17. // reduced to zero).
  18. // The mixing of the tiles each with different parameters gives a nice
  19. // animated look to the water. It is no longer just sliding in one direction, but
  20. // appears to move more like real water.
  21.  
  22. // The resulting sum of normalmaps, is used to calculate the refraction of the clouds
  23. // in a cube map and can also be used for other nice effects. In this example the
  24. // colormap of the material under water is distorted to fake some kind of refraction
  25. // (for this example the water is a bit too transparent, but it shows this refraction
  26. // better)
  27.  
  28. // A flowmap determines in the red and green channel the normalized direction of the
  29. // flow and in the blue channel wavelength.
  30. // The alpha channel is used for the transparency of the water. Near the edge, the
  31. // water becomes less deep and more transparent. Also near the edge, the waves tend
  32. // to be smaller, so the same alpha channel also scales the height of the waves.
  33. // Currently the wavelength is in its own channel (blue), but could be premultiplied
  34. // to the red and green channels. This makes this channel available for changing the
  35. // speed of the waves per tile.
  36.  
  37.  
  38. // Further improvements
  39. // Besides the obvious improvements mentioned in the code (such as premultiplying
  40. // the direction of the waves with the scale, or moving the texscale multiplication
  41. // to the texture coordinates), one could get rid of tiling in this code and pass it
  42. // tiled geometry. This way the whole lookup of the flowmap (which is constant over
  43. // each tile) could be moved to the vertexshader, removing the the construction of
  44. // the flow rotation matrix. As this is done 4 times per pixel, it might give a big
  45. // performance boost (one does need to pass on 4 constant matrices to the fragment
  46. // shader, which will cost you a bit of performance).
  47. //
  48. //////////////////////////////////////////////////////////////////////////////////
  49. //                     This software is Creditware:
  50. //
  51. // you can do whatever you want with this shader except claiming rights
  52. // you may sell it, but you cannot prevent others from selling it, giving it away
  53. // or use it as they please.
  54. //
  55. // Having said that, it would be nice if you gave me some credit for it, when you
  56. // use it.
  57. //
  58. //                     Frans van Hoesel, (c) 2010
  59. //////////////////////////////////////////////////////////////////////////////////
  60.  
  61.  
  62. // movie at youtube: http://www.youtube.com/watch?v=TeSuNYvXAiA?hd=1 (in Germany this is blocked by youtube)
  63. // making of at http://www.youtube.com/watch?v=wdcvPegJ1lw&hd=1 (works even in Germany)
  64.  
  65. // Thanks to Bart Campman, Pjotr Svetachov and Martijn Kragtwijk for their help.
  66.  
  67. Shader "Custom/TiledDirectionalFlow"
  68. {
  69.     Properties
  70.     {
  71.         _MainTex ("Base (RGB)", 2D) = "white" {}
  72.         _FlowMap ("Flow", 2D) = "red" {}
  73.         _WaterNormalMap("Water normal", 2D) = "blue" {}
  74.         _SkyBox("SkyBox", CUBE) = "" {}
  75.         _FlowSpeed("Flow speed", float) = 1.0
  76.         _FlowTileScale("Flow tile scale", float) = 35.0
  77.         _NormalTileScale("Normal tile scale", float) = 10.0
  78.     }
  79.     SubShader
  80.     {
  81.         Tags { "RenderType"="Opaque" }
  82.         LOD 200
  83.        
  84.         CGPROGRAM
  85.         #pragma surface surf Lambert
  86.         #pragma target 3.0
  87.  
  88.         sampler2D _MainTex, _FlowMap, _WaterNormalMap;
  89.         samplerCUBE _SkyBox;
  90.         float _FlowSpeed, _FlowTileScale, _NormalTileScale;
  91.  
  92.         struct Input
  93.         {
  94.             float2 uv_MainTex;
  95.             float2 uv_FlowMap;
  96.             float2 uv_WaterNormalMap;
  97.             float3 viewDir;
  98.         };
  99.  
  100.         void surf (Input IN, inout SurfaceOutput o)
  101.         {
  102.        
  103.             // texScale determines the amount of tiles generated.
  104.             float texScale = _FlowTileScale;
  105.             // texScale2 determines the repeat of the water texture (the normalmap) itself
  106.             float texScale2 = _NormalTileScale;
  107.             float myangle;
  108.             float transp;
  109.             float3 myNormal;
  110.            
  111.             float2 mytexFlowCoord = IN.uv_FlowMap * texScale;
  112.             // ff is the factor that blends the tiles.
  113.             float2 ff =  abs(2.0*(frac(mytexFlowCoord)) - 1.0) -0.5;      
  114.             // take a third power, to make the area with more or less equal contribution
  115.             // of more tile bigger
  116.             ff = 0.5-4.0*ff*ff*ff;
  117.             // ffscale is a scaling factor that compensates for the effect that
  118.             // adding normal vectors together tends to get them closer to the average normal
  119.             // which is a visible effect. For more or less random waves, this factor
  120.             // compensates for it
  121.             float2 ffscale = sqrt(ff*ff + (1-ff)*(1-ff));
  122.             float2 Tcoord = IN.uv_WaterNormalMap  * texScale2;
  123.            
  124.             // offset makes the water move
  125.             float2 _offset = float2(_Time.x * _FlowSpeed,0);
  126.            
  127.             // I scale the texFlowCoord and floor the value to create the tiling
  128.             // This could have be replace by an extremely lo-res texture lookup
  129.             // using NEAREST pixel.
  130.             float3 flow = tex2D(_FlowMap, floor(mytexFlowCoord)/ texScale).rgb;
  131.            
  132.             // flowdir is supposed to go from -1 to 1 and the line below
  133.             // used to be sample.xy * 2.0 - 1.0, but saves a multiply by
  134.             // moving this factor two to the sample.b
  135.             float2 flowdir = flow.xy -0.5;    
  136.            
  137.             // sample.b is used for the inverse length of the wave
  138.             // could be premultiplied in sample.xy, but this is easier for editing flowtexture
  139.             flowdir *= flow.b;
  140.            
  141.             // build the rotation matrix that scales and rotates the complete tile
  142.             float2x2 rotmat = float2x2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
  143.            
  144.             // this is the normal for tile A
  145.             float2 NormalT0 = tex2D(_WaterNormalMap, mul(rotmat, Tcoord) - _offset).rg;
  146.            
  147.             // for the next tile (B) I shift by half the tile size in the x-direction
  148.             flow = tex2D( _FlowMap, floor((mytexFlowCoord + float2(0.5,0)))/ texScale ).rgb;
  149.            
  150.             flowdir = flow.b * (flow.xy - 0.5);
  151.             rotmat = float2x2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
  152.             // and the normal for tile B...
  153.             // multiply the offset by some number close to 1 to give it a different speed
  154.             // The result is that after blending the water starts to animate and look
  155.             // realistic, instead of just sliding in some direction.
  156.             // This is also why I took the third power of ff above, so the area where the
  157.             // water animates is as big as possible
  158.             // adding a small arbitrary constant isn't really needed, but helps to show
  159.             // a bit less tiling in the beginning of the program. After a few seconds, the
  160.             // tiling cannot be seen anymore so this constant could be removed.
  161.             // For the quick demo I leave them in. In a simulation that keeps running for
  162.             // some time, you could just as well remove these small constant offsets
  163.             float2 NormalT1 = tex2D(_WaterNormalMap, mul(rotmat, Tcoord) - _offset*1.06+0.62).rg ;
  164.            
  165.             // blend them together using the ff factor
  166.             // use ff.x because this tile is shifted in the x-direction
  167.             float2 NormalTAB = ff.x * NormalT0 + (1.0-ff.x) * NormalT1;
  168.            
  169.             // the scaling of NormalTab and NormalTCD is moved to a single scale of
  170.             // NormalT later in the program, which is mathematically identical to
  171.             // NormalTAB = (NormalTAB - 0.5) / ffscale.x + 0.5;
  172.            
  173.             // tile C is shifted in the y-direction
  174.             flow = tex2D( _FlowMap, floor((mytexFlowCoord + float2(0.0,0.5)))/ texScale ).rgb;
  175.            
  176.             flowdir = flow.b * (flow.xy - 0.5);
  177.             rotmat = float2x2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);      
  178.             NormalT0 = tex2D(_WaterNormalMap, mul(rotmat, Tcoord) - _offset*1.33+0.27).rg;
  179.            
  180.             // tile D is shifted in both x- and y-direction
  181.             flow = tex2D( _FlowMap, floor((mytexFlowCoord + float2(0.5,0.5)))/ texScale ).rgb;
  182.            
  183.             flowdir = flow.b * (flow.xy - 0.5);
  184.             rotmat = float2x2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
  185.             NormalT1 = tex2D(_WaterNormalMap, mul(rotmat, Tcoord) - _offset*1.24).rg ;
  186.        
  187.             float2 NormalTCD = ff.x * NormalT0 + (1.0-ff.x) * NormalT1;
  188.             // NormalTCD = (NormalTCD - 0.5) / ffscale.x + 0.5;
  189.            
  190.             // now blend the two values together
  191.             float2 NormalT = ff.y * NormalTAB + (1.0-ff.y) * NormalTCD;
  192.            
  193.             // this line below used to be here for scaling the result
  194.             //NormalT = (NormalT - 0.5) / ffscale.y + 0.5;
  195.            
  196.             // below the new, direct scaling of NormalT
  197.             NormalT = (NormalT - 0.5) / (ffscale.y * ffscale.x);
  198.             // scaling by 0.3 is arbritrary, and could be done by just
  199.             // changing the values in the normal map
  200.             // without this factor, the waves look very strong
  201.             NormalT *= 0.3;
  202.             // to make the water more transparent
  203.             transp = tex2D( _FlowMap, IN.uv_FlowMap ).a;
  204.             // and scale the normals with the transparency
  205.             NormalT *= transp*transp;
  206.            
  207.             // assume normal of plane is 0,0,1 and produce the normalized sum of adding NormalT to it
  208.             myNormal = float3(NormalT,sqrt(1.0-NormalT.x*NormalT.x - NormalT.y*NormalT.y));
  209.            
  210.             float3 reflectDir = reflect(IN.viewDir, myNormal);
  211.             float3 envColor = texCUBE(_SkyBox, reflectDir).rgb;
  212.  
  213.             // very ugly version of fresnel effect
  214.             // but it gives a nice transparent water, but not too transparent
  215.             myangle = dot(myNormal,normalize(IN.viewDir));
  216.             myangle = 0.95-0.6*myangle*myangle;
  217.            
  218.             // blend in the color of the plane below the water 
  219.            
  220.             // add in a little distortion of the colormap for the effect of a refracted
  221.             // view of the image below the surface.
  222.             // (this isn't really tested, just a last minute addition
  223.             // and perhaps should be coded differently
  224.            
  225.             // the correct way, would be to use the refract routine, use the alpha channel for depth of
  226.             // the water (and make the water disappear when depth = 0), add some watercolor to the colormap
  227.             // depending on the depth, and use the calculated refractdir and the depth to find the right
  228.             // pixel in the colormap.... who knows, something for the next version
  229.             float4 base = tex2D(_MainTex, IN.uv_MainTex + myNormal.xy/texScale2*0.03*transp);
  230.            
  231.             base = float4(lerp(base.rgb,envColor,myangle*transp),1.0 );
  232.            
  233.             // note that smaller waves appear to move slower than bigger waves
  234.             // one could use the tiles and give each tile a different speed if that
  235.             // is what you want
  236.  
  237.             o.Albedo = base.rgb;
  238.             o.Alpha = base.a;
  239.            
  240.            
  241.            
  242.            
  243.         }
  244.         ENDCG
  245.     }
  246.     FallBack "Diffuse"
  247. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement