Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug (SOLVED) (2019.4.2f1/2020.2.5f1, URP/SG 7.4.1) Shadows look different in build than in editor

Discussion in 'Shader Graph' started by melos_han_tani, Feb 21, 2021.

  1. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    Edit - on a whim I decided to try upgrading to 2020.2.5f1 - nothing changes.

    Hi all, I use custom lighting for all my shaders in this game. I'm trying to figure out why when I make a build, I get these weird shadows appearing (see 2nd image).

    They appear in multiple scenes, and always tend to look like big squares with tiny gaps in between them. As far as I can tell there's nothing in my game that would cast those shadows, and they always seem to be to the world-forward of my camera.


    This is the correct shadows (Editor)
    shadows_playmode.png

    Incorrect shadows (BUILD)
    shadows_build.png

    My guess is something weird is happening with the shadows in my cel shader, but I don't get why it would ONLY happen in the build. Here's the code I'm using to output shadow attenuation in my cel shaders

    Code (CSharp):
    1.  
    2.  
    3. void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
    4. {
    5. #if SHADERGRAPH_PREVIEW
    6.     Direction = half3(0.5,0.5,0);
    7.     Color = 1;
    8.     DistanceAtten = 1;
    9.     ShadowAtten = 1;
    10. #else
    11.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    12.     Light mainLight = GetMainLight(shadowCoord);
    13.     Direction = mainLight.direction;
    14.     Color = mainLight.color;
    15.     DistanceAtten = mainLight.distanceAttenuation;
    16.  
    17.     //ShadowAtten = mainLight.shadowAttenuation;
    18.  
    19.     // too blocky?
    20.     ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    21.     half shadowStrength = GetMainLightShadowStrength();
    22.     ShadowAtten = SampleShadowmap(shadowCoord, TEXTURE2D_ARGS(_MainLightShadowmapTexture,
    23.             sampler_MainLightShadowmapTexture),
    24.             shadowSamplingData, shadowStrength, false);
    25. #endif
    26. }
    27.  
    Here's another example of the incorrect shadows in another area. It sort of looks like a shadowmap of the level projected incorrectly?

    upload_2021-2-21_14-20-7.png



    And here's my URP Asset parameters

    upload_2021-2-21_14-15-11.png

    Forward Renderer

    upload_2021-2-21_14-15-25.png
     
    Last edited: Feb 22, 2021
  2. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    Worth mentioning I don't use light baking or whatever.

    Also, there are a few errors in the Shadergraph Shader, but they've never caused ill effects in the editor

    upload_2021-2-21_14-23-46.png
    upload_2021-2-21_14-24-0.png


    There's a few keywords I enable on the shader graph too. Although the only ones I specified manually are _MAIN_LIGHT_SHADOWS_CASCADE and _SHADOWS_SOFT

    upload_2021-2-21_14-27-26.png

    upload_2021-2-21_14-27-56.png
     
  3. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,296
    I do not have any solution, but I am using kind of similar setup and I don't have errors in my shaders. There are these truncation warnings, but you have TEXCOORD error, this is wild guess, but maybe you included some custom nodes multiple times and something duplicated? I think in manual examples they wrap code in #ifdef to avoid this.
    Also did you check that your render features do not break your project?
     
    melos_han_tani likes this.
  4. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    Thank you! The include thing might be the issue - I did not know that's how custom nodes work (so any custom nodes should sit inside a subgraph, moreover I should wrap the custom code it in an #ifndef so that a function never gets defined twice in a given shader?)

    Render features are possible, but removing HBAO didn't do anything and I would hope the other one wouldn't break something (but worth a try if the include stuff doesn't work)
     
  5. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    Okay, that seemed to not do anything, but maybe I'm doing something wrong. I'm just going to copy paste my entire custom lighting HLSL file, after I added the #define per the manual instructions. I also tried removing my renderer features but that didn't do anything. I don't have those texcoord errors in the shader anymore, though.

    Every time I use a custom function, it is through a subgraph (if that matters)

    Currently stuck looking through this thread: https://forum.unity.com/threads/shadow-cascades-weird-since-7-2-0.828453/#post-6863054 Maybe something in there will work...?

    Code (CSharp):
    1.  
    2. #ifndef ANALGESIC_LIGHTING_INCLUDED
    3. #define ANALGESIC_LIGHTING_INCLUDED
    4.  
    5. void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
    6. {
    7. #if SHADERGRAPH_PREVIEW
    8.     Direction = half3(0.5,0.5,0);
    9.     Color = 1;
    10.     DistanceAtten = 1;
    11.     ShadowAtten = 1;
    12. #else
    13.  
    14.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    15.     Light mainLight = GetMainLight(shadowCoord);  
    16.     Direction = mainLight.direction;
    17.     Color = mainLight.color;
    18.     DistanceAtten = mainLight.distanceAttenuation;
    19.    
    20.     //ShadowAtten = mainLight.shadowAttenuation;
    21.     //ShadowAtten = MainLightRealtimeShadow(shadowCoord);
    22.  
    23.     ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    24.     half4 shadowParams = GetMainLightShadowParams();
    25.     ShadowAtten = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
    26.    
    27.     // too blocky?
    28.     // ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    29.     // half shadowStrength = GetMainLightShadowStrength();
    30.     // ShadowAtten = SampleShadowmap(shadowCoord, TEXTURE2D_ARGS(_MainLightShadowmapTexture,
    31.     //        sampler_MainLightShadowmapTexture),
    32.     //        shadowSamplingData, shadowStrength, false);
    33. #endif
    34. }
    35.  
    36.  
    37. void MainLightDirectionOnly_half(float3 WorldPos, out half3 Direction)
    38. {
    39. #if SHADERGRAPH_PREVIEW
    40.     Direction = half3(0.5,0.5,0);
    41. #else
    42.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    43.     Light mainLight = GetMainLight(shadowCoord);  
    44.     Direction = mainLight.direction;
    45. #endif
    46. }
    47.  
    48. //half3 MyCustomLightingFunction(Light light, half3 normalWS) {
    49.     //halft dotNL = saturate(dot(normalWS, Light.direction);
    50.     //return Light.color * Light.distanceAttenuation * Light.shadowAttenuation * dotNL;
    51. //}
    52.  
    53. float invLerp(float from, float to, float value) {
    54.      return (value - from) / (to - from);
    55. }
    56.  
    57. void AdditionalLights_half(float3 WorldPosition, float fadeStart, float fadeEnd, float3 normal, out half3 AdditionalLight, out half maxNDotL, out half disAtten)
    58. {
    59.     half3 lightingFromAdditionalLights = half3(0,0,0);
    60.     #if SHADERGRAPH_PREVIEW
    61.     AdditionalLight = half3(0,0,0);
    62.     maxNDotL = 0;
    63.     disAtten = 0;
    64.     #else
    65.     int pixelLightCount = GetAdditionalLightsCount();
    66.     pixelLightCount = min(pixelLightCount,4);
    67.     for (int i = 0; i < pixelLightCount; i++) {
    68.         Light light = GetAdditionalLight(i, WorldPosition);
    69.         lightingFromAdditionalLights += light.color;
    70.         half nDotL = dot(normal,light.direction);
    71.         nDotL = invLerp(-0.1,0.1,nDotL);
    72.         nDotL = saturate(lerp(0,1,nDotL));
    73.         float thisDisAtten = clamp(0,1,light.distanceAttenuation*1000);
    74.         maxNDotL = max(nDotL,maxNDotL);
    75.         disAtten = max(disAtten, thisDisAtten);
    76.     }
    77.     //maxNDotL = 1;
    78.     maxNDotL *= disAtten;
    79.     AdditionalLight = lightingFromAdditionalLights;
    80.     #endif
    81. }
    82. #endif
     
  6. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    After some messing around, I fixed it!

    So I have 4 cel shaders for slightly different use cases.

    The solution: In EACH cel shader, I have to define global, multi-compile boolean keywords

    "_MAIN_LIGHT_SHADOWS" and "_MAIN_LIGHT_SHADOWS_CASCADE" . Otherwise shadows just don't work.

    On top of this, I think it's necessary for me to use the MainLightRealtimeShadow function. Here's the tricky part: over the past year figuring this stuff out, I found solutions that told me to copy-paste the code inside of MainLightRealtimeShadow, hence my code in previous posts.

    I think what was happening was, because that code was being copied out of the function, it was being pasted into some place without the correct defines or accessible variables or something. I'm not sure, working in HLSL is honestly incomprehensible because there seem to be like 100 accessible magic variables and functions at any given time and my IDE doesn't help with autocomplete.

    This, combined with me having keywords not set to the magic values of "MULTI_COMPILE" and "GLOBAL" led to some insane cascade bug but only in the build process.

    Code (CSharp):
    1. void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
    2. {
    3. #if SHADERGRAPH_PREVIEW
    4.     Direction = half3(0.5,0.5,0);
    5.     Color = 1;
    6.     DistanceAtten = 1;
    7.     ShadowAtten = 1;
    8. #else
    9.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    10.     Light mainLight = GetMainLight(shadowCoord);
    11.     Direction = mainLight.direction;
    12.     Color = mainLight.color;
    13.     DistanceAtten = mainLight.distanceAttenuation;
    14.  
    15.     ShadowAtten = MainLightRealtimeShadow(shadowCoord);
    16.  
    17. #endif
    18. }
    19.  
    Anyways... I don't know who writes the docs, but the definitions of "Global/Local" and "Multi compile', etc... are very confusing to someone who has not been writing HLSL for years.

    Moreover, writing custom shader code is confusing as well because of all the stuff that's 'magically there'... and there's no real good way to teach myself this. It's not even a matter of 'okay sure, I can use these HLSL functions Unity made, and that makes sense', but logically I don't understand why/how they're even accessible in the first place.

    I forgot to mention that this is all for an Unlit Master Node using SG 7.4.1.This reminded me of this thread I found a year ago or so, where you DO have to define (in EACH shader) those two keywords I mentioned, set to Multi Compile and Global. (Not 100% sure on multi compile but it seems to make shadows disappear in builds if I don't have it set to Multi Compile).

    https://forum.unity.com/threads/sha...uation-do-not-work-in-urp-unlit-graph.803025/

    It's possible that these keywords are now autodefined or something in later versions of Shader Graph, but I'm not sure.
     
    Last edited: Feb 23, 2021
    Qriva likes this.
  7. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    ..Also I did keep that "#ifndef MYKEYWORD #define KEYWORD" code . I don't know if that fixed anything or not but it didn't break anything, so I'm not going to bother to test it
     
  8. Hysparions

    Hysparions

    Joined:
    Jan 7, 2019
    Posts:
    29
    Thank you so much for your tip ! I was getting the same problem and it was the mainlight shadow keyword that was set to local in my shader. Instead of using the keyword in every shader tough, you could create a subshader graph with the custom function and the keywords and the reuse this sub shader in your variants. I did this on my project to avoid a mess with 15+ shader to change and possibility of forgetting this S*** for next ones.
     
  9. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    thanks! do you have an example of the custom function's code? i always have trouble knowing if those are compiling right because there's no documentation anywhere (as is usual lol)
     
  10. Hysparions

    Hysparions

    Joined:
    Jan 7, 2019
    Posts:
    29
    Hello @melos_han_tani This is my current code to retrieve the light and shadow. Note that I added a Distance Attenuation parameter to smooth out distant shadows to avoid black shadows apearing suddenly on the background. Use this in ShaderGraph to make your transition seamless with zones farther than shadow distance by decreasing shadow strength linearly instead of abruptly

    Code (CSharp):
    1. void MainLight_float(float3 WorldPos, float ShadowDistance, out float3 Direction, out float3 Color, out float DistanceAtten, out float ShadowAtten)
    2. {
    3.     #ifdef SHADERGRAPH_PREVIEW
    4.         Direction = float3(0.5, 0.5, 0);
    5.         Color = 1;
    6.         DistanceAtten = 1;
    7.         ShadowAtten = 1;
    8.     #else
    9.      
    10.     float4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    11.     half4 shadowMask = half4(1, 1, 1, 1);
    12.     Light mainLight = GetMainLight(shadowCoord, WorldPos, shadowMask);
    13.     Direction = mainLight.direction;
    14.     Color = mainLight.color;
    15.  
    16.     float camDist = distance(WorldPos, _WorldSpaceCameraPos);
    17.     DistanceAtten = clamp((ShadowDistance - camDist) / 25, 0, 1);
    18.  
    19.     ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    20.     float shadowStrength = GetMainLightShadowStrength();
    21.     ShadowAtten = SampleShadowmap(shadowCoord, TEXTURE2D_ARGS(_MainLightShadowmapTexture,
    22.         sampler_MainLightShadowmapTexture),
    23.         shadowSamplingData, shadowStrength, false);
    24.  
    25.     #endif
    26. }
     
  11. melos_han_tani

    melos_han_tani

    Joined:
    Jan 11, 2018
    Posts:
    78
    Whoops, I meant to ask more specifically re: this:

    Instead of using the keyword in every shader tough, you could create a subshader graph with the custom function and the keywords and the reuse this sub shader in your variants.

    When you're doing this, do you mean in your light&shadow retrieval subshader graph's blackboard, you're adding the keywords? Or that the keywords are going in the custom function?

    If the former, that's a good idea - never thought to do that. If the latter, I don't know how to do that and am curious to see the code.


     
  12. Hysparions

    Hysparions

    Joined:
    Jan 7, 2019
    Posts:
    29
    Don't need to code anything, Just Create a Sub Shader Graph with Create->Shader->SubShaderGraph
    Then, on the left side panel click on + to add a Boolean Keyword
    You'll have to set 3 of them to make it work :
    Here are the settings you must provide