# Subpixel Reconstruction Antialiasing

Matthäus G. Chajdas^{1,3}, Morgan McGuire^{3,2}, David
Luebke^{3}

^{1}Technische Universität München,
^{2}Williams College,
^{3}NVIDIA

*Accepted to ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games 2011*

## Abstract

Subpixel Reconstruction Antialiasing (SRAA) combines singlepixel (1x) shading with subpixel visibility to create antialiased images without increasing the shading cost. SRAA targets deferred shading renderers, which cannot use multisample antialiasing. SRAA operates as a post-process on a rendered image with super-resolution depth and normal buffers, so it can be incorporated into an existing renderer without modifying the shaders. In this way SRAA resembles Morphological Antialiasing (MLAA), but the new algorithm can better respect geometric boundaries and has fixed runtime independent of scene and image complexity. SRAA benefits shading-bound applications. For example, our implementation evaluates SRAA in 1.8 ms (1280x720) to yield antialiasing quality comparable to 4-16x shading. Thus SRAA would produce a net speedup over supersampling for applications that spend 1 ms or more on shading; for comparison, most modern games spend 5-10 ms shading. We also describe simplifications that increase performance by reducing quality.

## Download

- Paper
- Subpixel reconstruction anti-aliasing video (updated, see below for details)

## Bibtex

```
@InProceedings{CML11,
author = "Chajdas, Matth{\"a}us G. and Mc{G}uire, Morgan and Luebke, David",
title = "Subpixel Reconstruction Antialiasing",
booktitle = "Proceedings of the ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games",
year = "2011",
publisher = "ACM Press"
}
```

## Kernel source

```
float3 normal(int x, int y)
{
return normalBuffer.Get(x, y) * 2.0 -
make_float3(1, 1, 1);
}
float depth(int x, int y)
{
return depthBuffer.Get(x, y);
}
float bilateral(float3 centerN, float centerZ,
float3 tapN, float tapZ)
{
return exp(-scale * max((1.0 - dot(centerN, tapN)),
depthScale * abs(centerZ - tapZ)));
}
// Iterate the "center" (cx, cy) of the filter
// over the samples in the pixel at (x, y)
float weights[9] = {0};
for (int cy = y; cy < (y + 2); ++cy) {
for (int cx = x; cx < (x + 2); ++cx) {
float3 N = normal(cx, cy);
float Z = depth(cx, cy);
float tmpWeights[9] = {0};
float sum = 0.0f;
// Iterate over the neighboring samples
for (int j = 0; j < 3; ++j) {
for (int i = 0; i < 3; ++i) {
// If inside filter support
if ((abs(i - 1 - cx) <= 1) && (abs(j - 1 - cy) <= 1)) {
int tapX = x + i - 1;
int tapY = y + j - 1;
// Compute the filter weight
float w = bilateral (N, Z,
normal(tapX, tapY), depth(tapX, tapY));
tmpWeights[i + j * 3] = w;
sum += w;
}
}
}
for (int t = 0; t < 9; ++t) {
weights[t] += tmpWeights[t] / sum;
}
}
}
// Apply the filter
float3 result = make_float3 (0, 0, 0);
for (int j = 0; j < 3; ++j) {
for (int i = 0; i < 3; ++i) {
result += weights[i + j * 3] * 0.25 *
colorBuffer.Get(x + i - 1, y + j - 1);
}
}
```

## Updates

- 1.3.2011, 8:30 UTC: Added full-resolution screenshots.
- 1.3.2011, 10:20 UTC: Updated video, re-encoded with twice the bit-rate.
- 7.3.2011, 7:00 UTC: Added fixed kernel source.
`Bilateral`

call was incorrect. - 28.03.2011, 8:00 UTC: Updated video again, the 4-up part is now compressed from new source material with higher quality.
- 23.10.2012, 8:00 UTC: Updated the paper link, should work again.