Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

uv panning / scrolling on really simple shaders ?!

Discussion in 'Shaders' started by warby, Apr 4, 2011.

  1. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    i am trying to make some fallback shaders for some really advanced ones i made with strumpys shader editor !
    i modified some really simple unlit shaders from the unify wiki !

    the only thing i can NOT figure out is how add texture panning / scrolling to these guys ( imagine i want to make a waterfall or convey-belt )

    in the "pirates of new horizons" prototype we used that texture-offset script which is ridiculously expansive ! (according to the profiler 50-60% of our frame render time was wasted on only that script !)
    so naturally we deemed it too prohibitively expansive for us to use.


    strumpys shader editor has a uvpan node and i know how to set it up with the time node and everything but i cant find any documentation on how to do panning on these low end shaderlab guys:

    Code (csharp):
    1. Shader "ese_opaque_vertexlit"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("_MainTex", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "Queue"="Geometry" }
    10.         Pass
    11.         {
    12.         ColorMaterial AmbientAndDiffuse
    13.         Lighting On
    14.         SetTexture [_MainTex] { Combine texture * primary DOUBLE }
    15.         }
    16.     }
    17. Fallback "ese_opaque_unlit"
    18. }
    Code (csharp):
    1. Shader "ese_opaque_unlit"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("_MainTex", 2D) = "white" {}
    6.     }  
    7.     SubShader
    8.     {
    9.         Tags { "Queue"="Geometry" }
    10.         BindChannels { Bind "Color", color }
    11.         Pass
    12.         {
    13.         SetTexture [_MainTex] { Combine texture * primary }
    14.         }
    15.     }
    16. Fallback "VertexLit"
    17. }
    please tell me its possible at all to add uv panning to these without requiring shader model 2.0-3.0 ?! and of course how ?!
     
    Last edited: Apr 4, 2011
  2. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,631
    Well, you can do it with just scripting and the renderer.material.SetTextureOffset command. No need to do it from the shader.
     
  3. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    but thats exactly what i am trieng to avoid because of performance

    shader > script
     
    DebugLogError likes this.
  4. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,631
    Really? That seems awfully weird. Are you sure there wasn't anything wrong with the script itself?
     
  5. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    Code (csharp):
    1. using System.Collections;
    2. using UnityEngine;
    3.  
    4.  
    5. public class ScrollingTextures : MonoBehaviour
    6. {
    7.  
    8.     public float horizontalScrollSpeed = 0.25f;
    9.     public float verticalScrollSpeed = 0.25f;
    10.  
    11.     private bool scroll = true;
    12.  
    13.     public void FixedUpdate()
    14.     {
    15.         if (scroll)
    16.         {
    17.             float verticalOffset = Time.time * verticalScrollSpeed;
    18.             float horizontalOffset = Time.time * horizontalScrollSpeed;
    19.             renderer.material.mainTextureOffset = new Vector2(horizontalOffset, verticalOffset);
    20.         }
    21.     }
    22.  
    23.     public void DoActivateTrigger()
    24.     {
    25.         scroll = !scroll;
    26.     }
    27.  
    28. }
    29.  
    thats the script that we used !

    but in every other engine that i have ever used it was always cheaper do do this kind of stuff via shader it would go against the laws of physics ^^ that doing this via script should be better for performance ... at least in my head ^^

    so yes i am hell bend on doing it via shader !
     
    Talmagett_Games likes this.
  6. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,748
    Should that be in the FixedUpdate? Not that it would make a massive difference. Just tried your script on some game objects in my test scene and had no change in framerate at all. How many objects are you using this on? I added the script to 100 objects and there was still no change in the framerate. Wonder what is going on.
     
    Last edited: Apr 4, 2011
  7. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    id say around 10 per scene i guess
     
  8. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    so i tried update instead of fixedupdate that makes no difference in performance but commenting out this line gives me an extra 100 fps

    // renderer.material.mainTextureOffset = new Vector2(horizontalOffset, verticalOffset);

    ( there are 1000 objects in the scene with the shader / script for debugging purposes)



    edit: i also just tried:

    unlit/texture (the cheapest shader) + script

    vs

    100 instructions sse shader model 3 shader with panning and no script

    and its again a 100 fps difference in favor of the shader !


    it is proven for scrolling purposes:
    shader > script

    now how do we enable this on low end machines with shaderlab though ?
     
    Last edited: Apr 4, 2011
  9. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,748
    Interesting it must be a Hardware restriction or opengl/directx difference. I just tested a scene that alters renderer.material.mainTextureOffset 10,000 * FixedUpdate rate and my scene goes from 78fps to 72fps with the scroll turned on. I did cache the material in Start mind.

    Anyway that doesn't get the question answered does it :)
     
    Last edited: Apr 4, 2011
  10. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    hardware wise it must be possible to do scrolling on lets say some intel integrated graphics chip:

    rayman2 which is stone old has scrolling water falls you guys think they did that via code ?

     
  11. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    That sounds absurd. Were you running any other scripts? How many milliseconds per frame are you talking?
     
  12. warby

    warby

    Joined:
    Mar 23, 2009
    Posts:
    162
    of course i was running and rendering the entire game !

    the game ran 60 fps on my machine at the time so id say 8 ms ?!
     
  13. Pia

    Pia

    Joined:
    Feb 16, 2009
    Posts:
    78
    As far as I know, accessing renderer.material.someProperty causes Unity to internally create a new material. Therefore it makes perfect sense that shaders are far more effective.
     
  14. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    How many objects were UV panning? That is a very long time to spend modifying materials.

    Actually, now that I think of it: have you tried moving that code to Update()? That's the only time the effects will be seen, anyway. I have also seen odd situations where accessing properties from Update or FixedUpdate takes way longer than doing so in the other.

    Accessing Renderer.material instances the material the first time it is called for a given renderer. After that, though, there is no more memory being allocated. The only real overhead should be from GPU state changes, which wouldn't actually be traced back to the script by the profiler.
     
  15. Dover8

    Dover8

    Joined:
    Aug 20, 2010
    Posts:
    94
    A lot of discussion on the topic here, but how do you actually do UV scrolling in a shader??? I to need to do this as I cannot attach scripts to asset bundles, which we use to load in items to our environment at run time.
     
    andrewnielson and Sponxie like this.
  16. Orion

    Orion

    Joined:
    Mar 31, 2008
    Posts:
    261
    To clarify where your slowdown probably comes from:

    Using "renderer.whatever" will search the GameObject for a Renderer component in a rather expensive way. Instead do this:

    Code (csharp):
    1. Renderer _renderer;
    2.  
    3. public void Awake()
    4. {
    5.   _renderer = renderer;
    6. }
    7.  
    8. public void Update()
    9. {
    10.   _renderer.whatever.doWhatever();
    11. }
    In your case you could even store the material.


    And I'd also like to know how to scroll the texture via shader, since in my case I need it for about 500 objects at once :)
     
  17. David_Biggs

    David_Biggs

    Joined:
    Sep 13, 2012
    Posts:
    8
    Grave dig here but this is what came up 1st on google so I'll add to it.

    What the OP wanted as a UV scrolling solution in a shader. The C# method is just bad.

    In a vertex/fragment cg shader simply do this in your vertex function:

    Code (CSharp):
    1. VertexOutput vert(VertexInput i)
    2. {
    3.     VertexOutput o;
    4.     o.vertex = mul(UNITY_MATRIX_MVP, i.vertex);
    5.     i.texcoord.x += _Time * _speedU;
    6.     i.texcoord.y += _Time * _speedV;
    7.     o.texcoord = i.texcoord;
    8.     return o;
    9. }
    _speedU and _speedV are passed in to the shader.
     
    twitchfactor and JanTuts like this.
  18. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Firstly _Time is not a single value, it's a float4. The code above while compile for some platforms but not all as conversion from float4 to float isn't always handled. The behavior when it does work is also not what you expect as _Time.x, the value that will be used in most cases where the above compiles, is actually "time / 20". The value you want is actually _Time.y, which is just time unmodified.

    See: http://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

    Secondly adding any value from _Time to the texture coordinates straight is a bad idea. At first it'll be fine, but if your game runs for long enough that value from _Time is going to be a very large number and there are going to start being precision issues that will present themselves as the texture starting to look pixelated. You want to wrap the time value, after multiplying it by the speed scale, to stay with in a 0 to 1 range using frac().

    Thirdly when ever possible if you can do math operations in a shader using a vector instead of multiple scalers (i.e.: float4 instead of four floats) you should. Most GPUs are designed to do vector math, so something like float4 * float4 takes as much time as float * float, so doing the later 4 times is 4 times slower than the single float4 * float4. Shader compilers can sometimes be smart enough to combine these, but don't count on it.

    So, the above lines 5 though 7 should be condensed to the following single line.
    Code (CSharp):
    1. o.texcoord.xy = i.texcoord.xy + frac(_Time.y * float2(_speedX, _speedY));

    Edit: Some additional information. The above thread before David_Biggs' post is talking about texture scrolling with fixed function shaders. In that case the use of a script is the only answer. However fixed function shaders are effectively deprecated in Unity 5.0 and there's not a lot of reason to use them in 4.0. In 5.0 they get converted to vert / frag style shaders when actually used so there's no good reason to keep using the fixed function forms.
     
    Last edited: Oct 21, 2015
    nindim, Beauque, JanTuts and 3 others like this.
  19. David_Biggs

    David_Biggs

    Joined:
    Sep 13, 2012
    Posts:
    8
    Thanks bgolos, I'd realised _Time was a vector soon after posting this. The frac() call is a great idea too,
     
  20. monotoan

    monotoan

    Joined:
    Feb 13, 2015
    Posts:
    11
    In case anyone comes still looking for this like I did, here's a way to integrate UV scrolling/panning into Unity 5's default editable standard shader. (To give due credit: this code is a slight modification of the helpful tutorial here).

    1. Add these input variables to the Properties { } section at the top:

    Code (CSharp):
    1.         _ScrollXSpeed("X Scroll Speed",Range(0,1)) = 0.1
    2.         _ScrollYSpeed("Y Scroll Speed",Range(0,1)) = 0.1
    2. Add equivalent variables to the declarations in the SubShader { } section, somewhere around sampler2D _MainTex:

    Code (CSharp):
    1.         fixed _ScrollXSpeed;
    2.         fixed _ScrollYSpeed;
    3. Finally, modify your void surf() function to include the following starred lines. Basically the added code is grabbing your MainTex, adding your scrollspeed multiplied by time (so it changes each frame), and then passes that updated texture data into your tex2D instead of MainTex.


    Code (CSharp):
    1.       void surf (Input IN, inout SurfaceOutputStandard o) {
    2.  
    3.             fixed2 scrolledUV = IN.uv_MainTex; //***
    4.  
    5.             fixed xScrollValue = frac(_ScrollXSpeed * _Time.y); //***
    6.             fixed yScrollValue = frac(_ScrollYSpeed * _Time.y); //***
    7.             scrolledUV += fixed2(xScrollValue, yScrollValue); //***
    8.  
    9.             // Albedo comes from a texture tinted by color
    10.             fixed4 c = tex2D (_MainTex, scrolledUV) * _Color; //***
    11.             o.Albedo = c.rgb;
    12.             // Metallic and smoothness come from slider variables
    13.             o.Metallic = _Metallic;
    14.             o.Smoothness = _Glossiness;
    15.             o.Alpha = c.a;
    16.         }
     
  21. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    any ideas why i get distortion, when trying to multiply the scroll speed by a vertexcolor?
    Code (CSharp):
    1. fixed4 c = tex2D (_MainTex, float2(IN.uv_MainTex.x, IN.uv_MainTex.y+(_Time.x*_ScrollSpeed*IN.vertColor.r))) * _Color;
     
  22. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    I’m going to guess the vertex color isn’t constant across the triangle? That’ll cause all sorts of weirdness.
     
  23. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    Yes i'd like to adjust scroll speed by a gradient in the vertex colors, anyway to avoid distortions on the triangle that have gradient?
     
  24. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Yes and no.

    The frac() in my example is important! That will remove the majority of the distortion you're seeing. Lets think through why.

    If you have a gradient value that's multiplying the pan rate, if the top of a quad is a speed of 1.0, and the bottom of the quad is a speed of 0.9, after 1 minutes the top of the quad will have panned 60 units, but the bottom will have only panned 54 units. The result is the top and bottom are off by 6 units, or 6 widths of the texture, causing terrible stretching. And that's only 1 minute in, a few minutes later and it's hundreds of units off.

    Now the frac() function limits a value to it's fractional component, ie: 0.0 to ~1.0 (0.999... really). If used with the same setup above after 1 minute the top will have still panned 60 units, but frac(60) == 0.0, and frac(54) is also 0.0. In the worst case the top and bottom UVs will only be off by one unit. It'll still be skewed, but no where near as badly.

    But this introduces a new issue where the top and bottom are looping at different times. Every 1 second the top loops, but the bottom takes 1.111 seconds meaning the UVs start to oscillate and flicker. When the UVs are panning at a constant speed across the entire face this isn't an issue since the entire face is looping at the same time.

    So, it would seem the answer is no.

    But there is a solution.

    Flow maps.

    Or rather using some of the ideas behind flow maps. It requires you do most of the UV work per pixel rather than per vertex though. The basic idea is instead of just panning the UVs, you're panning two sets of UVs, sampling the texture twice, and fading between the two to hide where it's out of phase. This works well for water or similarly noisy textures, less so for anything man made or with regular patterns.
     
    AcidArrow likes this.
  25. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    @bgolus thanks for the explanation, i was afraid something like that was happening.
    the flowmaps work well for bumps but not for diffuse unfortunately. you see the loop rather hard fading in and out. also it restricts to using only a small part of the texture :( at least that's my experience with it, please correct me if I'm wrong.
     
  26. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    There's ways you can make the fade be not so bad, assuming it's kind of lumpy like terrain textures often are. You could also try scaling the texture based on the speed which causes a different kind of distortion, but one that's usually not as objectionable. Or you could quantize the speed so it happens in steps and there are hard breaks.

    It kind of depends on what exactly you're trying to do. You have to accept some kind of artifact to do this.
     
  27. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    trying to change scroll speed on a waterfall mesh
    upload_2018-1-12_16-54-14.png

    tried putting the distortion in small quads in between but because i also apply vertex displacement i get even stranger artifacts :)
     
  28. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    If you're going to do it that way, your better off just not having those quads.

    However a waterfall is a perfect example of where you shouldn't be modifying the pan speed at all, only the UVs. Stretch out the UVs in the direction you want it to look like it's moving faster.
     
    jister likes this.
  29. GamerGirl89

    GamerGirl89

    Joined:
    Oct 20, 2018
    Posts:
    2
    so was this ever solved?
     
  30. Don-Tako

    Don-Tako

    Joined:
    Jan 31, 2013
    Posts:
    7
    I really don't like shader graph, so I always hard-code my own shaders (or modify online ones)

    So, I think this should be useful for someone...

    Im using this, just be sure that your texture is set to Repeat (no clamp)
    Its works for my 2D cascade tiles for a platformer game.


    Code (CSharp):
    1. Shader "Custom/TextureScroll" {
    2.     Properties
    3.     {
    4.         _MainTex ("Texture", 2D) = "white" {}
    5.         _ScrollXSpeed("X Scroll Speed",Range(0,100)) = 0.1
    6.         _ScrollYSpeed("Y Scroll Speed",Range(0,100)) = 0.1
    7.     }
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Opaque" }
    11.  
    12.         LOD 100
    13.  
    14.         Pass
    15.         {
    16.             CGPROGRAM
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.  
    20.             #include "UnityCG.cginc"
    21.  
    22.             struct appdata
    23.             {
    24.                 float4 vertex : POSITION;
    25.                 float2 uv : TEXCOORD0;
    26.             };
    27.  
    28.             struct v2f
    29.             {
    30.                 float2 uv : TEXCOORD0;
    31.                 float4 vertex : SV_POSITION;
    32.             };
    33.  
    34.             sampler2D _MainTex;
    35.             float4 _MainTex_ST;
    36.             fixed _ScrollXSpeed;
    37.             fixed _ScrollYSpeed;
    38.  
    39.             v2f vert (appdata v)
    40.             {
    41.                 v2f o;
    42.                 o.vertex = UnityObjectToClipPos(v.vertex);
    43.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    44.                 return o;
    45.             }
    46.            
    47.             fixed4 frag (v2f i) : SV_Target
    48.             {
    49.                 half4 sub;
    50.                 half2 uv = i.uv;
    51.                 half t = _Time.x - floor(_Time.x);
    52.                 uv.x =  i.uv.x + t * _ScrollXSpeed;
    53.                 uv.y =  i.uv.y + t * _ScrollYSpeed;
    54.                 fixed4 col = tex2D(_MainTex, uv);
    55.                 return col;
    56.             }
    57.             ENDCG
    58.         }
    59.     }
    60. }
     
    Beauque likes this.
  31. Beauque

    Beauque

    Joined:
    Mar 7, 2017
    Posts:
    61
    Does modifying/panning the uvs in fragment instead of vertex shader make any difference?
    So I could toggle panning using a shader_feature_fragment keyword