Going 3D

Fragments

With the edge functions, I have now access to all the points inside of a triangle but there is something missing. The color of the pixel. Until now I have assumed that every pixel has the same color. The color has not been part of each pixel but a general information of the entire object. But this is not how objects work in real life. I want to be able to interpolate the colors inside of the triangle based of the colors of the vertices.

I will first create a struct called a Fragment. A Fragment is almost the same as a pixel and it contains information of the coordinates in the screen, the color, the depth, and more. The name Fragment comes from OpenGL, this is how it is named there.

struct Fragment {
    Vec2 coords;
    float depth;
    ColorRGBA color;
};

Now, I want to talk about a concept called Barycentric Coordinates which we already touched in the previous section without mention it. This will allow us to interpolate each of the Fragments values.

Barycentric Coordinates

Barycentric coordinates are coordinates inside of a triangle. They describe the weight of a point inside of the triangle in relation to the vertices of this one. The way to calculate them is quite easy, because it uses the values of the edge functions (from the previous article) divided by the area of the triangle.

for (int x = x0; x < x1; x++) {
    Vec2 p = {x + 0.5f, y + 0.5f};

    float w0 = TriangleEdgeFunction(triangle.v1.coords,
                                    triangle.v2.coords, p);
    float w1 = TriangleEdgeFunction(triangle.v2.coords,
                                    triangle.v0.coords, p);
    float w2 = TriangleEdgeFunction(triangle.v0.coords,
                                    triangle.v1.coords, p);

    // Check inside based on winding
    bool inside = clockwise ? (w0 <= 0 && w1 <= 0 && w2 <= 0)
                            : (w0 >= 0 && w1 >= 0 && w2 >= 0);

    if (inside) {
        // Barycentric Coordinates
        float b0 = w0 * invArea;
        float b1 = w1 * invArea;
        float b2 = w2 * invArea;

        // Render the triangle
    }
}

With this coordinates I can interpolate the values of a Fragment and obtain a unique color for each of them given the weights inside the triangle. The following function takes a triangle and creates a new interpolated Fragment.

Fragment InterpolateTriangleFrament(Triangle triangle, float b0, float b1,
                                  float b2) {
    Fragment frag;

    // Perspective-correct interpolation
    float invZ0 = 1.0f / triangle.v0.coords.z;
    float invZ1 = 1.0f / triangle.v1.coords.z;
    float invZ2 = 1.0f / triangle.v2.coords.z;

    float invZ = b0 * invZ0 + b1 * invZ1 + b2 * invZ2;
    float z = 1.0f / invZ;

    // Frag Depth
    frag.depth = z;

    // Frag Color
    frag.color.r =
        (b0 * triangle.v0.color.r * invZ0 + b1 * triangle.v1.color.r * invZ1 +
         b2 * triangle.v2.color.r * invZ2) *
        z;
    frag.color.g =
        (b0 * triangle.v0.color.g * invZ0 + b1 * triangle.v1.color.g * invZ1 +
         b2 * triangle.v2.color.g * invZ2) *
        z;
    frag.color.b =
        (b0 * triangle.v0.color.b * invZ0 + b1 * triangle.v1.color.b * invZ1 +
         b2 * triangle.v2.color.b * invZ2) *
        z;

    return frag;
}

I also need to take into consideration the perspective to calculate correctly the interpolation.

Squared Wave SVG