Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Need help with a 2D Sprite Shader (Shadows, etc)

Discussion in 'Shaders' started by Fellshadow, Feb 12, 2014.

  1. Fellshadow

    Fellshadow

    Joined:
    Dec 2, 2012
    Posts:
    169
    I'm very new to shaders, and I'm having trouble implementing a 2D sprite shader. My game uses 2D graphics in 3D space, and I've messed around with the default shader and was able to implement z buffering as well as alpha-test to make the sprites properly display based on z distance rather than layers.

    Now, I'm trying to add some more complex features to the shader, but I have no idea where to begin.
    What I'm trying to implement to my shader:
    -Casting and receiving shadows
    -normal/depth maps and self shadows (I'm trying to use this shader, although it doesn't have self shadows)
    -And of course using z depth instead of layers

    One more thing that I want to try, if it's possible, is essentially have a "side shadow" for objects. When casting shadows off the sprites, the shadow would look fine as long as the light is behind or infront of the sprite. When the light is to the side of the sprite, however, it will only project a thin line. I was thinking of having each object have a "side sprite" that the shader will use to cast a shadow if the light is to the side of the object, instead of it's current sprite. I'm worried if it's possible or how well the shadow will line up, though.

    Any advice or help would be much appreciated, as shaders are still very confusing for me
     
  2. Fellshadow

    Fellshadow

    Joined:
    Dec 2, 2012
    Posts:
    169
    Well, I'm still trying it on my own, since no is responding here...
    I'm having trouble getting it to work with directional lights. I've attempted editing the shader I linked to in my original post, and here is what I have so far:

    Code (csharp):
    1. // Shader for Unity integration with SpriteLamp
    2. // Written by Steve Karolewics  Indreams Studios
    3. Shader "Custom/shr_SpriteNormalShader"
    4. {
    5.     Properties
    6.     {
    7.         _MainTex ("Diffuse Texture", 2D) = "white" {}
    8.         _Normal ("Normal", 2D) = "bump" {}
    9.         _Depth ("Depth", 2D) = "gray" {}
    10.         _SpecColor ("Specular Material Color", Color) = (1,1,1,1)
    11.         _Shininess ("Shininess", Float) = 10
    12.         _AmplifyDepth ("Amplify Depth", Float) = 1
    13.         _CelShadingLevels ("Cel Shading Levels", Float) = 0
    14.     }
    15.  
    16.     SubShader
    17.     {
    18.    
    19.         ZWrite On
    20.         AlphaTest Greater 0.1
    21.         Pass
    22.         {    
    23.             Tags { "LightMode" = "ForwardBase" }
    24.  
    25.             CGPROGRAM
    26.  
    27.             #pragma vertex vert  
    28.             #pragma fragment frag
    29.             #include "UnityCG.cginc"
    30.  
    31.             // User-specified properties
    32.             uniform sampler2D _MainTex;
    33.  
    34.             struct VertexInput
    35.             {
    36.                 float4 vertex : POSITION;
    37.                 float4 color : COLOR;
    38.                 float4 uv : TEXCOORD0;    
    39.             };
    40.  
    41.             struct VertexOutput
    42.             {
    43.                 float4 pos : POSITION;
    44.                 float4 color : COLOR;
    45.                 float2 uv : TEXCOORD0;
    46.             };
    47.  
    48.             VertexOutput vert(VertexInput input)
    49.             {
    50.                 VertexOutput output;
    51.  
    52.                 output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    53.                 output.color = input.color;
    54.                 output.uv = float2(input.uv);
    55.                 return output;
    56.             }
    57.  
    58.             float4 frag(VertexOutput input) : COLOR
    59.             {
    60.                 float4 diffuseColor = tex2D(_MainTex, input.uv);
    61.  
    62.                 float3 ambientLighting = float3(UNITY_LIGHTMODEL_AMBIENT) * float3(diffuseColor) *
    63.                     float3(input.color);
    64.                 return float4(ambientLighting, diffuseColor.a);
    65.             }
    66.  
    67.             ENDCG
    68.         }
    69.  
    70.         Pass
    71.         {    
    72.             Tags { "LightMode" = "ForwardAdd" }
    73.             Blend One One // additive blending
    74.  
    75.             CGPROGRAM
    76.  
    77.             #pragma vertex vert  
    78.             #pragma fragment frag
    79.             #pragma target 3.0
    80.  
    81.             #include "UnityCG.cginc"
    82.  
    83.             // User-specified properties
    84.             uniform sampler2D _MainTex;
    85.             uniform sampler2D _Normal;
    86.             uniform sampler2D _Depth;
    87.             uniform float4 _SpecColor;
    88.             uniform float4 _LightColor0;
    89.             uniform float _Shininess;
    90.             uniform float _AmplifyDepth;
    91.             uniform float _CelShadingLevels;
    92.  
    93.             struct VertexInput
    94.             {
    95.                 float4 vertex : POSITION;
    96.                 float4 color : COLOR;
    97.                 float4 uv : TEXCOORD0;
    98.             };
    99.  
    100.             struct VertexOutput
    101.             {
    102.                 float4 pos : POSITION;
    103.                 float4 color : COLOR;
    104.                 float2 uv : TEXCOORD0;
    105.                 float4 posWorld : TEXCOORD1;
    106.             };
    107.  
    108.             VertexOutput vert(VertexInput input)
    109.             {
    110.                 VertexOutput output;
    111.  
    112.                 output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    113.                 output.posWorld = mul(_Object2World, input.vertex);
    114.  
    115.                 output.uv = float2(input.uv);
    116.                 output.color = input.color;
    117.                 return output;
    118.             }
    119.  
    120.             float4 frag(VertexOutput input) : COLOR
    121.             {
    122.                 float4 diffuseColor = tex2D(_MainTex, input.uv);
    123.  
    124.                 // To compute the correct normal:
    125.                 //   1) Get the pixel value from the normal map
    126.                 //   2) Subtract 0.5 and multiply by 2 to convert from the range 0...1 to -1...1
    127.                 //   3) Multiply by world to object matrix, to handle rotation, etc
    128.                 //   4) Negate Z so that lighting works as expected (sprites further away from the camera than
    129.                 //      a light are lit, etc.)
    130.                 //   5) Normalize
    131.                 float3 normalDirection = (tex2D(_Normal, input.uv).xyz - 0.5f) * 2.0f;
    132.                 normalDirection = float3(mul(float4(normalDirection, 1.0f), _World2Object));
    133.                 normalDirection.z *= -1;
    134.                 normalDirection = normalize(normalDirection);
    135.  
    136.                 // To adjust depth:
    137.                 //   1) Get the depth value from the depth map
    138.                 //   2) Subtract 0.5 and multiply by 2 to convert from the range 0...1 to -1...1
    139.                 //   3) Multiply by the amplify depth value, and subtract from the fragment's z position
    140.                 float depthColor = (tex2D(_Depth, input.uv).x - 0.5f) * 2.0f;
    141.                 float3 posWorld = float3(input.posWorld);
    142.                 posWorld.z -= depthColor * _AmplifyDepth;
    143.                
    144.                 float attenuation;
    145.                 float3 lightDirection;
    146.                
    147.                 if (0.0 == _WorldSpaceLightPos0.w) // directional light?
    148.                 {
    149.                     attenuation = 1.0; // no attenuation
    150.                     lightDirection = normalize(float3(_WorldSpaceLightPos0));
    151.                 }
    152.                 else{
    153.                     //float3 vertexToLightSource = float3(_WorldSpaceLightPos0 - mul(modelMatrix, input.vertex));
    154.                     float3 vertexToLightSource = float3(_WorldSpaceLightPos0) - posWorld;
    155.                     float distance = length(vertexToLightSource);
    156.                     attenuation = 1.0 / distance; // linear attenuation
    157.                     lightDirection = normalize(vertexToLightSource);
    158.                 }
    159.                
    160.  
    161.                 // Compute diffuse part of lighting
    162.                 float normalDotLight = dot(normalDirection, lightDirection);
    163.                 float diffuseLevel = attenuation * max(0.0f, normalDotLight);
    164.  
    165.                 // Compute specular part of lighting
    166.                 float specularLevel;
    167.                 if (normalDotLight < 0.0f)
    168.                 {
    169.                     // Light is on the wrong side, no specular reflection
    170.                     specularLevel = 0.0f;
    171.                 }
    172.                 else
    173.                 {
    174.                     // For orthographic cameras, the view direction is always known
    175.                     float3 viewDirection = float3(0.0f, 0.0f, -1.0f);
    176.                     specularLevel = attenuation * pow(max(0.0, dot(reflect(-lightDirection, normalDirection),
    177.                         viewDirection)), _Shininess);
    178.                 }
    179.  
    180.                 // Add cel-shading if enough levels were specified
    181.                 if (_CelShadingLevels >= 2)
    182.                 {
    183.                     diffuseLevel = floor(diffuseLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5f);
    184.                     specularLevel = floor(specularLevel * _CelShadingLevels) / (_CelShadingLevels - 0.5f);
    185.                 }
    186.  
    187.                 float3 diffuseReflection = float3(diffuseColor) * input.color *
    188.                     float3(_LightColor0) * diffuseLevel;
    189.                 float3 specularReflection = float3(_LightColor0) * float3(_SpecColor) *
    190.                     input.color * specularLevel;
    191.                 return float4(diffuseReflection + specularReflection, diffuseColor.a);
    192.              }
    193.  
    194.              ENDCG
    195.         }
    196.     }
    197.     // The definition of a fallback shader should be commented out
    198.     // during development:
    199.     // Fallback "Transparent/Diffuse"
    200. }
    I've gone through this page to see how directional lights were done there and to try to add it to this shader, but I honestly have no clue what I'm doing.
    Would really like some help, please.
     
  3. Fellshadow

    Fellshadow

    Joined:
    Dec 2, 2012
    Posts:
    169
    After reading through the article for the 5th time, I noticed that he mentioned that since we are using 2D, we always know the normaldirection and viewdirection. In my case, this is not true, since I'm doing everything in a 3D environment.
    I've tried to adjust the shader to calculate the normaldirection and viewdirection, but I'm getting an error. Here is what I changed in the above shader:

    Code (csharp):
    1. float3 myNormal = (tex2D(_Normal, input.uv).xyz - 0.5f) * 2.0f;
    2. myNormal.z *= -1;
    3.                
    4. float4x4 modelMatrix = _Object2World;
    5. float4x4 modelMatrixInverse = _World2Object;
    6.  
    7. float3 normalDirection = normalize(float3(mul(float4(myNormal, 0.0), modelMatrixInverse)));
    8. float3 viewDirection = normalize(float3(float4(_WorldSpaceCameraPos, 1.0) - mul(modelMatrix, input.vertex)));
    Everything seems to work fine except for the viewdirection line. It gets the following errors:
    Shader error in 'Custom/shr_SpriteNormalShader': Shader program had errors at line 76
    Shader error in 'Custom/shr_SpriteNormalShader': Program 'frag', expression left of ."vertex" is not a struct or array at line 143
    Shader error in 'Custom/shr_SpriteNormalShader': Program 'vert', expression left of ."vertex" is not a struct or array at line 143

    Any help at all?