Search Unity

Setting unity_LODFade from script

Discussion in 'Shaders' started by gilley033, May 3, 2020.

  1. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    Hello all, I am trying to set the unity_LODFade built in shader variable from script, however it does not work. Here is the code:

    Code (CSharp):
    1. rend.GetPropertyBlock(block);
    2. Vector4 fadeOutVec = new Vector4(tFadeOut, tFadeOut * 16f);
    3. block.SetVector("unity_LODFade", fadeOutVec);
    4. rend.SetPropertyBlock(block);
    where tFadeOut is a value between 1 and 0 (lerped over time).

    I'm not sure if tFadeOut * 16 is correct for the y argument, but I don't think it matters in this case as my test shader is just using the x value.

    Here's the shader:
    Code (CSharp):
    1. Shader "Custom/CrossFadeLOD"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Color", Color) = (1,1,1,1)
    6.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    7.     }
    8.         SubShader
    9.         {
    10.             Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
    11.             LOD 200
    12.  
    13.             CGPROGRAM
    14.  
    15.             #pragma multi_compile _ LOD_FADE_CROSSFADE
    16.             #pragma multi_compile_instancing
    17.             #pragma instancing_options lodfade
    18.  
    19.             #pragma surface surf Standard alpha:fade
    20.             #pragma target 3.0
    21.  
    22.             sampler2D _MainTex;
    23.            
    24.             struct Input
    25.             {
    26.                 float2 uv_MainTex;
    27.             };
    28.  
    29.             fixed4 _Color;
    30.  
    31.             void surf(Input IN, inout SurfaceOutputStandard o)
    32.             {
    33.                 // Albedo comes from a texture tinted by color
    34.                 fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    35.                 o.Albedo = c.rgb;
    36.     #ifdef LOD_FADE_CROSSFADE
    37.                 o.Alpha = c.a * unity_LODFade.x;
    38.     #else
    39.                 o.Alpha = c.a;
    40.     #endif
    41.             }
    42.             ENDCG
    43.         }
    44.             FallBack "Transparent/Diffuse"
    45. }

    Is it not possible to set this value manually? My use case is, I am trying to create a script that can make use of existing shaders that work with LODGroup, but I want to control the LOD transition process myself. It's for an Asset Store product which is why I am trying to make the script work with existing shaders that work with LDOGroup, to allow for maximum compatibility and not make my users have to modify their shaders.

    Thanks for any help!
     
  2. dyox

    dyox

    Joined:
    Aug 19, 2011
    Posts:
    619
    Bump
     
  3. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    I couldn't get it to work, I suspect it is not possible, but hopefully someone comes along with a different answer.
     
  4. sewy

    sewy

    Joined:
    Oct 11, 2015
    Posts:
    150
    Hey guys, I've managed to get it working - not sure about the unity_LODFade.y value - I got it printed using this (which helped me quite a lot for this project) and it returns the same value as unity_LODFade.x.

    I've used your code and made it coroutine which "lerps" the tFadeOut value over time (same result as LODGroup crossFade is doing) - when lods needs to changed, call this coroutine instead.

    For surface take a look at dithercrossfade
    For VF shader, all you need is
    #pragma multi_compile _ LOD_FADE_CROSSFADE, and
    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy); - at the beginning of frag shader

    Code (CSharp):
    1. //Call inside Start()
    2. rend.sharedMaterial.EnableKeyword("LOD_FADE_CROSSFADE");
    3.  
    4. // Coroutine/Lerp this
    5. Vector4 fadeOutVec = new Vector4(tFadeOut, tFadeOut);
    6. block.SetVector("unity_LODFade", fadeOutVec);
    7. rend.SetPropertyBlock(block); // Blocks can be pooled
    8.  
    9. // Call this one frame after tFadeOut == 0, this will cause static batching to work again
    10. rend.SetPropertyBlock(null);
    11.  
    Keep in mind, that when changing LOD0->LOD1
    LOD0: unity_LODFade.x -> <1, 0>
    LOD1: unity_LODFade.x -> <-1, 0>

    Also take a look at this, I am using it for all lods which should not be rendered instead of disabling the mesh renderer.
    rend.forceRenderingOff = false;


    EDIT: I've made a few updates
    • EnableKeyword should be called on rend.sharedMaterial, because it DOES lead to material instancing if using rend.material
    • Clear the property block one frame after fade == 0 to reenable static batching (otherwise it will not work due to different propertyBlock
     
    Last edited: Apr 20, 2021
    tmonestudio likes this.
  5. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    Very nice! Two questions, if you don't mind.

    "is sharedMaterial is used, it wil break for other object that are using it." - can you explain this more?

    "Keep in mind, that when changing LOD0->LOD1
    LOD0: unity_LODFade.x -> <1, 0>
    LOD1: unity_LODFade.x -> <-1, 0>"

    You are saying for the LOD0 object which is fading out, you lerp from 1 to 0?
    And for the LOD1 object which is fading in, you lerp from -1 to 0?
    That does not seem correct, so I think I am misunderstanding.

    Thanks!
     
  6. sewy

    sewy

    Joined:
    Oct 11, 2015
    Posts:
    150
    Because you would be enabling Keyword for shared material (material asset), it will influence all of it usages in the scenes, so if you use the same material for X object in the scene, but you want to crossfade only one of those objects, it will crossfade all of those objects. (EDIT: it will, but that should not matter, because unity_LODFade.x will be 0 - see my first post) As I got it working only yesterday, I still have some unknowns, but will continue to watch it.

    Correct, for SRP, the LODDitheringTransition(), the code says
    Code (CSharp):
    1. // LOD0 must use this function with ditherFactor 1..0
    2. // LOD1 must use this function with ditherFactor -1..0
    3. // This is what is provided by unity_LODFade
    4. void LODDitheringTransition(uint2 fadeMaskSeed, float ditherFactor)
    via trial and error (and printing the value by above mantioned function) I've found out, that if you have currentLOD and nextLOD, currentLOD always should be lerping from 1 to 0, and nextLOD from -1 to 0. It does not matter if you move up or down in LODs.


    Edit: Updated link
     
    Last edited: Sep 20, 2023
  7. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    Ok, awesome, thanks!!
     
  8. KYL3R

    KYL3R

    Joined:
    Nov 16, 2012
    Posts:
    135
    I found an explanation in the docs, where they say:

    Level-of-detail fade when using LODGroup. x is fade (0..1), y is fade quantized to 16 levels, z and w unused.

    so .y will be roughly the same, but the transition would be more bumpy I guess.
     
    sewy likes this.
  9. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    1,191
    I stopped trying to make this work for a while, but am renewing my efforts once again.

    To be honest, I don't really understand why you need to use -1 to 0 for the stuff that is fading in. If you fade in starting at 0 and going to 1, then fade out starting at 1 and ending at 0, doesn't that accomplish the same thing?

    And how can fully faded in and fully faded out be represented by the same value (0)?

    In any case, I tested using 0 -> 1 for fading in and 1 -> 0 for fading out and it seems to work just fine.

    Another note for anyone using URP. LOD_FADE_CROSSFADE does not seem to be supported until URP 14, which requires Unity 2022.2.
     
  10. sewy

    sewy

    Joined:
    Oct 11, 2015
    Posts:
    150
    As I was saying, i got the -1 -> 0 from Unity code. Trial and error and some shader printing values confirmed it for me for default pipeline.