A minimal Direct3D 11 implementation of "antialiased point sampling", useful for smooth fractional movement and non-integer scaling of pixel art AKA "fat pixel" aesthetics.
Also view below side-by-side point sampling comparison on YouTube (video is zoomed in to counter implicit downsampling & compression artifacts and make aa effect more apparent) or check out the Shadertoy.
The actual sampler is set to bilinear filtering (the default D3D11 sampler state) in order to utilize single texture-read hardware interpolation, then emulating point sampling in the shader and applying AA at the fat pixel boundaries. Use with premultiplied alpha textures* and keep a one pixel transparent border around each sprite/tile.
This is the algorithm (see shaders.hlsl
for context):
float2 pix = floor(p.tex) + min(frac(p.tex) / fwidth(p.tex), 1) - 0.5;
For sharper aa, try:
float2 pix = floor(p.tex) + smoothstep(0, 1, frac(p.tex) / fwidth(p.tex)) - 0.5;
Note that the D3D setup here cuts a lot of corners to keep it small relative to the pixel shader containing the algorithm. For a more conventional D3D11 setup reference, see the original Minimal D3D11
*I recently made a small (~17 KB) command line utility for this, called texprep.exe
, available for download at the bottom of this Gist (note: the executable it is unsigned and so might be - falsely! - flagged as malware by e.g. Windows Defender).
It reads most common image formats, converts to 32-bit (ARGB) and saves it out to either -png
, -bmp
or -bin
(a raw sequence of B, G, R, A, ...
bytes that can be imported into your application without the need for decoding). New! in version 1.1.x is the option to export to -txt
, which creates a text file with the image data encoded as an int array[]
.
You can specify multiple input files. An output format will apply to subsequent files until another is specified. Default is -png
.
Similarly, -pm1
turns premultiplied alpha ON and -pm0
turns it OFF. Premultiplied alpha processing is OFF by default.
Examples:
C:\>texprep -bmp -pm1 mario.png
texprep : reading 'mario.png' (640x640) .. premultiplying alpha .. writing 'mario_pm1.bmp' .. done.
C:\>texprep -bin onedrive.ico
texprep : reading 'onedrive.ico' (40x40) .. writing 'onedrive_40x40_pm0.bin' .. done.
C:\>texprep -bin cursor.cur -pm1 red50.png -png -pm0 splash.tif tiger.jpg
texprep : reading 'cursor.cur' (256x170) .. writing 'cursor_256x170_pm0.bin' .. done.
texprep : reading 'red50.png' (129x128) .. premultiplying alpha .. writing 'red50_129x128_pm1.bin' .. done.
texprep : reading 'splash.tif' (3000x700) .. writing 'splash_pm0.png' .. done.
texprep : reading 'tiger.jpg' (1920x1280) .. writing 'tiger_pm0.png' .. done.
C:\>texprep -txt adventurer.gif
texprep : reading 'adventurer.gif' (898x505) .. writing 'adventurer_pm0.txt' .. done.
The latter will produce a text file that looks something like this:
// adventurer.gif
#define TEXTURE_WIDTH 898
#define TEXTURE_HEIGHT 505
int texture[] =
{
0xff2c2c2c, 0xff2c2c2c, 0xff2c2c2c, 0xff2c2c2c, 0xff2c2c2c, 0xff2c2c2c, ...
...
};
Example use case here: Minimal D3D11 sprite renderer
an alternative to rendering each tile as a separate antialiased sprite would be to first render the tiles normally, pixel-aligned, to a separate texture, then transform and render that to the screen with aa applied. in general, remember to use premultiplied alpha textures and keep a single-pixel transparent border around them or (for single-sprite textures) set the texture address mode to BORDER and provide a 0-value there