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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

surface shaders, multiple lights, and final color

Discussion in 'Shaders' started by Yarharhar, Jul 14, 2012.

  1. Yarharhar

    Yarharhar

    Joined:
    Jul 14, 2012
    Posts:
    4
    I'm writing a surface shader that does a toon-shading effect for an object. Essentially, in the finalcolor function for the renderpass, I look up the 'brightness' of a pixel, and from there limit it to a few different values.

    This generally works, though I've noticed that with multiple lights (directional, in this case), I start getting weird artifacts; namely pixel brightnesses that shouldn't be able to get generated.

    My working theory right now is that Unity combines multiple lights in surface shaders in some way that happens *after* the final color has been applied, (which is why these invalid brightness values are getting used).


    Really, I'm just asking how surface shaders handle multiple lights, since that whole concept is abstracted out for the process of writing them.


    Edit: I figured this out. Documenting it here for someone who runs into the same situation later.

    Essentially, the problem occurs when you use a finalColor function in the forward-additive pass (which causes all the weird additive artifacts). The solution is in your finalColor function to wrap the code in:
    #ifndef UNITY_PASS_FORWARDADD

    #endif
    to prevent it from running
     
    Last edited: Nov 18, 2012
    shelim likes this.
  2. hausmaus

    hausmaus

    Joined:
    Dec 9, 2011
    Posts:
    105
    I just tried a quick demo of a finalcolor toon effect and it appears to work as expected. Of course, what I did is very primitive / silly and may not demonstrate the problem you are having. Here is the shader:

    Code (csharp):
    1. Shader "AE/Toon" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _Base    ("Base", Range(0,1)) = 0
    5.         _Boost   ("Boost", Range(1,8)) = 4
    6.         _Edge    ("Edge", Range(1,96)) = 32
    7.     }
    8.     SubShader {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 200
    11.        
    12.         CGPROGRAM
    13.         #pragma surface surf Lambert finalcolor:tooncolor
    14.  
    15.         sampler2D _MainTex;
    16.         float _Base, _Boost, _Edge;
    17.  
    18.         struct Input {
    19.             float2 uv_MainTex;
    20.         };
    21.        
    22.         void tooncolor (Input IN, SurfaceOutput o, inout fixed4 color)
    23.         {
    24.             color.r = ( color.r * _Base ) + pow( _Boost * color.r, _Edge );
    25.             color.b = ( color.b * _Base ) + pow( _Boost * color.b, _Edge );
    26.             color.g = ( color.g * _Base ) + pow( _Boost * color.g, _Edge );
    27.         }
    28.        
    29.         void surf (Input IN, inout SurfaceOutput o) {
    30.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    31.             o.Albedo = c.rgb;
    32.             o.Alpha = c.a;
    33.         }
    34.         ENDCG
    35.     }
    36.     FallBack "Diffuse"
    37. }

    With all sliders set to 0, you should get the effect of the default diffuse shader. With other values, you should get a somewhat convincing toon effect, regardless of the number of directional lights hitting the object. For example:



    Of course, when going through finalcolor, you are able to get an interesting effect, but probably never a true "toon" or cell-shaded effect since you will always have some other shading model's lighting output on the surface to work around. You can see the toon lighting models that Unity ships with in the Standard Assets folder if you import them into your project. They occur earlier in the pipeline of course and have a more predictable effect.

    The only thing I can think of is that you might be overdriving channels or have a small math error in the finalcolor function. Can you please post your shader?

    -abm
     
  3. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    Thanks Yarharhar! You fixed my issue for me! I was making a vertex coloured shader, trying to turn off surface lighting but maintaining attenuation. I'm not *quite* there, but the final color tint in the additive shadow pass was certainly one of my issues!