Search Unity

Question URP shadard surface shader best approximation

Discussion in 'Shaders' started by dejce18, Jul 18, 2022.

  1. dejce18

    dejce18

    Joined:
    Apr 28, 2021
    Posts:
    3
    Hi everybody,

    I'm quite new to this shader thing in unity so I have a question about it :). So, I'm following Sebastian Lague's tutorial about procedural mesh generation and he's used a standard surface shader but the thing is, that this type of shader is not supported in URP. What is the best approximation to a standard surface shader (I hope it's not a shader graph because I do not like that thing)? Is there any way to make the standard surface shader work and not show pink mesh ?

    Btw is there any way to define arrays in shader graphs blackboard?

    Thanks for your help
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The URP's default Lit Shader.

    No.
     
  3. dejce18

    dejce18

    Joined:
    Apr 28, 2021
    Posts:
    3
    Is there any workaround to this? Or better say, is there any way to use a color array or float array in the shader graph? If there is what is the best practice?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Unity doesn't support material properties that are arrays for Shader Graph or otherwise. However you can still pass arrays to shaders. For hand written shaders this is as straightforward as adding an array uniform to the shader code. But for Shader Graph the only way to do it would be to use a custom function that uses a separate .hlsl file.*

    https://docs.unity3d.com/Packages/com.unity.shadergraph@11.0/manual/Custom-Function-Node.html

    Code (csharp):
    1. #ifndef MYSHADER_CUSTOM_ARRAY
    2. #define MYSHADER_CUSTOM_ARRAY
    3.  
    4. float _MyFloats[];
    5.  
    6. void GetMyFloatValue_float(int index, out float value)
    7. {
    8.   return _MyFloats[index];
    9. }
    10.  
    11. float4 _MyColors[];
    12.  
    13. void GetMyColorValue_float(int index, out float4 value)
    14. {
    15.   return _MyColors[index];
    16. }
    17.  
    18. #endif MYSHADER_CUSTOM_ARRAY
    To assign the values to the array, you'll need to use a c# script.
    https://docs.unity3d.com/ScriptReference/Material.SetFloatArray.html
    https://docs.unity3d.com/ScriptReference/Material.SetColorArray.html

    A few things to be mindful of.
    • As mentioned before, arrays cannot be material properties. This also means they are not serialized, meaning they won't be saved, nor will they survive level loads or sometimes even context switches (alt-tab or minimizing on PC, or switching apps on mobile / console). So you may need to set the array on the material every update to make sure it stays valid.
    • The array size cannot change after it's been assigned to a material. It must be created and assigned at the largest size you'll reasonably need. If you need to change it for some reason, you'll need to make a create a new material and assign a different sized array to that. This can be particularly annoying in the editor as it may necessitate restarting the editor if you assign an array to a material asset and need to change the size. Using a ComputeBuffer / StructuredBuffer instead of an array can get around this.
    • If you are assigning color values, Unity will normally do color space conversions for you when the values you set are a color property. Since arrays are not properties, Unity will not do the color space conversion. So you need to know what color space your project is using for rendering and do the color space conversion yourself when assigning the values to the array you pass to the material.
    Code (csharp):
    1. // c# code
    2. for (int i=0; i<MyColors.Length; i++)
    3. {
    4.   if (PlayerSettings.colorSpace == ColorSpace.Linear)
    5.     myColorsData[i] = myColors[i].linear;
    6.   else
    7.     myColorsData[i] = myColors[i];
    8. }
    9. mat.SetColorArray("_myColors", myColorsData);

    * Technically there's one way to do it that doesn't require a separate file. Instead of using an array, you can use a texture that you set the pixel values of from code, and then sample the individual texels of. This sounds like it should be really bad to do, but it's not and isn't significantly worse for performance. Especially on desktop where you can use
    _MyArrayTexture.Load(int2(index,0)
    in a custom function to treat the texture almost like an array, though you can also use a sample node as well if you manipulate the UV position properly.
     
    lclemens and dejce18 like this.
  5. dejce18

    dejce18

    Joined:
    Apr 28, 2021
    Posts:
    3
    Thank you very much for your time and explanation