Search Unity

(kinda Solved) Can't change texture offset via script in URP?

Discussion in 'Scripting' started by sotirissmix, Sep 6, 2019.

  1. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    Hello,
    I'm trying to animate the texture offset of one of my object's materials by changing it via script using the following:

    Code (CSharp):
    1. private void LateUpdate()
    2. {
    3.     _rnd.materials[1].mainTextureOffset = new Vector2(0,speed * Time.time);
    4. }
    But it's not working in the slightest. The texture offset stands still as a rock. From googling around, I found absolutely nothing related to this.

    It is my guess that this is because of the URP material, but I am not sure.

    Does anybody know of any solution? I'd really not rather create a new shader just for this.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Actually it's how you reference the .materials array, and how that interacts with the material instantiating rules that Unity uses whenever you modify a texture in place on a renderer.

    What you need to do is copy the entire material array out to a temp array of the same size, change the texture offset you want on the one material you care about, then assign the entire array back to the .materials property.
     
  3. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    I see. Is that efficient though? Or is there a more efficient way? Copying it back and forth every frame sounds like it could be performance heavy.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Technically you don't have to copy it back and forth. At Start() you can just copy it out, then when the offset changes, adjust it in your offline copy and sling it back. I have no idea of the performance implications of any of it, and I imagine they might be highly platform and even video driver dependent.
     
  5. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    As I understand it, I'm not referencing the materials in another array, I'm copying them into another array, changing the values, then copying them back to the original array, overwriting the initial values.

    Copying it only once won't work, since the value changes every frame (it's a conveyor belt texture, constantly offset on the Y axis).

    But I guess it's a temporary solution. I'll inspect further, maybe there's another way. Thanks for the help!
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    The most obvious way I can think of would be to break out the UV-scrollable part of the model into a separate mesh and renderer, and then you only need to update that one material each frame, everything else is static.

    This of course assumes you control and can modify the source geometry. If you cannot, you could always rip the submeshes apart at runtime (load time) and create separate objects manually! It would be messy but certainly doable.
     
  7. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    Yeah, it's possible to do that, but I was wondering how to do it by only modifying the material at runtime.

    As an update, I tried your method, and it doesn't seem to work either.

    I've copied the materials into another array, modified the one material's offset, then reassigned the original materials to the modified ones. It still doesn't work.

    This is my code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ConveyorBelt : MonoBehaviour
    6. {
    7.     public float speed = 0.02f;
    8.     Renderer _rnd;
    9.     Material[] _beltReference = new Material[3];
    10.  
    11.     void Start()
    12.     {
    13.  
    14.         _rnd = GetComponent<Renderer>();
    15.         _beltReference = _rnd.materials;
    16.     }
    17.  
    18.     private void Update()
    19.     {
    20.         _beltReference[1].mainTextureOffset = new Vector2(0, 1);
    21.     }
    22.     private void LateUpdate()
    23.     {
    24.         _rnd.materials = _beltReference;
    25.     }
    26.  
    27. }
    28.  
    I know it isn't being animated here, I'm just setting it to a specific value to see if it changes. And it doesn't.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Hm, the code above does not change the texture offset over time. It sets it to a constant Vector2(0,1);

    I changed your code to use Time.time, made a test multi-material model, model, put it in a scene and it works fine: only one material scrolls, as expected.

    See enclosed .unitypackage.
     

    Attached Files:

  9. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    As far as I can see, you're using the Standard shader. I'm on URP (formerly, LWRP), using the Lit shader.
     
  10. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    For now, my solution that works is to use a custom shader that animates over time. Here's my botched solution in case anybody is wondering:



    Keep in mind, if "Animate" is ticked, it will try to animate at a lower framerate in the editor. Upon pressing play, everything seems to run ok.
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Interesting... I overlooked that distinction (that you clearly put in your first post), assuming that at the outer .materials property point of contact, all materials would behave the same.

    Can you verify that if you select the URP material, then the code I posted does NOT work? I don't have easy access to such a thing, but I am curious.

    Thanks in advance! Glad you got it going though.
     
  12. sotirissmix

    sotirissmix

    Joined:
    Dec 1, 2017
    Posts:
    9
    Yes, I've verified it by using the code I posted above. Not sure if I messed it up, but it doesn't seem to work.
    It's possible that, due to the different shader architecture, the texture offset functions don't change values that correspond to the actual offset of the URP shaders.
     
  13. maxklimenok

    maxklimenok

    Joined:
    Feb 14, 2017
    Posts:
    2
    Just in case if someone struggles with changing properties of materials (in SRP) and with materials instancing, it seams that this is a solution:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class SlidingTexture : MonoBehaviour
    4. {
    5.     public float delayBetweenFrames;
    6.  
    7.     private Renderer rend;
    8.     private Material[] mat;  //Just in case you have more than one materials on a Renderer.
    9.     private float timeOfLastTextureShift;
    10.  
    11.     void Start()
    12.     {
    13.         rend = GetComponent<Renderer>();
    14.         mat = rend.materials;
    15.     }
    16.  
    17.     void Update()
    18.     {
    19.         float timePassed = Time.timeSinceLevelLoad - timeOfLastTextureShift;
    20.         if ( timePassed < delayBetweenFrames) return;
    21.      
    22.      
    23.         Vector2 newOffset = new Vector2(mat[0].GetTextureOffset("_BaseMap").x + 0.1f, 0);
    24.         rend.materials[0].SetTextureOffset("_BaseMap", newOffset);
    25.         timeOfLastTextureShift = Time.timeSinceLevelLoad;
    26.     }
    27. }
    AnimatedQuadTexture.gif

    The above example animates a Quad by sliding "10-frame" texture along X-axis at preset time intervals. I'm using 2020.1.0a19 version with latest packages available at the moment.

    The materials get instantiated for each quad at scene loading and could be manipulated independently of each other (i.e. colored differently).

    A few words about "_BaseMap" and other textures on your material: to get understanding of what properties/filed you actually need to change, switch Unity inspector to Debug Mode and inspect your material, you'll find lots of interesting stuff under the hood.
     
    Oelson, sj631, AlejMC and 7 others like this.
  14. reignamation

    reignamation

    Joined:
    Aug 13, 2018
    Posts:
    1
    use : material.SetTextureOffset("_BaseMap", newOffset);

    "_MainTex" will not work instead "_BaseMap".
     
    Noss32, DenisIsDenis, Kaltik and 20 others like this.
  15. KyoXel

    KyoXel

    Joined:
    Feb 4, 2020
    Posts:
    1
    Dude I log in just to say THANK YOU, you are absolute hero, googled so much and didn't find anything that could help :3
     
    dshewmaker likes this.
  16. xkhannx

    xkhannx

    Joined:
    Jan 5, 2019
    Posts:
    2
    Awesome hint man! This shouldn't be as difficult to find as it is.
     
  17. tomraegan

    tomraegan

    Joined:
    Mar 28, 2016
    Posts:
    137
    T
    Muchas grazias.
     
  18. AlejMC

    AlejMC

    Joined:
    Oct 15, 2013
    Posts:
    149
    Are the built-in URP materials texture scale/offset by any chance optimized behind a 'per-renderer data' tag?
    That way probably using property blocks would be the best way.
     
  19. deezero7

    deezero7

    Joined:
    Jun 24, 2019
    Posts:
    2
    Thank's a Lot
     
  20. jubaerjams8548

    jubaerjams8548

    Joined:
    Jun 8, 2020
    Posts:
    41
    try this......its gonna work In Sha Allah


    using UnityEngine;

    public class upset : MonoBehaviour
    {
    // Scroll the main texture based on time

    float scrollSpeed = 2f;
    Renderer rend;

    void Start()
    {
    rend = GetComponent<Renderer>();
    }

    void Update()
    {
    float offset = Time.time * scrollSpeed;
    rend.material.mainTextureOffset = new Vector2(offset, 0);
    }
    }
     
  21. sj631

    sj631

    Joined:
    Dec 23, 2016
    Posts:
    22
    That definitely helped me
     

    Attached Files:

  22. awardell

    awardell

    Joined:
    Feb 5, 2014
    Posts:
    71


    I haven't bothered doing this through code, but if you're using animations then these are the two properties you need to animate the offsets.

    This is with the URP Lit shader

    EDIT: they're _BaseMap_ST.z and _BaseMap_ST.w in case the image ever becomes a dead link.
     
  23. Jelmer123

    Jelmer123

    Joined:
    Feb 11, 2019
    Posts:
    243
    I came here to search for the offset code for the detail map.

    I found it later in the shader's code. It is _DetailAlbedoMap.
    material.SetTextureOffset("_DetailAlbedoMap", new Vector2(offsetX, offsetY));

    THere's also for example: _DetailNormalMap and _DetailMask
     
    Oelson likes this.
  24. brian1gramm1

    brian1gramm1

    Joined:
    Sep 15, 2017
    Posts:
    56
    This fixed my issue. Thanks.
     
  25. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Have any of you guys noticed that the offset animations don't work in release mode? Mine look fantastic in the editor, but whenever I build a release the material has no animation. I'm not sure if it makes a difference, but I'm using DOTS, HRV2, URP, and Addressables.
     
  26. kartoonist435

    kartoonist435

    Joined:
    Feb 14, 2015
    Posts:
    73
    Here is the answer:
    mat.SetTextureOffset("_BaseMap", new Vector2(offset, 0));
     
    XLAN and muhammad_ali_safdar like this.
  27. Kaltik

    Kaltik

    Joined:
    Nov 14, 2013
    Posts:
    2
    Big thanks!
     
    XLAN and muhammad_ali_safdar like this.
  28. abdullahNA

    abdullahNA

    Joined:
    Mar 13, 2022
    Posts:
    4
    .
    big hands for you Man. It is the simple solution I really needed.
    "_MainTex" is work in SRP but not work for URP material. If any body is using URP then use "_BaseMap" instead of "_MainTex".
    Thanks...
     
    francois_unity732 likes this.
  29. Colmod

    Colmod

    Joined:
    Jan 9, 2022
    Posts:
    7


    Thank you...you helped me so much, recently upgraded my project to urp and im using 'Physics Tank maker' and after i swithced my track scroll stopped working. thank you so much, We need more people like this!
     
    Last edited: Sep 27, 2023