Search Unity

Question RenderTexture instance being removed from shader

Discussion in 'Shaders' started by Abended, May 18, 2021.

  1. Abended

    Abended

    Joined:
    Oct 9, 2018
    Posts:
    142
    I have 2 materials on a mesh.
    For material 1 I have 2 textures.
    One of the textures is a RenderTexture instance.

    I would like to use this instance (or at least the alpha channel of it) as another input for a different shader's texture.
    I have a script on the object that has a reference to the rendertexture.
    I've created a method that grabs the RT reference and places it in the other shader via a material property block. When this happens, the texture input from the original shader goes blank.

    Shader #1 - has both textures
    upload_2021-5-17_21-53-41.png

    Then I run this script to set the RT as a texture on shader #2
    Code (CSharp):
    1.         public void AddRenderTexture()
    2.         {
    3.             var fairwayscript = gameObject.GetComponent<FairwayShape>();
    4.        
    5.             var ren = gameObject.GetComponent<Renderer>();
    6.  
    7.  
    8.             var mat = ren.materials[1]; // Get Second Material!
    9.             var mpb = new MaterialPropertyBlock();
    10.  
    11.             ren.GetPropertyBlock(mpb, 1); // I have tried 0 and 1 here and in the set all combinations
    12.  
    13.             mpb.SetTexture("_NoGrassTex", fairwayscript.ren);
    14.             ren.SetPropertyBlock(mpb);
    15.         }
    Shader #1 - now lost the RT
    upload_2021-5-17_21-55-16.png

    Shader #2 - the RT is now associated here, but removed from the previous shader
    upload_2021-5-17_21-56-53.png

    how can I have this instance of RT be used by both shaders? it's 2048, so I'd hate to duplicate it and double the amount of memory used for the same texture. I've also tried to save it as an in-memory Texture2D, but the same thing happens.
     
    Last edited: Jun 23, 2021
  2. Abended

    Abended

    Joined:
    Oct 9, 2018
    Posts:
    142
    Anybody want to take a crack at it? I don't know where to begin with this one.
     
  3. Abended

    Abended

    Joined:
    Oct 9, 2018
    Posts:
    142
    Somebody throw me a bone! I updated the original post with the steps of what happens.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The question I would have is how is the render texture set on the first material?

    Presumably the render texture is set on the first material via a material property block as well, and if that's being set via
    .SetPropertyBlock(mpb);
    as well, you're going to have a bad time.

    Understand there are two ways to set (and get) a material property block on a renderer, either for the whole renderer, or per material index, and you should not mix the two.

    See this comment from the documentation: https://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html
    The implication of that is there is a per-renderer block and a list of per-material blocks on the renderer. If you call
    SetPropertyBlock(mpb)
    and then
    GetPropertyBlock(mpb, #)
    , the get will return an empty block, because you originally assigned a per-renderer block and then tried to get a per-material block, which was not set.

    So to reiterate the above, my theory is some other code is doing the equivalent of:
    Code (csharp):
    1. mpb.SetTexture("_MowingTexture", fairwayscript.ren);
    2. // we'll ignore you're confusingly naming both a render texture and renderer component 'ren' in your code example...
    3. // the important part is the set function isn't using a number
    4. ren.SetPropertyBlock(mpb);
    Then your code is doing:
    Code (csharp):
    1. // could be a 0 or 1 or any number here, it doesn't matter
    ren.GetPropertyBlock(mpb, 1);[/code]
    Which is getting you a blank property block, since no per-material block was ever assigned. Then you're doing:
    Code (csharp):
    1. mpb.SetTexture("_NoGrassTex", fairwayscript.ren);
    2. // note, no number again
    3. ren.SetPropertyBlock(mpb);
    And blowing away the original property block that had
    _MowingTexture
    set with the new one that now only has
    _NoGrassTex
    set.


    TLDR: You can probably fix this with
    ren.GetPropertyBlock(mpb);
    , don't put a number there at all.
     
  5. Abended

    Abended

    Joined:
    Oct 9, 2018
    Posts:
    142
    @bgolus I love you, man. I appreciate you sharing your knowledge on the forum! So I think you gave me the a-ha moment. When I first set the block, I am doing it globally. Then when I set this one, I am switching it to a material level block and destroying/overriding the global one. When I first started this, there was only one material. Now I am layering on a second one. I assumed the MPB was always at the material level. Now that I see the distinction, I may be able to attack it a different way. I want to believe I initially was setting it with no index, as I didn't learn about it until tried reading about it in the documentation. But I'm at least coming at it as it's a solvable issue, I had thought when I posted this that I was just hitting some kind of limitation because I was generating the RT in code and assigning it from memory instead of doing it in the editor. I'm getting back on the horse, I'll let you know if it works! Thanks again!
     
  6. Abended

    Abended

    Joined:
    Oct 9, 2018
    Posts:
    142
    @bgolus As a follow up, I got this to work by simply adding the texture in the same method that I added the original. I had made a new test class to try and figure out how to deal with the new material, by doing so, caused the confusion with the MPB. Still some issues, but I'm getting there. Thanks for the leg up!