Anti-Aliasing Tessellated Polygons in GL – Part II

In the end RTFM was the solution…. and a bit of simple algebra..

7.2 Polygon Antialiasing

Antialiasing the edges of filled polygons is similar to antialiasing points and lines. However, antialiasing polygons in color index mode isn’t practical since object intersections are more prevalent and you really need to use OpenGL blending to get decent results.

To enable polygon antialiasing call glEnable() with GL_POLYGON_SMOOTH. This causes pixels on the edges of the polygon to be assigned fractional alpha values based on their coverage. Also, if you want, you can supply a value for GL_POLYGON_SMOOTH_HINT.

In order to get the polygons blended correctly when they overlap, you need to sort the polygons in front to back order. Before rendering, disable depth testing, enable blending and set the blending factors to GL_SRC_ALPHA_SATURATE (source) and GL_ONE (destination). The final color will be the sum of the destination color and the scaled source color; the scale factor is the smaller of either the incoming source alpha value or one minus the destination alpha value. This means that for a pixel with a large alpha value, successive incoming pixels have little effect on the final color because one minus the destination alpha is almost zero.

Since the accumulated coverage is stored in the color buffer, destination alpha is required for this algorithm to work. Thus you must request a visual or pixel format with destination alpha. OpenGL does not require implementations to support a destination alpha buffer so visual selection may fail.

Ok so the cognitive leap I failed to make was the whole background thing. if you draw your background – all your final colors are blended with it – this is what I battled against tonight. To end up with the colors you want, you need draw your polygons in inverse order. Nearest first. Thats the counter intuitive part. The background gets drawn last. So if you want a non-black background you need to draw a Quad to the screen last. The reason is obvious if you think of the math of the blend function.

Source and destination scale factors are referred to as (SR ,SG ,SB ,SA) and (DR ,DG ,DB ,DA).

Source and destination color components are referred to as (RS ,GS ,BS ,AS) and (RD ,GD ,BD ,AD).

They are understood to have integer values between zero and (KR ,KG ,KR ,KA), where KR = 2mR 1, KG = 2mG 1, KB = 2mB 1, KA = 2mA 1 and (mR ,mG ,mB ,mA) is the number of red, green, blue, and alpha bit planes. This is a fancy way of saying 8 bits = maximum value of 256, 16 bits = maximum value of 65536 and so on. For our example below we can assume that k? = 1.0 as it simplifies the math.

Colours are blended by combining the defined source and destination blend factors and source and destination pixels using the following equation.

R (d) = min(KR,RSSR+RDdR)

G (d) = min(KG,GSSG+GDdG)

B (d) = min(KB,BSSB+BDdB)

A (d) = min(KA,ASSA+ADdA)

AS = Alpha source which is material opacity, ranging from 1.0 (KA), representing complete opacity, to 0.0 (0), representing complete transparency.

AD = Alpha destination

The essential components of the solution is as follows

glClearColor

( 0.0, 0.0, 0.0, 0.0 );

You must start with RGBA values all at zero – this becomes obvious when you look at the maths.

glEnable( GL_BLEND );

glEnable( GL_POLYGON_SMOOTH );

We need to turn on blending and polygon smoothing.

glDisable

( GL_DEPTH_TEST );

Depth is useless – sorting is futile.

glBlendFunc ( GL_SRC_ALPHA_SATURATE , GL_ONE );

GL_SRC_ALPHA_SATURATE

 in the source scale factor signifies (SR ,SG ,SB ,SA) =  (i , i , i, 1)  where i = min (AS , kA – AD) / kA

GL_ONE

in the destination scale factor signifies (DR ,DG ,DB ,DA) = (1 ,1 , 1, 1)

Assume a destination pixel starts with 0,0,0,0 (so Ad = 0)

We write 1,0,0,0 to the display at a pixel location

We can assume that KA is 1

so

(RS , GS, BS, AS)  = (1,0,0,1)

(RD ,GD ,BD ,AD) = (0,0,0,0)

min (AA , KA – AD) / K-> min (1 , 1 – 0) ->i = 1

so

(SR ,SG ,SB ,SA)  = (1, 1, 1, 1)

(DR ,DG ,DB ,DA) = (1 ,1 , 1, 1)

R (d) = min(1,1*1+0*1) = 1

G (d) = min(1,0*1+0*1) = 0

B (d) = min(1,0*1+0*1) = 0

A (d) = min(1,1*1+0*1) = 1

So we have written a 100% opaque red pixel

Now lets write a green over the top of that…

so

(RS ,GS ,BS ,AS)  = (0,1,0,1)

(RD ,GD ,BD ,AD) = (1,0,0,1)

min (AS , KA – Ad) / K-> min (1 , 1 – 1)/1 -> i = 0

(SR ,SG ,SB ,SA)  = (0, 0, 0, 1)

(DR ,DG ,DB ,DA) = (1 ,1 , 1, 1)

so

R (d) = min(1,0*0+1*1) = 1

G (d) = min(1,1*0+0*1) = 0

B (d) = min(1,0*0+0*1) = 0

A (d) = min(1,1*1+1*1) = 1

And thats still Red – not Green because of the destination alpha. As soon as a pixel gets to an alpha of 1 – its is no longer changed by subsequent writes. As long as you write the pixels closest first they will naturally clip. All the tessellated polygon artifact alphas will saturate instead of cancel out – so you will get no artifacts.

The explanation of why it works is all in the maths.

About James McParlane

CTO Massive Interactive. Ex Computer Whiz Kid - Now Grumpy Old Guru.
This entry was posted in GL. Bookmark the permalink.

2 Responses to Anti-Aliasing Tessellated Polygons in GL – Part II

  1. v.vamsi krishna says:

    hi ,
    i was trying to enable antialiasing by using glEnable(GL_POLYGON_SMOOTH) which by the way seems to be working with lines and poings i mean glEnable( GL_POINTS_SMOOTH) AND glEnable( GL_LINES_SMOOTH ) work.. but the polygon anti aliasing doesnt’ seem to be working. I have an ati card so wondering if its’ due to bad drivers for ati or some other problem. plz help out
    Thank you

  2. Define "not working"?

    What do you see?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s