Difference between Supersampling and Multisampling

I'm looking at shapes rendering recently and I learned about the edge test to decide whether a pixel is inside or outside the shape.
The next thing is how to anti-alias the edge. I found two concepts SSAA and MSAA about that but I don't know what is the difference. I know that both takes more than one sample for a pixel.

So I implemented what I've understood from Scratchpixel website on top of the fuction DrawRectangleSlowly Like so:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
void FillRectangle(win32_screen_buffer *Buffer, basis Basis, v2 Dim, v4 Color)
{
    v2 P1 = Basis.O;
    v2 P2 = Basis.O + Dim.x*Basis.X;
    v2 P3 = Basis.O + Dim.x*Basis.X + Dim.y*Basis.Y;
    v2 P4 = Basis.O + Dim.y*Basis.Y;

    s32 MinX = FloorF32ToS32(MIN(MIN(P1.x, P2.x), MIN(P3.x, P4.x))) - 1;
    s32 MinY = FloorF32ToS32(MIN(MIN(P1.y, P2.y), MIN(P3.y, P4.y))) - 1;
    s32 MaxX = CeilF32ToS32(MAX(MAX(P1.x, P2.x), MAX(P3.x, P4.x))) + 1;
    s32 MaxY = CeilF32ToS32(MAX(MAX(P1.y, P2.y), MAX(P3.y, P4.y))) + 1;
    
    if(MinX < 0)
        MinX = 0;
    if(MinY < 0)
        MinY = 0;

    if(MaxX > Buffer->Width)
        MaxX = Buffer->Width;
    if(MaxY > Buffer->Height)
        MaxY = Buffer->Height;

#define SAMPLING 1
#if SAMPLING
    v2 SampleOffsets[4] =
    {
        V2(1.0f/3, 1.0f/3), V2(2.0f/3, 1.0f/3),
        V2(1.0f/3, 2.0f/3), V2(2.0f/3, 2.0f/3)
    };
    v4 Samples[4];
#endif
                             
    f32 SA = Color.a;
    f32 SR = (Color.r*SA) * 255;
    f32 SG = (Color.g*SA) * 255;
    f32 SB = (Color.b*SA) * 255;
    
    u8 *Row = (u8 *)Buffer->Memory + MinY*Buffer->Pitch + MinX*4;
    for(s32 Y = MinY;
        Y<MaxY;
        Y++)
    {
        u32 *Pixels = (u32 *)Row;
        for(s32 X = MinX;
            X<MaxX;
            X++)
        {
            bool32 Touched = false;
#if SAMPLING
            for(u32 I=0;
                I<ARRAY_COUNT(SampleOffsets);
                I++)
            {
                v2 PP = Vi2(X, Y) + SampleOffsets[I];
#else
                v2 PP = Vi2(X, Y);
                v4 C = V4(0,0,0,0);
#endif
                if(Inner(PP-P1, Perp(P2-P1)) >= 0 &&
                   Inner(PP-P2, Perp(P3-P2)) >= 0 &&
                   Inner(PP-P3, Perp(P4-P3)) >= 0 &&
                   Inner(PP-P4, Perp(P1-P4)) >= 0)
                {
                    Touched = true;
#if SAMPLING
                    Samples[I] = V4(SR, SG, SB, SA);
#else
                    C = V4(SR, SG, SB, SA);
#endif
                }
#if SAMPLING
                else
                {
                    Samples[I] = V4(0, 0, 0, 0);
                }
            }
#endif
            if(Touched)
            {
#if SAMPLING
                v4 C = (Samples[0] + Samples[1] +
                        Samples[2] + Samples[3]);
                C *= 1.0f/4;
#endif
                f32 DA = 1.0f-C.a;
                f32 DR = DA * ((*Pixels >> 16) & 0xff);
                f32 DG = DA * ((*Pixels >> 8) & 0xff);
                f32 DB = DA * (*Pixels & 0xff);
                
                u32 Color = (RoundF32ToU32(C.r + DR) << 16 |
                             RoundF32ToU32(C.g + DG) << 8 |
                             RoundF32ToU32(C.b + DB));
                *Pixels = Color;
            }
            *Pixels++;
        }
        Row += Buffer->Pitch;
    }
}


Is this SSAA or MSAA?
The other thing is in the website mentioned they were saying you can optimize this by doing the sampling only around the edges. Any ideas on how to do that?

The last thing, can this edge test algorithm work with curved edges or I have to use something else?

Edited by Ameen Sayegh on
That's SSAA.

Supersampling takes multiple samples per pixel and calculates "average" color. Either with uniform weights, or depending on position of pixels. Supersampling basically filters whole image. It is slower, but it also anti-aliases hard edges inside textures, not only on polygon edges.

Multisampling takes only one sample per pixel, but uses positions of samples to determine how much are is inside of polygon/triangle and then reduces color value based on % of color being outside (or alpha channel). Multisampling is faster, because it uses less texture bandwidth, but it anti-aliases only polygon edges - usually that is enough.

Here's a nice explanation with more details: https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/

To do detect edges, you could change code to detect these three conditions:
1) all samples are inside polygon (basically Touched = true in your code). In such case just take one sample.
2) sample samples are outside of polygon. At least one of Inner(..) calls returns < 0 value, and at least one of Inner(..) calls returns >= 0 value. In such case take multiple samples, like you in Touched = true case. Or just take one sample, and reduce it's color based on how many samples are outside of polygon.
3) all samples are outside of polygon - all Inner(..) calls return < 0 value. Don't render the sample.

Edited by Mārtiņš Možeiko on
This is an amazing article, Thank you.

So the difference is how many times you run the pixel shader (which is, up to my knowledge, what decide what the pixel color should be). In SSAA you run the shader on every sample and you average the results but in MSAA if any of samples is covered you run the shader on the pixel not the samples and you take the coverage percentage (# covered samples divided by # samples).

In my function there isn't much of a difference since it is just an assignment.

Edited by Ameen Sayegh on