I'm Morgan McGuire (@CasualEffects). I've been working on computer graphics and games for 20 years at great places including NVIDIA, Williams College, Brown University, and Activision.

See my home page for a full index of my blog posts, books, research, and projects.

Thursday, March 31, 2016

Pixel Art Tech Tips

[Programmer!] pixel art from my recent
game jam projects. The portrait is derived
from licensed art by Oryx
This article describes art tools and programming techniques I use for creating indie games with pixel art for game jams.

Pixel art, also sometimes known as "8-bit" and "16-bit" art, is digital art drawn at a scale where individual pixels are visible. It combines classical art techniques from mosaics and textiles with digital conventions and golden-age gaming aesthetics.

Pixel art is popular among indie game developers. In addition the currency of retro style and its implicit reference to a gameplay-first mindset, the cost of producing pixel art is typically lower than for higher-resolution or 3D assets. While great artists can create masterpieces under the limiting constraints of pixel art, those constraints also make it accessible to novices. Anyone can easily modify or create small sprites with limited colors compared to the difficulty of 3D modeling, texturing, and animation.

Some great examples of games embracing an extremely low-resolution and limited-color palette can be found in the PICO-8 fantasy console and LOW REZ JAM. Opengameart.org is a large repository of creative commons and public domain pixel art (of mixed quality) that is available for games.

For fantastic art studio advice on drawing pixel art, see Pedro Medieros' tutorials.

Pixel Art Examples

Painting by Octavi Navarro
"Canabalt" game by Adam "Atomic" Saltsman
"Night Pearl" game by Christina Antoinette Neofotistou

Sprite sheet from the Street Fighter game
Mark Ferrari's Mountain Gate from the Battle Mage game.
This image also animates and changes weather and time
of day using palette animation!
Screenshot from Castlevania

Photoshop

I draw most of my pixel art directly in Photoshop, using the Pencil and Eraser tools at 1px size with 100% opacity and 100% hardness. Some other essential configuration changes for Photoshop pixel art are below.

Change the Units for Rulers and Type in PreferencesUnits & Rulers to "Pixels."


Set Preferences→GeneralImage Interpolation to "Nearest Neighbor."

To get a pixel-accurate cursor, disable ViewSnap

Because you'll be zoomed in most of the time, you may will likely want to toggle ViewShowPixel Grid frequently.

To see sprite grids, enable ViewExtras and then under PreferencesGuidesGrid & Slices, set Gridline Every to your sprite size in pixels.


Set font antialiasing to "None" for very small (say, 8 pixel) or "Crisp" for larger title-screen text.


Save your pixel art in a lossless format, such as BMP, TGA, TIFF, GIF, or PNG. PNG is usually preferred today because it works well with many modern tools and browsers.

Photoshop outputs rather large PNG files. You can reduce their size with no loss of quality using a tool such as ImageOptim, which uses the PNGcrush, OptiPNG, PNGOUT, AdvPNG, and Zopfli libraries and tools to do its work. There is an online tool for this as well.

I occasionally use Pixen and Pixlr editors for pixel art, but usually find that the full power of Photoshop gives a better workflow.

Resizing with HQx

Sometimes you'll want to enlarge pixel art and make it appear more like vector art. This is useful when creating icons and cover art from pixel resources. It is also sometimes desirable for rendering assets or whole games mastered as pixel art in a more contemporary style.

Maxim Stepin developed the family of "HQx" algorithms for heuristically rescaling pixel art by 2x, 3x, and 4x. They work best on high-contrast, palette images. Implementations are provided by:




Nick Darnell provides an online tool called "Depixelizer" for performing HQx resizing, which also supports an alpha channel.

During the art process, I often resize objects using either nearest-neighbor interpolation or the HQX algorithms, and then paint over them in Photoshop to restore the pixel look at the new scale.

OpenGL & GLSL

Set the texture or sampler GL_TEXTURE_MAG_FILTER filter to GL_NEAREST.

Set the GL_TEXTURE_MIN_FILTER to either GL_NEAREST_MIPMAP_NEAREST (for sharp but flickering shrunk textures) or GL_LINEAR_MIPMAP_LINEAR (for slightly blurry shrunk textures without flickering).

By default, OpenGL runs the fragment shader at the centers of pixels covered by rasterized primitives. That is, in GLSL gl_FragCoord.xy will be an integer plus (0.5, 0.5).

Use texelFetch to read from textures at integer pixel coordinates.

Javascript Canvas

To make a Canvas object display enlarged (so that "pixels" are visible on a high-resolution display), increase its width and height by an integer multiple and then set nearest-neighbor interpolation for the Canvas itself by:

canvas.style.msInterpolationMode = 'nearest-neighbor';
canvas.style.imageRendering = '-o-crisp-edges';
canvas.style.imageRendering = '-moz-crisp-edges';
canvas.style.imageRendering = '-webkit-optimize-contrast';
canvas.style.imageRendering = 'optimize-contrast';
canvas.style.imageRendering = 'crisp-edges';
canvas.style.imageRendering = 'pixelated';

When drawing Images or other Canvases onto the canvas, enforce nearest-neighbor interpolation for them by:

var ctx = canvas.getContext("2d");
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;

Most browsers treat integer coordinates as the centers of pixels, which causes lines and rendered sprites at integer coordinates to be blurred. To avoid this, use:

ctx.translate(-0.5, -0.5);

Unfortunately, there is no standard way to disable font antialising using fillText. On some browsers on some platforms, the following will work (for example, currently most OS X browsers will obey these):

canvas.style.mozOsxFontSmoothing = 'unset';
canvas.style.webkitFontSmoothing = 'none';
canvas.style.fontSmooth = 'never';
canvas.style.fontSmoothing = 'never';

The only reliable solution for pixel font rendering from arbitrary fonts is to pre-render your fonts to an Image and the use drawImage to copy individual characters or strings of text as a bitmap font. It may be possible to create a bitmap web font that prevents antialiasing, although I have not found an example of one yet.

HTML & CSS

When embedding pixel art images into web pages, use the CSS styles from above, but applied in CSS:
img, canvas {
  ms-interpolation-mode: nearest-neighbor;
  image-rendering: -o-crispedges;
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-optimize-contrast;
  image-rendering: pixelated;
}

Social Media

Social media tools such as Twitter tend to shrink and re-compress images. This destroys the quality of pixel art. A trick for sharing your artwork in a way that will preserve the details is to resize the image in Photoshop by an integer factor (say, 8x) using Nearest interpolation. This makes the pixels visible.

Then, leave one transparent pixel in the corner of the image to prevent the social media site from converting the image to a JPG, which has lossy compression that will blur the pixels. Save the result as a GIF or PNG image and upload it to your favorite site. Beware that animated GIFs will likely be converted to MP4 video files, which will become blurry as well.

Some Pixel Art Links




Morgan McGuire (@morgan3d) is a professor at Williams College, a researcher at NVIDIA, and a professional game developer. His most recent games are Project Rocket Golfing for iOS and Skylanders: Superchargers for consoles. He is the author of the Graphics Codex, an essential reference for computer graphics now available in iOS and Web Editions.