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

Question How to make additional lights cast shadows? (Toon shader setup)

Discussion in 'Shader Graph' started by BartiriX, Oct 9, 2019.

  1. BartiriX

    BartiriX

    Joined:
    Dec 12, 2016
    Posts:
    4
    Hi there!

    Im trying to make a toon shader and Im currently using the recent Unite 2019 talk about that topic and the blog post mentioned in that video as resources for code and learning to understand shaders. This is my very first shader, and while I'm getting a long ways, I'm stuck at an issue I just can't figure out or find anything about.

    For the shader, I simulate lambertian lighting. For the main light, there's a special piece of code that I don't really understand that helps get the light and allows objects to cast shadows. Its straight from the video, but here it is anyway:

    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.     #if SHADOWS_SCREEN
    10.         half4 clipPos = TransformWorldToHClip(WorldPos);
    11.         half4 shadowCoord = ComputeScreenPos(clipPos);
    12.     #else
    13.         half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    14.     #endif
    15.         Light mainLight = GetMainLight(shadowCoord);
    16.         Direction = mainLight.direction;
    17.         Color = mainLight.color;
    18.         DistanceAtten = mainLight.distanceAttenuation;
    19.     #if !defined(_MAIN_LIGHT_SHADOWS) || defined(_RECEIVE_SHADOWS_OFF)
    20.         ShadowAtten = 1.0h;
    21.     #endif
    22.    
    23.     #if SHADOWS_SCREEN
    24.         ShadowAtten = SampleScreenSpaceShadowmap(shadowCoord);
    25.     #else
    26.         ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    27.         half shadowStrength = GetMainLightShadowStrength();
    28.         ShadowAtten = SampleShadowmap(shadowCoord, TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowSamplingData, shadowStrength, false);
    29.     #endif    
    30. #endif
    31. }
    I've tried reverse engineering it and looking in the lighting.hlsl code to figure out how I can also allow my additional lights to cast shadows. I've got them working to the point where they light up objects with their own color, but I can't figure out how objects can also stop the light from hitting other objects that they are obscuring (or rather, the parts of it that they're obscuring).

    Does anyone know how to do this?
    Currently, the code I'm using looks like this:
    Code (CSharp):
    1. void AdditionalLights_half(half3 WorldPosition, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
    2. {
    3.  
    4.   half3 diffuseColor = 0;
    5.    half3 specularColor = 0;
    6.    Color = 0;
    7.    half shadowAtten = 0;
    8.    half distanceAtten = 0;
    9.    Direction = 0;
    10.  
    11. #ifndef SHADERGRAPH_PREVIEW
    12.    int pixelLightCount = GetAdditionalLightsCount();
    13.    for (int i = 0; i < pixelLightCount; ++i)
    14.    {
    15.        Light light = GetAdditionalLight(i, WorldPosition);
    16.        Color = light.color;
    17.        distanceAtten = light.distanceAttenuation;
    18.        shadowAtten = light.shadowAttenuation;
    19.         Direction = light.direction;
    20.    }
    21.     #endif
    22.    
    23.     ShadowAtten = shadowAtten;
    24.     DistanceAtten = distanceAtten;
    25. }
    And the nodes that set up the lighting:



    If there's any other info I need to provide, I'll gladly do so to the best of my ability.

    Thanks in advance!
     
  2. whoisj

    whoisj

    Joined:
    Jan 4, 2018
    Posts:
    26
    cirocontinisio and BartiriX like this.
  3. BartiriX

    BartiriX

    Joined:
    Dec 12, 2016
    Posts:
    4
    I'll have a look at it tonight, sounds promising.
     
    whoisj likes this.
  4. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    334
    i have shader graph challenge i hope you can give me some helpful tips..
    My goal is to simulate same old effect of shadow mask in old unity versions, i want show real time shadows on close distance (like 10 meters away from camera) then fades away to show baked maps for far static opaque gameobjects..

    Challenges i faced:

    1- i have already custom functions (using some unity github resources) of showing real time toon shadows on unlit shader, but as soon as i bake shadow maps, something happens to the light data , they appear black!

    2- i have no idea how to read or obtain baked shadow maps through custom script, do you know the hlsl function for it?? it's hard to find documentations about this stuff.. i know shader graph PBR master node handle this stuff for you for baked shadows.. but it kills real time shadows for static objects nearby..which results in the characters inside shadows not receiving shadows..
     
  5. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
    I saw ur PR, since you know a little about custom functions can you help me out with this:

    https://forum.unity.com/threads/sampler-parameter-must-come-from-a-literal-expression.795855/
     
  6. larsbertram1

    larsbertram1

    Joined:
    Oct 7, 2008
    Posts:
    6,898
  7. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
  8. larsbertram1

    larsbertram1

    Joined:
    Oct 7, 2008
    Posts:
    6,898
    in order get an idea of how lwrp/urp passes light information to the shaders you could have a look into the lighting.hlsl file. GetMainLight() and GetAdditionalLight() are the basic functions here which will provide you with the direction, color, distance attenuation and shadow attenuation of each light.
    picking up this per light data will let you implement any custom lighting.
     
  9. larsbertram1

    larsbertram1

    Joined:
    Oct 7, 2008
    Posts:
    6,898
    your custom function simply loops over all additional lights and – only returns the values for the last light.
    in order to do lighting properly you will have to do lighting within the loop, sum it up and return the final result instead.


    half3 MyCustomLightingFunction(Light light, half3 normalWS) {
    halft dotNL = saturate(dot(normalWS, Light.direction);
    return Light.color * Light.distanceAttenuation * Light.shadowAttenuation * dotNL;
    }

    void AdditionalLights_half(float3 WorldPosition, half3 surfaceNormalInWorldSpace, out half3 additionLight)
    {
    hafl3 lightingFromAdditionalLights = half3(0,0,0);
    int pixelLightCount = GetAdditionalLightsCount();
    for (int i = 0; i < pixelLightCount; ++i) {
    Light light = GetAdditionalLight(i, WorldPosition);
    lightingFromAdditionalLights += MyCustomLightingFunction(light, surfaceNormalInWorldSpace);
    }
    additionLight = lightingFromAdditionalLights;
    }

    // additionLight has to be multiplied with the diffuse color
     
  10. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
    Do you know how to get input data like vertex normal, position, view and so on from code without passing node parameters?
    The `Lighting.hlsl` lighting functions use InputData struct defined in `Input.hlsl` but I can't see how to use them to get data directly.
     
  11. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
    Did that actually run for you?
    Because whenever I write a function (whether internal or exposed in custom node) that takes the `Light` struct as a parameter I get errors (I think undeclared variable) .
     
  12. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
    Ah, I used to just ADD the lighting from the Main Light with Addition Lights not Multiply them.

    Here is my version:

    Code (CSharp):
    1. void DiffSpec_half(half3 N, half3 V, float3 WorldPos, half3 SpecColor, half Smoothness,  out half3 Diffuse, out half3 Specular)
    2. {    
    3.     Diffuse = 0;
    4.     Specular = 0;
    5. #ifndef SHADERGRAPH_PREVIEW  
    6.    
    7.     Light l = GetMainLight(); // GET MAIN LIGHT
    8.     half3 attenColor = l.color * l.distanceAttenuation * l.shadowAttenuation;
    9.  
    10.     Diffuse += Lambert_half(attenColor, l.direction, N);
    11.     Specular += BlinnPhong_half(attenColor, l.direction, N, V, SpecColor, Smoothness);  
    12.     int pixelLightCount = GetAdditionalLightsCount();
    13.     for (int i = 0; i < pixelLightCount; ++i)
    14.     {
    15.         Light l = GetAdditionalLight(i, WorldPos); // GET ADDT LIGHTS
    16.         half3 attenColor = l.color * l.distanceAttenuation * l.shadowAttenuation;
    17.  
    18.         Diffuse += Lambert_half(attenColor, l.direction, N);
    19.         Specular += BlinnPhong_half(attenColor, l.direction, N, V, SpecColor, Smoothness);
    20.     }
    21. #endif
    22. }
     
  13. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    632
    I you wrote the Medium article I would like to see some code please.