Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved No way to get global Shader values in SubGraphs?

Discussion in 'Shader Graph' started by tonycoculuzzi, Jun 15, 2020.

  1. tonycoculuzzi

    tonycoculuzzi

    Joined:
    Jun 2, 2011
    Posts:
    301
    I'm scratching my head trying to understand how to access global Shader values in SubGraphs (for example, Shader.SetGlobalColor, Shader.SetGlobalTexture, etc.

    Does anyone know how to access global values in a Sub-Graph?

    If not, it seems like a pretty unfortunate and confusing limitation
     
  2. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    What was the workaround? Big fan of your work btw
     
  3. tonycoculuzzi

    tonycoculuzzi

    Joined:
    Jun 2, 2011
    Posts:
    301
    I ended up using a ShaderGraph custom function node, which referenced a .cginc file defining the global Texture2D property and outputting it to the custom node output.

    I had a lot of unity-employee help on account of the ShaderGraph having really unhelpful errors/warnings haha, I wouldn't have been able to figure it out otherwise

    and thank you! I appreciate you saying that :)
     
    alexanderameye likes this.
  4. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    77
    Could you post the .cginc files for your solution? That sounds a lot more workable but I'm not sure how to make those sorts of files or interface with them through scripts.

    --

    Unrelated - I've seen about 3 or 4 topics asking this topic's same question. Are there plans to allow subgraphs to have references which can then be modified through scripts?

    --

    For my solution, I've been doing this in a script so I can access these global values in shader graphs.

    Shader.SetGlobalColor("Global_HeightFog_Color", heightFogColor);

    But then I still have to define each global variable in the outer-most shader graph and send it into the subgraphs, which is tedious. A common example is I have a fog subgraph I use in multiple shaders, which all use the same fog global variables. But I have to define all the fog variables in each shader graph in order to send them into the fog subgraph.
     
  5. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    77
    (Alternatively could a ShaderGraph dev share a tutorial on how to define global variables this way?)
     
  6. RoyBarina

    RoyBarina

    Joined:
    Jul 3, 2017
    Posts:
    95
    Could be great feature to have. sub-graph global keywords\variables
    There were already two types of keywords, Enum and Boolean (there's also a "built-in" keywords but those reserved for the pipeline asset to control them) that we can change in a sub-graph through a C# script with something like this:
    Code (CSharp):
    1. private const string MY_KEYWORD = "KeywordReferenceName";
    2. void ChangeMyKeyword(bool enable){
    3.     if(enable)
    4.         Shader.EnableKeyword(MY_KEYWORD);
    5.     else
    6.         Shader.DisableKeyword(MY_KEYWORD);
    7. }
    Why there isn't a float keyword?
     
  7. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    77
    I was able to ask OP about the method used, and they said:

    "I had to create a .cginc that defined and returned the global texture sampler, and then referenced the get method from the.cginc in a shadergraph custom/code node."

    If anyone knows what I'd have to write to do this that would be appreciated! I'm calling SetGlobalFloat, SetGlobalColor, etc on a GameObject and would like to be able to read those variables through the custom shadergraph node.
     
  8. DDobyns

    DDobyns

    Joined:
    Jan 28, 2018
    Posts:
    7
    Would this .cginc path support a Global VectorArray variable (which isn't a supported input/output type in Shader Graph) assuming all operations on that array happen within the custom node?

    I'm trying to maintain a list of object positions, then in the shader run a for loop that checks distance from each of them (for an interactive grass shader)
     
  9. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    818
    Not sure if you're still looking for this, but its fairly straightforward:

    1. Create a new .hlsl file, for example `get-global-color.hlsl` with the contents as:
    Code (CSharp):
    1. //Make sure its only included once!
    2. #ifndef CUSTOM_COLOR_INCLUDED
    3. #define CUSTOM_COLOR_INCLUDED
    4.  
    5. float4 _My_Global_Color;
    6.  
    7. void getGlobalColor_float(out float4 color)
    8. {
    9.     color = _My_Global_Color;  
    10. }
    11. #endif
    2. Create a new custom functions node, with an output of your color. Drag in the file, and call the function `getGlobalColor`, without the float suffix as described in the manual.
     
    florianBrn and melos_han_tani like this.
  10. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    77
    Thanks, that's a big help! I ended up not needing to work on shaders any more for my current project but this'll be a huge help in the future.
     
  11. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    818
    I believe with newer versions you can also make a variable not exposed in the Graph Inspector, effectively making it a global. But for 2019.4 at least thats not an option, i'm not sure what version specifically adds this.
     
  12. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,108
    I am in 2019.4 and not exposed variable works the way you described it, but I am not certain about versions, becuase I upgraded URP or Shader Graph version (currently URP 7.3.1, SG 7.5.1).

    About the code above this is nice, but there is problem if you wanted to set global texture or sample it, I still don't know how to do it. Something flashed before my eyes that version ~10.0 has some helper structs or something to make it easy, but its for unity 2020.2.
     
  13. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    818
    A global texture shouldnt be much different i think? What you can do is:

    1. Call SetGlobalTexture from C#
    2. In a custom function sample the texture, accept a UV as argument and just return the float4.
     
  14. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,108
    Something like this in custom function?
    Code (CSharp):
    1. sampler2D _SomeGlobalTexture;
    So is there way to expose this texture to the graph outside (from custom function to the graph)?
     
  15. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    818
    Something like this should work:

    In your hlsl include:
    And then your node:
    upload_2021-2-24_11-2-21.png

    Its possible to define an output as Texture2D too, but i wasnt really able to convince ShaderGraph to use it.
     
    Last edited: Feb 26, 2021
  16. gigazelle

    gigazelle

    Joined:
    May 6, 2017
    Posts:
    10
    I've created global variables inputs within my sub-graph, then hook up the global variables as properties wherever that sub-graph is used. It's an extra step hooking up the sub-graph, but it's super simple and doesn't require an extra file.

    If you want to manipulate the variables in C#, you have to make sure that the properties in the graph are NOT exposed.
     
  17. JJRivers

    JJRivers

    Joined:
    Oct 16, 2018
    Posts:
    137
    The reason folks are asking for that "extra file" is to get rid of refactoring hell when you change something, you have one file that controls all the properties associated with the system you're creating :)
     
  18. AustinMclEctro

    AustinMclEctro

    Joined:
    May 3, 2017
    Posts:
    16
    I have a question that is related:

    Can we set unexposed Shader Graph properties using hlsl, without having to manually hook up nodes?

    For example, let's say I have the following:
    • An exposed bool called Override, hooked up to a branch to choose between either an exposed Color property, or an unexposed Color property, like this:
    upload_2022-7-2_13-50-47.png

    The custom function TestRetrieveProps would set the unexposed property via hlsl, like this:

    upload_2022-7-2_13-46-48.png

    This does not work, as the material's color appears unset/black when Override is false. If it worked, we'd expect it to appear red.

    My use case for this is not wanting to have a mess of node connections from a Custom Function that retrieves a large number of stored properties for a particular shader.

    Of course, in the above, we could just output the color from the function and hook it up to the Branch node, but this is what I'm trying to avoid.

    upload_2022-7-2_13-58-20.png

    It would be very nice if I could set these properties from hlsl code, without having to hook up ~50 properties strewn across my shader graph. Is this possible?
     
  19. quizcanners

    quizcanners

    Joined:
    Feb 6, 2015
    Posts:
    108
    This feels like the main feature a SubGraph should have.

    You can put functionality like, for example, applying custom fog in your Subgraph. So I will need to add the same exact [Unexposed Global] fog parameters every time I add the Subgraph to a shader.
    And what if I change the fog method, add and remove some parameters? Then I will need to modify all of the shaders that use the fog SubGraph.

    Am I missing or misunderstanding something?
     
    Genebris likes this.
  20. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,469
    It really seems like you should be able to unexpose inputs to make them pull from globals the same as you can with a regular graph. It's disappointing that this isn't supported.

    I'm trying the custom function approach, but I can't figure out the correct syntax for returning a Texture2D as an output. The closest I've come so far is doing something like:

    Code (csharp):
    1. Texture2D<float4> _MyGlobalTex;
    2.  
    3. void getMyGlobalTex_float(out Texture2D<float4> tex)
    4. {
    5.    tex = _MyGlobalTex;
    6. }
    ...with the output of the node in the graph inspector set to "Texture 2D".

    Whereupon I get the error:
    "cannot implicitly convert output parameter from 'Texture2D<float4>' to 'struct UnityTexture2D'..."

    If I try this instead:
    Code (csharp):
    1. Texture2D _MyGlobalTex;
    2.  
    3. void getMyGlobalTex_float(out Texture2D tex)
    4. {
    5.    tex = _MyGlobalTex;
    6. }
    ...I get the same error.
     
    Last edited: Oct 24, 2023
  21. FredMoreau

    FredMoreau

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    113
    Hi @Brady,

    this is something we're actually looking into. We're working adding some samples on the topic, but in the meantime, here's some sample code.

    Code (CSharp):
    1. #ifndef GLOBAL_TEXTURE_PROVIDER_INCLUDED
    2. #define GLOBAL_TEXTURE_PROVIDER_INCLUDED
    3.  
    4. TEXTURE2D(_gColorMap);
    5. SAMPLER(sampler_gColorMap);
    6. float4 _gColorMap_TexelSize;
    7. float4 _gColorMap_ST;
    8.  
    9. // Shader Graph Preview will throw an error if the first output is a Texture
    10.  
    11. void TextureProvider_float(out float dummy, out UnityTexture2D ColorMap)
    12. {
    13.     dummy = 1;
    14.     ColorMap = UnityBuildTexture2DStruct(_gColorMap);
    15. }
    16.  
    17. void TextureProvider_half(out float dummy, out UnityTexture2D ColorMap)
    18. {
    19.     dummy = 1;
    20.     ColorMap = UnityBuildTexture2DStruct(_gColorMap);
    21. }
    22.  
    23. #endif
    Note that Shader Graph will throw an error if the first output of a Custom Function Node, or Sub Graph, is a Texture type.
    That's why I've added a dummy float output in the example above.

    For 3D textures and cubemaps, you'll need the following global declarations:
    Code (CSharp):
    1. TEXTURE3D(_gTex3D);
    2. SAMPLER(sampler_gTex3D);
    3.  
    4. TEXTURECUBE(_gCubeMap);
    5. SAMPLER(sampler_gCubeMap);
    The following returns types:
    Code (CSharp):
    1. out UnityTexture3D Tex3D),
    2. out UnityTextureCube Cubmap
    And to use the following struct building macros:
    Code (CSharp):
    1. Tex3D = UnityBuildTexture3DStruct(_gTex3D);
    2. Cubmap = UnityBuildTextureCubeStruct(_gCubeMap);
    Hope this helps.

    Screenshot 2023-10-24 at 11.20.09 PM.png
     
    Brady likes this.
  22. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,469
    Thanks so much! This is exactly the kind of info that needs to be in the documentation. Thanks!
     
    FredMoreau likes this.