# Fighting aliasing on specular highlights

MSAA does a good job removing aliases on mesh edges, but it does not help with aliased specular highlights:

Notice the aliased specular highlights on the doors and windows.

I believe the reason for this lays in the way normals are filtered after being sampled from their maps, so I wonder if there are some known methods how to make highlights more smooth?

• Are you rendering everything in HDR? Is MSAA applied on the HDR buffer? If so, try to apply it after converting to LDR. It just looks like MSAA isn't affecting those highlights at all. – snake5 Sep 30 '14 at 11:44
• How are you storing your normals? If you are storing three channels and using mipmaps you could use Toksvig's technique. This (along with other specular anti-aliasing techniques) is described very well here: blog.selfshadow.com/2011/07/22/specular-showdown – GuyRT Sep 30 '14 at 13:49
• @snake5 I use R10G10B10A2 and store 1/4 of the final color (in SRGB). I will try 16 bits but I did try before and I did not remember seing the difference. Moreover, I use XNA I cannot controll the resolve. – cubrman Sep 30 '14 at 13:56
• @GuyRT, I saw that article - the final result he showed in WebGl (red teapot) looks horrible when scaled down. The highlight is completely distorted. Did he mess it up? Cuz I don't need such "smothing". – cubrman Sep 30 '14 at 13:57
• @cubrman It's not about the render target used for rendering, it's about doing MSAA with LDR (8 bits per channel). And about XNA... well, that sucks. If this is really important to you, you might want to switch to another Direct3D wrapper for C#, like SlimDX. – snake5 Oct 1 '14 at 9:08

I've found a cheap solution in an article by Christian Schüler, where he posted a shader code used in Velvet Assassin. He smothes highlight by limiting maximum gloss to the surface curvature, which is computed by ddx and ddy of world-space normalized normals. It produces amazing results, maybe not so in noice reduction but in temporal stability of highlights (fancy graphical term, which means how much something changes across frames). Screenshots will not show the effect well enough, so I will not post them, but in action I got rid of all specular flickering on moving objects.

For those of you who are lazy (like me :)) and don't want to scan the code for the needed parts (I highly suggest that you do though, because the shader is stellar for learning purpouses) here are the relevant bits:

Add this after you transformed normal into world space and normalized it:

float3 ddxN = ddx(Normal.rgb);
float3 ddyN = ddy(Normal.rgb);
float curv2 = max( dot( ddxN, ddxN ), dot( ddyN, ddyN ) );
float gloss_max = -0.0909h - 0.0909h * log2(CURV_MOD*curv2);
gloss = min(gloss, gloss_max);
gloss = min(MAX_POW, exp2(1 + lerp(POW_MOD_MIN, POW_MOD_MAX, gloss )));


Compute specular this way:

half D = (gloss  + 1) * pow(NdotH, gloss);
specularColor = lightColor.rgb * NdotL * D;


CURV_MOD - how much smothing to apply. Be careful it can make some small objects too shiny. I have it set to 2.

MAX_POW - the maximum specular power you want to have in your engine. I have it set to 2000. POW_MOD_MIN, POW_MOD_MAX - gloss boundaries. Change the lower value if you want very dull but super broad specular at low gloss values, change the higher value if you want extremely sharp specular with low AA at high gloss values. Top value will be limited by MAX_POW as well. My values are 5, 15.

Experiment! I had to multiply the final specular result by 1/100 to make it consistent with my lighting, but for the rest I pretty seamlessly transitioned from using just a simple lit() function for specular.

Yeah one more thing - I had to change my specular intensity depending on gloss - the lower the gloss - the higher the intensity. Otherwise my highlights were too dull at low gloss.

Once again all credit for this solution goes to Christian Schüler.