Search Unity

Cartoon Shader smooth highlights/shadows

Discussion in 'Shaders' started by f3nnec, Nov 28, 2015.

  1. f3nnec

    f3nnec

    Joined:
    Nov 28, 2015
    Posts:
    1
    Hi,
    I'pretty new to unity shaders and a bit lost.
    I found a toon shader script on https://en.wikibooks.org/wiki/Cg_Programming/Unity/Toon_Shading which i'm pretty happy with exept for 2 things:

    - there is no texture
    - lights intersection are straight/linear (between outlines/shadow/specular)

    I managed to add a texture slot without troubles but i'm working on it since few days now without being able to deal the "smooth transition" between lights...

    They talk about "smoothstep" at bottom of the wiki's link....
    Does it work with "vertex shader" ?

    This is what i have right now...
    one.jpg

    This is what i'm looking for... (blurry/smooth steps)
    two.jpg

    Here is the code:
    Code (CSharp):
    1. Shader "Cg shader for toon shading" {
    2.    Properties {
    3.       _Color ("Diffuse Color", Color) = (1,1,1,1)
    4.       _UnlitColor ("Unlit Diffuse Color", Color) = (0.5,0.5,0.5,1)
    5.       _DiffuseThreshold ("Threshold for Diffuse Colors", Range(0,1)) = 0.1
    6.       _OutlineColor ("Outline Color", Color) = (0,0,0,1)
    7.       _LitOutlineThickness ("Lit Outline Thickness", Range(0,1)) = 0.1
    8.       _UnlitOutlineThickness ("Unlit Outline Thickness", Range(0,1)) = 0.4
    9.       _SpecColor ("Specular Color", Color) = (1,1,1,1)
    10.       _Shininess ("Shininess", Float) = 10
    11.       _MainTex ("Main Texture", 2D) = "white" {}
    12.      
    13.      
    14.      
    15.      
    16.      
    17.    }
    18.    SubShader {
    19.       Pass {    
    20.          Tags { "LightMode" = "ForwardBase" }
    21.             // pass for ambient light and first light source
    22.          CGPROGRAM
    23.          #pragma vertex vert
    24.          #pragma fragment frag
    25.          #include "UnityCG.cginc"
    26.          uniform float4 _LightColor0;
    27.             // color of light source (from "Lighting.cginc")
    28.          // User-specified properties
    29.          uniform float4 _Color;
    30.          uniform float4 _UnlitColor;
    31.          uniform float _DiffuseThreshold;
    32.          uniform float4 _OutlineColor;
    33.          uniform float _LitOutlineThickness;
    34.          uniform float _UnlitOutlineThickness;
    35.          uniform float4 _SpecColor;
    36.          uniform float _Shininess;
    37.          uniform sampler2D _MainTex;
    38.        
    39.        
    40.        
    41.        
    42.          struct vertexInput {
    43.             float4 vertex : POSITION;
    44.             float3 normal : NORMAL;
    45.             float4 texcoord : TEXCOORD0;
    46.            
    47.          };
    48.          struct vertexOutput {
    49.             float4 pos : SV_POSITION;
    50.             float4 posWorld : TEXCOORD1;
    51.             float3 normalDir : TEXCOORD2;
    52.             float2 uv : TEXCOORD0;
    53.            
    54.          };
    55.          vertexOutput vert(vertexInput input)
    56.          {
    57.             vertexOutput output;
    58.             float4x4 modelMatrix = _Object2World;
    59.             float4x4 modelMatrixInverse = _World2Object;
    60.            
    61.              output.posWorld = mul(modelMatrix, input.vertex);
    62.             output.normalDir = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
    63.             output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    64.            
    65.             output.uv =input.texcoord;
    66.            
    67.             return output;
    68.          }
    69.          float4 frag(vertexOutput input) : COLOR
    70.          {
    71.             float3 normalDirection = normalize(input.normalDir);
    72.             float3 viewDirection = normalize(_WorldSpaceCameraPos - input.posWorld.xyz);
    73.             float3 lightDirection;
    74.             float attenuation;
    75.             if (0.0 == _WorldSpaceLightPos0.w) // directional light?
    76.             {
    77.                attenuation = 1.0; // no attenuation
    78.                lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    79.             }
    80.             else // point or spot light
    81.             {
    82.                float3 vertexToLightSource = _WorldSpaceLightPos0.xyz - input.posWorld.xyz;
    83.                float distance = length(vertexToLightSource);
    84.                attenuation = 1.0 / distance; // linear attenuation
    85.                lightDirection = normalize(vertexToLightSource);
    86.             }
    87.   ///////////////////////////////////////////////////////////////////////////
    88.  
    89.             // default: unlit
    90.            
    91.             float3 fragmentColor = attenuation * _UnlitColor.rgb;
    92.             // low priority: diffuse illumination
    93.             if (attenuation * max(0.0, dot(normalDirection, lightDirection)) >= _DiffuseThreshold)
    94.             {
    95.                fragmentColor = _LightColor0.rgb * _Color.rgb;
    96.             }
    97.             // higher priority: outline
    98.             if (dot(viewDirection, normalDirection) < lerp(_UnlitOutlineThickness, _LitOutlineThickness, max(0.0, dot(normalDirection, lightDirection))))
    99.             {
    100.                fragmentColor = _LightColor0.rgb * _OutlineColor.rgb;
    101.             }
    102.             // highest priority: highlights
    103.          
    104.             if (dot(normalDirection, lightDirection) > 0.0 && attenuation *  pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), _Shininess) > 0.5)
    105.                // more than half highlight intensity?
    106.             {
    107.                fragmentColor = _SpecColor.a * _LightColor0.rgb * _SpecColor.rgb + (1.0 - _SpecColor.a) * fragmentColor;
    108.             }
    109.            
    110.  
    111.            
    112.             return float4(fragmentColor, 1.0) * tex2D(_MainTex, input.uv);
    113.            
    114.            
    115.          
    116.          }
    117.          ENDCG
    118.       }
    119.      
    Many thanks in advance for the help.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Smoothstep is a function built into CG (the shader language Unity uses), but their comment at the end doesn't really help since the way to use it in their case is not straight forward. Basically the reason for the harsh transitions in that shader are because they're using if statements instead of something that can be easily blended.

    Here's a basic example of what they're doing:

    color = unlitSurfaceColor;
    if (dot(lightDir, surfaceNormal) > 0.5)
    color = lightColor * surfaceColor;


    Here's a basic example of what you need to do instead to support smooth transitions:

    _DiffuseTransition ("Diffuse Transition", Range(0.001, 1.0)) = 0.1
    ...
    float ndotl = dot(lightDir, surfaceNormal);
    float diffuseBlend = smoothstep(0.0, 1.0, (ndotl * 0.5) / _DiffuseTransition + 0.5);
    color = lerp(unlitSurfaceColor, lightColor * surfaceColor, diffuseBlend);


    A diffuse transition value of 1 is a half lambert, 0.001 will be almost as sharp as the original code... assuming I didn't mess up the math.