Search Unity

Question Artifacts in Pixel art with Normal Maps

Discussion in 'General Graphics' started by carcasanchez, Dec 27, 2020.

  1. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hello, friends.
    I have been toying with normal maps and pixel art in Unity, and I have been very happy with the results.
    upload_2020-12-27_22-55-55.png
    Howevere, there's strange artifacts happening in close range:
    upload_2020-12-27_22-58-14.png upload_2020-12-27_23-1-18.png
    See those pesky black lines between pixels?

    I have cheked the textures, and does not seem a compression nor formatting problem. Possibly camera settings?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    How do you have the art setup? Are these textures using point sampling, or are you using extra large textures? What kind of shader are you using?
     
  3. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    They are 1024*1024 textures, using indeed point sampling, and using a custom toon surface shader:
    Code (CSharp):
    1. Shader "Custom/SteppedToon" {
    2.     //show values to edit in inspector
    3.     Properties{
    4.         [Header(Base Parameters)]
    5.         _Color("Tint", Color) = (0, 0, 0, 1)
    6.         _MainTex("Texture", 2D) = "white" {}
    7.         _NormalMap("NormalMap", 2D) = "white" {}
    8.         [HDR] _Emission("Emission", color) = (0 ,0 ,0 , 1)
    9.  
    10.         [Header(Lighting Parameters)]
    11.         _ShadowTint("Shadow Color", Color) = (0.5, 0.5, 0.5, 1)
    12.     }
    13.         SubShader{
    14.             //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
    15.             Tags{ "RenderType" = "Opaque" "Queue" = "Geometry"}
    16.  
    17.             CGPROGRAM
    18.  
    19.             //the shader is a surface shader, meaning that it will be extended by unity in the background to have fancy lighting and other features
    20.             //our surface shader function is called surf and we use our custom lighting model
    21.             //fullforwardshadows makes sure unity adds the shadow passes the shader might need
    22.             #pragma surface surf Stepped fullforwardshadows
    23.             #pragma target 3.0
    24.  
    25.             fixed4 _Color;
    26.             sampler2D _MainTex;
    27.             sampler2D _NormalMap;
    28.             half3 _Emission;
    29.  
    30.             float3 _ShadowTint;
    31.  
    32.             //our lighting function. Will be called once per light
    33.             float4 LightingStepped(SurfaceOutput s, float3 lightDir, half3 viewDir, float shadowAttenuation) {
    34.                 //how much does the normal point towards the light?
    35.                 float towardsLight = dot(s.Normal, lightDir);
    36.                 // make the lighting a hard cut
    37.                 float towardsLightChange = fwidth(towardsLight);
    38.                 float lightIntensity = smoothstep(0, towardsLightChange, towardsLight);
    39.  
    40.             #ifdef USING_DIRECTIONAL_LIGHT
    41.                 //for directional lights, get a hard vut in the middle of the shadow attenuation
    42.                 float attenuationChange = fwidth(shadowAttenuation) * 0.5;
    43.                 float shadow = smoothstep(0.5 - attenuationChange, 0.5 + attenuationChange, shadowAttenuation);
    44.             #else
    45.                 //for other light types (point, spot), put the cutoff near black, so the falloff doesn't affect the range
    46.                 float attenuationChange = fwidth(shadowAttenuation);
    47.                 float shadow = smoothstep(0, attenuationChange, shadowAttenuation);
    48.             #endif
    49.                 lightIntensity = lightIntensity * shadow;
    50.  
    51.                 //calculate shadow color and mix light and shadow based on the light. Then taint it based on the light color
    52.                 float3 shadowColor = s.Albedo * _ShadowTint;
    53.                 float4 color;
    54.                 color.rgb = lerp(shadowColor, s.Albedo, lightIntensity) * _LightColor0.rgb;
    55.                 color.a = s.Alpha;
    56.                 return color;
    57.             }
    58.  
    59.  
    60.             //input struct which is automatically filled by unity
    61.             struct Input {
    62.                 float2 uv_MainTex;
    63.                 float2 uv_NormalMap;
    64.             };
    65.  
    66.             //the surface shader function which sets parameters the lighting function then uses
    67.             void surf(Input i, inout SurfaceOutput o) {
    68.                 //sample and tint albedo texture
    69.                 fixed4 col = tex2D(_MainTex, i.uv_MainTex);
    70.  
    71.                 clip(col.a-0.0001);
    72.                 col *= _Color;
    73.                 o.Albedo = col.rgb;
    74.                 o.Normal = UnpackNormal(tex2D(_NormalMap, i.uv_NormalMap));
    75.                 o.Emission = _Emission;
    76.             }
    77.             ENDCG
    78.         }
    79.             FallBack "Standard"
    80. }
    81.  
    I'm not familiar with shader for 2d sprites, so it's possible I'm commiting a mistake somewhere
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    That's the problem right there. That's to do anti-aliasing for smooth normals, which isn't needed here since the lighting is always going to be hard edged since the normals are from a point sampled texture. That line explicitly has problems when you have sharp normals.

    Replace that line with 0.01, or replace the
    smoothstep
    in the line afterwards with a
    step(0.0, towardsLight);
     
  5. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Thanks! Everything is working now as intended