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

Hello,
I’m trying to animate the texture offset of one of my object’s materials by changing it via script using the following:

private void LateUpdate()
{
    _rnd.materials[1].mainTextureOffset = new Vector2(0,speed * Time.time);
}

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.

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.

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.

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.

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!

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.

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:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ConveyorBelt : MonoBehaviour
{
    public float speed = 0.02f;
    Renderer _rnd;
    Material[] _beltReference = new Material[3];

    void Start()
    {

        _rnd = GetComponent<Renderer>();
        _beltReference = _rnd.materials;
    }

    private void Update()
    {
        _beltReference[1].mainTextureOffset = new Vector2(0, 1);
    }
    private void LateUpdate()
    {
        _rnd.materials = _beltReference;
    }

}

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.

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.

4940591–479537–MultiMaterialTestFBX.unitypackage (87.9 KB)

As far as I can see, you’re using the Standard shader. I’m on URP (formerly, LWRP), using the Lit shader.

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.

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.

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.

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

using UnityEngine;

public class SlidingTexture : MonoBehaviour
{
    public float delayBetweenFrames;

    private Renderer rend;
    private Material[] mat;  //Just in case you have more than one materials on a Renderer.
    private float timeOfLastTextureShift;

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

    void Update()
    {
        float timePassed = Time.timeSinceLevelLoad - timeOfLastTextureShift;
        if ( timePassed < delayBetweenFrames) return;
      
      
        Vector2 newOffset = new Vector2(mat[0].GetTextureOffset("_BaseMap").x + 0.1f, 0);
        rend.materials[0].SetTextureOffset("_BaseMap", newOffset);
        timeOfLastTextureShift = Time.timeSinceLevelLoad;
    }
}

5384238--545631--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.

10 Likes

use : material.SetTextureOffset(“_BaseMap”, newOffset);

“_MainTex” will not work instead “_BaseMap”.

24 Likes

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

1 Like

Awesome hint man! This shouldn’t be as difficult to find as it is.

T

Muchas grazias.

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.

Thank’s a Lot

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();
}

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