iPhone 3D Programming : Anti-Aliasing Tricks with Offscreen FBOs (part 2) - Jittering

2/26/2011 5:02:11 PM

2. Jittering

Jittering is somewhat more complex to implement than supersampling, but it’s not rocket science. The idea is to rerender the scene multiple times at slightly different viewpoints, merging the results along the way. You need only two FBOs for this method: the on-screen FBO that accumulates the color and the offscreen FBO that the 3D scene is rendered to. You can create as many jittered samples as you’d like, and you still need only two FBOs. Of course, the more jittered samples you create, the longer it takes to create the final rendering. Example 5 shows the pseudocode for the jittering algorithm.

Example 5. Jitter pseudocode

for (int sample = 0; sample < SampleCount; sample++) {

vec2 offset = JitterTable[sample]

SetFrustum(LeftPlane + offset.x, RightPlane + offset.x,
TopPlane + offset.y, BottomPlane + offset.y,
NearPlane, FarPlane)


f = 1.0 / SampleCount
glColor4f(f, f, f, 1)
glBlendFunc(GL_ONE, GL_ONE)


The key part of Example 6-12 is the blending configuration. By using a blend equation of plain old addition (GL_ONE, GL_ONE) and dimming the color according to the number of samples, you’re effectively accumulating an average color.

An unfortunate side effect of jittering is reduced color precision; this can cause banding artifacts, as shown in Figure 4. On some platforms the banding effect can be neutralized with a high-precision color buffer, but that’s not supported on the iPhone. In practice, I find that creating too many samples is detrimental to performance anyway, so the banding effect isn’t usually much of a concern.

Figure 4. 2×, 4×, 8×, 16×, and 32× jittering

Determining the jitter offsets (JitterTable in Example 6-12) is a bit of black art. Totally random values don’t work well since they don’t guarantee uniform spacing between samples. Interestingly, dividing up each pixel into an equally spaced uniform grid does not work well either! Example 6 shows some commonly used jitter offsets.

Example 6. Popular jitter offsets
const vec2 JitterOffsets2[2] =
vec2(0.25f, 0.75f), vec2(0.75f, 0.25f),

const vec2 JitterOffsets4[4] =
vec2(0.375f, 0.25f), vec2(0.125f, 0.75f),
vec2(0.875f, 0.25f), vec2(0.625f, 0.75f),

const vec2 JitterOffsets8[8] =
vec2(0.5625f, 0.4375f), vec2(0.0625f, 0.9375f),
vec2(0.3125f, 0.6875f), vec2(0.6875f, 0.8125f),

vec2(0.8125f, 0.1875f), vec2(0.9375f, 0.5625f),
vec2(0.4375f, 0.0625f), vec2(0.1875f, 0.3125f),

const vec2 JitterOffsets16[16] =
vec2(0.375f, 0.4375f), vec2(0.625f, 0.0625f),
vec2(0.875f, 0.1875f), vec2(0.125f, 0.0625f),

vec2(0.375f, 0.6875f), vec2(0.875f, 0.4375f),
vec2(0.625f, 0.5625f), vec2(0.375f, 0.9375f),

vec2(0.625f, 0.3125f), vec2(0.125f, 0.5625f),
vec2(0.125f, 0.8125f), vec2(0.375f, 0.1875f),

vec2(0.875f, 0.9375f), vec2(0.875f, 0.6875f),
vec2(0.125f, 0.3125f), vec2(0.625f, 0.8125f),

Let’s walk through the process of creating a simple app with jittering. Much like we did with the supersample example, we’ll include a fun transition animation. (You can download the full project from the book’s website at This time we’ll use the jitter offsets to create a defocusing effect, as shown in Figure 5.

Figure 5. Defocus transition with jitter

To start things off, let’s take a look at the RenderingEngine class declaration and related types. It’s not unlike the class we used for supersampling; the main differences are the labels we give to the FBOs. Accumulated denotes the on-screen buffer, and Scene denotes the offscreen buffer. See Example 7.

Example 7. RenderingEngine declaration for the jittering sample
struct Framebuffers {
GLuint Accumulated;
GLuint Scene;

struct Renderbuffers {
GLuint AccumulatedColor;
GLuint SceneColor;
GLuint SceneDepth;
GLuint SceneStencil;

struct Textures {
GLuint Marble;
GLuint RhinoBackground;
GLuint TigerBackground;
GLuint OffscreenSurface;

class RenderingEngine : public IRenderingEngine {
RenderingEngine(IResourceManager* resourceManager);
void Initialize();
void Render(float objectTheta, float fboTheta) const;
void RenderPass(float objectTheta, float fboTheta, vec2 offset) const;
Textures m_textures;
Renderbuffers m_renderbuffers;
Framebuffers m_framebuffers;
// ...

Example 7 also adds a new private method called RenderPass; the implementation is shown in Example 8. Note that we’re keeping the fboTheta argument that we used in the supersample example, but now we’re using it to compute a scale factor for the jitter offset rather than a y-axis rotation. If fboTheta is 0 or 180, then the jitter offset is left unscaled, so the scene is in focus.

Example 8. RenderPass method for jittering
void RenderingEngine::RenderPass(float objectTheta, float fboTheta, vec2 offset) const
// Tweak the jitter offset for the defocus effect:

offset -= vec2(0.5, 0.5);
offset *= 1 + 100 * sin(fboTheta * Pi / 180);

// Set up the frustum planes:

const float AspectRatio = (float) m_viewport.y / m_viewport.x;
const float NearPlane = 5;
const float FarPlane = 50;
const float LeftPlane = -1;
const float RightPlane = 1;
const float TopPlane = -AspectRatio;
const float BottomPlane = AspectRatio;

// Transform the jitter offset from window space to eye space:

offset.x *= (RightPlane - LeftPlane) / m_viewport.x;
offset.y *= (BottomPlane - TopPlane) / m_viewport.y;

// Compute the jittered projection matrix:

mat4 projection = mat4::Frustum(LeftPlane + offset.x,
RightPlane + offset.x,
TopPlane + offset.y,
BottomPlane + offset.y,
NearPlane, FarPlane);

// Render the 3D scene - download the example to see this code.

Example 9 shows the implementation to the main Render method. The call to RenderPass is shown in bold.

Example 9. Render method for jittering
void RenderingEngine::Render(float objectTheta, float fboTheta) const

// This is where you put the jitter offset declarations
// from Example 6-13.

const int JitterCount = 8;
const vec2* JitterOffsets = JitterOffsets8;

glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffers.Accumulated);

glClearColor(0, 0, 0, 1);

for (int i = 0; i < JitterCount; i++) {

glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffers.Scene);

fboTheta, JitterOffsets[i]);


const float NearPlane = 5, FarPlane = 50;
glFrustumf(-0.5, 0.5, -0.5, 0.5, NearPlane, FarPlane);

glTranslatef(0, 0, -NearPlane * 2);

float f = 1.0f / JitterCount;
f *= (1 + abs(sin(fboTheta * Pi / 180)));
glColor4f(f, f, f, 1);

glBlendFunc(GL_ONE, GL_ONE);
glBindTexture(GL_TEXTURE_2D, m_textures.OffscreenSurface);

Example 9 might give you sense of déjà vu; it’s basically an implementation of the pseudocode algorithm that we already presented in Example 6-12. One deviation is how we compute the dimming effect:

float f = 1.0f / JitterCount;
f *= (1 + abs(sin(fboTheta * Pi / 180)));
glColor4f(f, f, f, 1);

The second line in the previous snippet is there only for the special transition effect. In addition to defocusing the scene, it’s also brightened to simulate pupil dilation. If fboTheta is 0 or 180, then f is left unscaled, so the scene has its normal brightness.

3. Other FBO Effects

An interesting variation on jittering is depth of field, which blurs out the near and distant portions of the scene. To pull this off, compute the viewing frustum such that a given slice (parallel to the viewing plane) stays the same with each jitter pass; this is the focus plane.

Yet another effect is motion blur, which simulates the ghosting effect seen on displays with low response times. With each pass, make incremental adjustments to your animation, and gradually fade in the alpha value using glColor.

  •  Building LOB Applications : Navigating RIA LOB Data
  •  Building LOB Applications : Databinding in XAML
  •  Microsoft XNA Game Studio 3.0 : Program Bugs
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Adding Vibration
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Using the Keyboard
  •  iPhone 3D Programming : Blending and Augmented Reality - Stencil Alternatives for Older iPhones
  •  iPhone 3D Programming : Blending and Augmented Reality - Poor Man’s Reflection with the Stencil Buffer
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Reading a Gamepad
  •  iPhone 3D Programming : Blending and Augmented Reality - Shifting Texture Color with Per-Vertex Color
  •  iPhone 3D Programming : Blending and Augmented Reality - Blending Extensions and Their Uses
  •  iPhone 3D Programming : Blending and Augmented Reality - Blending Caveats
  •  Building LOB Applications : Using Visual Studio 2010 WCF RIA Data Services Tooling
  •  Building LOB Applications : Implementing CRUD Operations in WCF Data Services
  •  iPhone 3D Programming : Blending and Augmented Reality - Wrangle Premultiplied Alpha
  •  iPhone 3D Programming : Blending and Augmented Reality - Blending Recipe
  •  Microsoft XNA Game Studio 3.0 : Controlling Color (part 3)
  •  Microsoft XNA Game Studio 3.0 : Controlling Color (part 2)
  •  Microsoft XNA Game Studio 3.0 : Controlling Color (part 1) - Games and Classes & Classes as Offices
  •  Microsoft XNA Game Studio 3.0 : Working with Colors
  •  iPhone 3D Programming : Textures and Image Capture - Creating Textures with the Camera
    Most View
    Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 3) - Sprite Drawing with SpriteBatch
    Web Security : Automating with LibWWWPerl - Checking Session Expiration
    The Truth About Free Trials (Part 1)
    Thunderbolt vs USB 3.0
    Share And Sync Data Between Your Devices
    CorelDraw 10 : Printing - Preflight and Print Preview
    Sony Xperia Z - Back In The Top Three?
    SQL Server 2005 Native XML Web Services : Exposing SQL Programmability as Web Services (part 1)
    Televisions Award – Q1 2013 (Part 1) : Sony KDL - 40HX853, Sony KDL-32HX753, Sony KDL-46HX853, Panasonic TX-P50GT50
    Damson Twist – Wireless Portable Bluetooth Speaker With Serious Bass
    Top 10
    10 Contenders For The 'Ultimate Protector' Crown (Part 5) : Microsoft Security Essentials 4.1, AVG Antivirus Free 2013
    10 Contenders For The 'Ultimate Protector' Crown (Part 4) : Norton Internet Security, Avast Free Antivirus Version 7
    10 Contenders For The 'Ultimate Protector' Crown (Part 3) : Eset Smart Security 6, Kaspersky Internet Security 2013, Zonealarm Internet Security 2013
    10 Contenders For The 'Ultimate Protector' Crown (Part 2) : Bitdefender Total Security 2013, Trend Micro Maximum Security, Mcafee Internet Security 2013
    10 Contenders For The 'Ultimate Protector' Crown (Part 1)
    Sony Xperia TL - Much Improved But Still Imperfect (Part 3)
    Sony Xperia TL - Much Improved But Still Imperfect (Part 2)
    Sony Xperia TL - Much Improved But Still Imperfect (Part 1)
    Simple.TV - Transmits TV Programs To Mobile (Part 2)
    Simple.TV - Transmits TV Programs To Mobile (Part 1)