           value: null
         normalMap:
           value: null
+        smoothShading:
+          value: options.smoothShading
       ,
         THREE.UniformsLib.lights
 


      SpriteMaterial.paletteTexture.needsUpdate = true

  constructor: (@options = {}) ->
    super
      transparent: true
      lights: true

  vec3 shadedColor = sourceColor * shadeFactor;

  // Find the nearest color from the palette to represent the shaded color.
  vec2 paletteColor = rampShadeDitherAlpha.rg;
  vec3 bestColor;
  float bestColorDistance;

  bool passedZero = false;
  vec3 earlierColor;
  vec3 laterColor;
  float blendFactor = 0.0;

  vec3 previousColor;
  float previousSignedDistance;

  for (int shadeIndex = 0; shadeIndex < #{LOI.Engine.Materials.SpriteMaterial.shadesCount}; shadeIndex++) {
    paletteColor.g = float(shadeIndex) / #{LOI.Engine.Materials.SpriteMaterial.shadesCount}.0;
    vec3 shade = texture2D(palette, paletteColor).rgb;

    // Measure distance to color.
    vec3 difference = shade - shadedColor;
    float signedDistance = difference.x + difference.y + difference.z;
    float distance = abs(difference.x) + abs(difference.y) + abs(difference.z);

    if (shadeIndex == 0) {
      // Set initial values in first loop iteration.
      bestColor = shade;
      bestColorDistance = distance;
      earlierColor = shade;
      laterColor = shade;
    } else {
      // See if we've crossed zero distance, which means our target shaded color is between the previous and current shade.
      if (previousSignedDistance < 0.0 && signedDistance >= 0.0 || previousSignedDistance >= 0.0 && signedDistance < 0.0) {
        passedZero = true;
        earlierColor = previousColor;
        laterColor = shade;
        blendFactor = abs(previousSignedDistance) / abs(signedDistance - previousSignedDistance);
      }

      if (distance < bestColorDistance) {
        bestColor = shade;
        bestColorDistance = distance;

      // Note: We have to make sure the distance increased since there could be two of the same colors in the palette.
      } else if (distance > bestColorDistance) {
        // We have increased the distance, which means we're moving away from the best color and can safely quit.
        break;
      }
    }

    previousSignedDistance = signedDistance;
    previousColor = shade;
  }

  vec3 destinationColor = bestColor;

  // Apply dithering.
  int x = int(mod(gl_FragCoord.x, 2.0));
  int y = int(mod(gl_FragCoord.y, 2.0));

  if (abs(0.5 - blendFactor) < rampShadeDitherAlpha.b / 2.0) {
    if (x==y) {
      destinationColor = earlierColor;
    } else {
      destinationColor = laterColor;
    }
  } else if (smoothShading && passedZero) {
    destinationColor = mix(earlierColor, laterColor, blendFactor);
  }

  // Color the pixel with the best match from the palette.
  gl_FragColor = vec4(destinationColor, rampShadeDitherAlpha.a);
