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. Have a look at our Games Focus blog post series which will show what Unity is doing for all game developers – now, next year, and in the future.
    Dismiss Notice

[SOLVED] CrossFading Lod Implementation

Discussion in 'Shaders' started by flogelz, Aug 12, 2018.

  1. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    Does somebody know how to implement Crossfading in Unitys Standard Shader?

    I already got so far to include LOD_FADE_CROSSFADE in the main shader file, but I'm really not sure about the implementation in the cgincs afterwards. (Also the SpeedTree versions looks quite different, so i can't simply transfer the data over to the standard shader)

    Does somebody has an idea?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,818
    In the other thread I posted a link to Keijiro’s example shaders.
    https://github.com/keijiro/CrossFadingLod

    This uses surface shaders, which is the recommended way to make custom shaders that use the Standard shading model. Modifying the Standard shader directly is a huge pain that usually requires modifying multiple cginc files to do fairly basic things, including cross fading.

    The funny thing is those examples are more complex than they need to be as Unity’s Surface Shaders now support cross fade dithering as a feature that can be enabled by adding a single keyword to the #pragma surface line. See the documentation here:
    https://docs.unity3d.com/Manual/SL-SurfaceShaders.html

    As for what’s actually needed to do dithered crossfading in a shader, see this catlike coding tutorial on the topic (its at the bottom of the page):
    https://catlikecoding.com/unity/tutorials/rendering/part-18/

    To get the standard shader to support dithered crossfading means needing to figure out how to get the per pixel screen position, and somewhere in the fragment shader call the function referenced in both the Keijiro example and the Catlike Coding tutorial. The simpliest solution might be to create a copy of the standard shader and the cginc file that has the first level of the fragment shader in it and add the function there. Exactly how to do this requires some decent understanding of shaders, and reading through the many include files. There’s no tutorial out there on how to go about this as really it’d be more complex than just reading the files yourself.
     
    grobonom and laurentlavigne like this.
  3. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    I got it! Thanks for all the help!! I also read the catlikecoding article before, but didn't understood a step correctly.
    But it works now! I'm not sure if it's the most optimized version, because the struct lines for the vertex program get duplicated for the fragment program, like catlikecoding explained in the article but it works alteast.

    Ok so here's a what you have to do:

    -> Download the shaders from the unity website, make a copy of the Standard.shader, UnityStandardCoreForward.cginc and the UnityStandardCore.cginc (I would suggest to rename them, atleast during the testing stage. If you rename them, remember to change the references in these files as well. The order, in which they, reference each other is this one: Standard.shader references UnityStandardCoreForward, which references UnityStandardCore) All the main changes we do get done in the UnityStandardCore.

    -> Then put #pragma multi_compile _ LOD_FADE_CROSSFADE into every Pass of the Standard.shader just above the other #pragma multi_compile (except in the Meta Passes)

    -> Then go into the UnityStandardCore.cginc (to open them, just drag them into VisualStudio) and copy the struct VertexOutputForwardBase completly and paste it in right after, so that you basically have the same struct two times. Now change the name of the duplicate to FragmentOutputForwardBase (just for good readability) and replace float4 pos : SV_POSITION with this code:

    Code (CSharp):
    1. #if defined(LOD_FADE_CROSSFADE)
    2.     UNITY_VPOS_TYPE vpos : VPOS;
    3. #else
    4.     float4 pos : SV_POSITION;
    5. #endif
    (The float4 pos is still in there, just in an if statement)

    ->Then go to half4 fragForwardBaseInternal (VertexOutputForwardBase i) and replace the VertexOutputForwardBase with our FragmentOutputForwardBase. After that, paste in these lines of code right at the top inside the half4 fragForwardBaseInternal (VertexOutputForwardBase i) function:

    Code (CSharp):
    1. #if defined(LOD_FADE_CROSSFADE)
    2.     ApplyDitherCrossFade(i.vpos);
    3. #endif

    And actually that was it! I hope this helps somebody and I'm also gonna upload my version here (I used the shader variants of Unity 5.6.3p1, just keep that in mind.)

    If you really want to understand what's happening here, checkt out the two links from bgolus, especially the catlikecoding Tutorial!
     

    Attached Files:

  4. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    After some testing i noticed some problems though, which I still have to look into:

    1. On some Meshes, transitions make during the transitiontime both objects transparent, which then results in dither that shows the objects or background behind the objects. This isnt a problem on the normal Unity Sphere though (so maybe it has todo with my meshes.)
    2. I still need to add support for shadow dither, when having realtime objects that produce shadows. (Its basically the same as the first implementation, just in codelines where the shadow gets produced. See catlikecoding for reference.)
     
  5. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    So after another week testing code i finally got it working. Finally!

    For the shadows: Make a duplicate of the UnityStandardShadow.cginc and make the references to this file from the main shader, like we did with the UnityStandardCore. Then search for the half4 fragShadowCaster and add the Crossfade part like this into the brackets:

    Code (CSharp):
    1. #if defined(UNITY_STANDARD_USE_DITHER_MASK) || defined(LOD_FADE_CROSSFADE) UNITY_VPOS_TYPE vpos : VPOS #endif
    (So we basically just add our Crossfade to the already existing method for the VPos.) And after that just paste this line again into the function:

    Code (CSharp):
    1.     #if defined(LOD_FADE_CROSSFADE)
    2.         UnityApplyDitherCrossFade(vpos);
    3.     #endif
    (You notice that i now write UnityApplyDitherCrossFade rather than ApplyDitherCrossFade. Thats important for the next step and you should go into the UnityStandardCore file and change this there too. I only discovered this recently, thats why im changing it now but I'll explain in the second post.)
     
    Last edited: Sep 1, 2018
  6. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    So why did I write ApplyDitherCrossFade at first? Thats because I was using Unity 5.6 and in the shaders of this version, the function for crossfading was different from the most recent approaches to crossfading. The problem with the transparent artifacts on the whole mesh also come from this older method. So you probably dont need to do this step, if you are working with recent Unity versions, but atleast in the older shaders you have to change it.

    The crossfade function is located in another cginc file, the UnityCG.cginc. Scroll almost to the end of the file to find a section about crossfading and use this code to replace all of it:

    Code (CSharp):
    1. //  LOD cross fade helpers
    2. #define UNITY_DITHER_CROSSFADE_COORDS
    3. #define UNITY_DITHER_CROSSFADE_COORDS_IDX(idx)
    4. #define UNITY_TRANSFER_DITHER_CROSSFADE(o,v)
    5. #define UNITY_TRANSFER_DITHER_CROSSFADE_HPOS(o,hpos)
    6.  
    7. #ifdef LOD_FADE_CROSSFADE
    8. #define UNITY_APPLY_DITHER_CROSSFADE(vpos)  UnityApplyDitherCrossFade(vpos)
    9.     sampler2D _DitherMaskLOD2D;
    10.     void UnityApplyDitherCrossFade(float2 vpos)
    11.     {
    12.         vpos /= 4; // the dither mask texture is 4x4
    13.         vpos.y = frac(vpos.y) * 0.0625 /* 1/16 */ + unity_LODFade.y; // quantized lod fade by 16 levels
    14.         clip(tex2D(_DitherMaskLOD2D, vpos).a - 0.5);
    15.     }
    16. #else
    17. #define UNITY_APPLY_DITHER_CROSSFADE(vpos)
    18. #endif
    Also change the references in the other files of your custom shader, so that the use this instead of the default code.

    It seems like the Unity Devs changed their method of calculating crossfade over the years (this code is from the same cginc just from the 2018.1 version) and this new method also works inside of older shader variants, like the ones from 5.6. Now if you've done all this, you should have a standard shader that supports crossfading! A rather complicated journey for such a feature, but it was worth it! I really learned a lot about shaders this way.

    If you have any questions about this, just leave a comment or write a message~
     
    grobonom likes this.
  7. HenrikoMagnifico

    HenrikoMagnifico

    Joined:
    Nov 25, 2014
    Posts:
    4
    Hey! Sorry to bother you so long after this post was made, but I'm having issues with crossfading in my game! I have never used custom shaders before so I'm really confused about a lot of this. From what I understand; I am able to replace the code in the Unity default shader code for crossfading in UnityCG.gcinc with your code above and it should work? Will that mess stuff up? Sorry I'm a total noob at shaders. Thanks!
     
  8. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    130
    Actually since Unity 2018 you can just copy the standard shader main file, get in there and remove the two Lines before LOD_FADE_CROSSFADE, which comment them out. They added the right support since this version, so no need to change anything else like i did before!
     
    DarkTree and joramvanloenen like this.
unityunity