I will give you code for a C rasterizer. I want you to write a new render_fb function that will shade the triangle based on its distance to the camera.
Make sure you use all the functions, like set_color correctly. Base your code on the render_fb_fixed_char.
Code:
```c
/* Implementation of a simple rasterized 3D renderer in pure C without external dependencies.
   This application is single-threaded and draws only triangles to the terminal.
   Shades are determined by brightness related to a light source and drawn as ASCII characters.
   Colors are fully supported through the extended palette of the terminal.
   This program only uses standard ISO C99 code. */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

/* Screen dimensions */
#define WIDTH 80
#define HEIGHT 40

/* Depth range */
#define ZNEAR 0.1f
#define ZFAR 100.0f

/* Field of view */
#define FOV 90.0f

/* Aspect ratio correction, since characters are taller than they are wide */
#define ASPECT_RATIO_CORRECTION 1.2f

/* Types and basic structures */
typedef unsigned char u8_t;
typedef float f32_t;
typedef int i32_t;
typedef unsigned int u32_t;

typedef struct { u8_t r, g, b, a; } col_t;
typedef struct { f32_t x, y, z; } vec3_t;
typedef struct { f32_t m[3][3]; } mat3_t;
typedef struct { vec3_t p; col_t c; } vert_t;
typedef struct { vert_t a, b, c; } tri_t;
typedef struct { vec3_t p; u8_t a; } light_t;
typedef struct { i32_t x, y; f32_t z; } screen_vert_t;
typedef struct { screen_vert_t a, b, c; } screen_tri_t;

/* Shades are determined by brightness related to a light source.
   By default, the camera emits a light of intensity 1.0, for simplicity */
const u8_t *shades[] = { " ", ".", ":", "-", "+", "=", "*", "#", "%", "@", "█" };
#define NSHADES (sizeof(shades) / sizeof(shades[0]))

/* Canvas to draw on screen */
col_t fb[HEIGHT][WIDTH]; // Frame buffer
f32_t zb_buffer[HEIGHT][WIDTH]; // Z-buffer

/* Utility functions */
const u8_t *get_shade(f32_t a) {
    if (a < 0.0f) a = 0.0f;
    if (a > 1.0f) a = 1.0f;
    u8_t shade = (u8_t)(a * (NSHADES - 1));
    return shades[shade];
}

void set_color(col_t c) {
    static f32_t error;
    printf("\x1b[38;2;%d;%d;%dm", c.r, c.g, c.b);

    f32_t total_chars = ASPECT_RATIO_CORRECTION + error;
    int full_chars = (int)floorf(total_chars);
    error = total_chars - full_chars;

    for(int i = 0; i < full_chars; ++i)
        printf("%s", get_shade(c.a));
}

/* Advances to the next line and resets color */
void next_line() {
    printf("\x1b[0m\n");
}

/* Clears the terminal screen and moves the cursor to the home position */
void clear_screen(){
    printf("\x1b[2J"); // Clear screen
    printf("\x1b[H");  // Move cursor to home position
}

/* Clears the frame buffer and initializes the Z-buffer */
void clear_fb() {
    for(u32_t i = 0; i < HEIGHT * WIDTH; i++) {
        ((col_t*)fb)[i] = (col_t){0, 0, 0, 255}; // Black background with full alpha
        ((f32_t*)zb_buffer)[i] = ZFAR;
    }
}

/* Function to project 3D point to 2D screen space */
screen_vert_t project_vertex(vec3_t p) {
    screen_vert_t sv;
    sv.x = (int)((p.x + 1.0f) * 0.5f * WIDTH);
    sv.y = (int)((1.0f - (p.y + 1.0f) * 0.5f) * HEIGHT); // Invert Y for screen space
    sv.z = p.z;
    return sv;
}

/* Function to compute barycentric coordinates */
void compute_barycentric(int px, int py, screen_tri_t tri, f32_t *u, f32_t *v, f32_t *w) {
    f32_t denom = (f32_t)((tri.b.y - tri.c.y)*(tri.a.x - tri.c.x) + (tri.c.x - tri.b.x)*(tri.a.y - tri.c.y));
    if (fabsf(denom) < 1e-6f) { // Degenerate triangle
        *u = *v = *w = -1.0f;
        return;
    }
    *u = ((tri.b.y - tri.c.y)*(px - tri.c.x) + (tri.c.x - tri.b.x)*(py - tri.c.y)) / denom;
    *v = ((tri.c.y - tri.a.y)*(px - tri.c.x) + (tri.a.x - tri.c.x)*(py - tri.c.y)) / denom;
    *w = 1.0f - (*u) - (*v);
}

/* Function to rasterize a single triangle using screen_tri_t */
void rasterize_triangle(tri_t tri) {
    // Project vertices to screen space
    screen_tri_t st = {
        .a = project_vertex(tri.a.p),
        .b = project_vertex(tri.b.p),
        .c = project_vertex(tri.c.p)
    };

    // Compute bounding box
    i32_t minX = st.a.x < st.b.x ? (st.a.x < st.c.x ? st.a.x : st.c.x) : (st.b.x < st.c.x ? st.b.x : st.c.x);
    i32_t maxX = st.a.x > st.b.x ? (st.a.x > st.c.x ? st.a.x : st.c.x) : (st.b.x > st.c.x ? st.b.x : st.c.x);
    i32_t minY = st.a.y < st.b.y ? (st.a.y < st.c.y ? st.a.y : st.c.y) : (st.b.y < st.c.y ? st.b.y : st.c.y);
    i32_t maxY = st.a.y > st.b.y ? (st.a.y > st.c.y ? st.a.y : st.c.y) : (st.b.y > st.c.y ? st.b.y : st.c.y);

    // Clamp to screen dimensions
    if(minX < 0) minX = 0;
    if(maxX >= WIDTH) maxX = WIDTH -1;
    if(minY < 0) minY = 0;
    if(maxY >= HEIGHT) maxY = HEIGHT -1;

    // Iterate over the bounding box
    for(int y = minY; y <= maxY; y++) {
        for(int x = minX; x <= maxX; x++) {
            f32_t u, v, w;
            compute_barycentric(x + 0.5f, y + 0.5f, st, &u, &v, &w);

            // Check if inside the triangle
            if(u >= 0.0f && v >= 0.0f && w >= 0.0f) {
                // Interpolate depth
                f32_t depth = u * st.a.z + v * st.b.z + w * st.c.z;

                // Z-buffer test
                if(depth < zb_buffer[y][x]) {
                    zb_buffer[y][x] = depth;

                    // Interpolate color
                    col_t color = {
                        .r = (u * tri.a.c.r) + (v * tri.b.c.r) + (w * tri.c.c.r),
                        .g = (u * tri.a.c.g) + (v * tri.b.c.g) + (w * tri.c.c.g),
                        .b = (u * tri.a.c.b) + (v * tri.b.c.b) + (w * tri.c.c.b),
                        .a = 255 // Full alpha
                    };

                    // Assign to frame buffer
                    fb[y][x] = color;
                }
            }
        }
    }
}

void render_fb_fixed_char() {
    const u8_t *fixed_shade = "█"; // Using the darkest shade for fixed character

    for(int y = 0; y < HEIGHT; y++) {
        for(int x = 0; x < WIDTH; x++) {
            col_t color = fb[y][x];
            set_color(color);
        }
        next_line();
    }
    // Reset color at the end
    printf("\x1b[0m");
}

int main() {
    /* Example triangle */
    tri_t triangle = {
        .a = { .p = { -1.0f, -1.0f, 1.0f }, .c = { 255, 0, 0, 255 } }, // Red
        .b = { .p = {  1.0f, -1.0f, 1.0f }, .c = { 0, 255, 0, 255 } }, // Green
        .c = { .p = {  0.0f,  1.0f, 1.0f }, .c = { 0, 0, 255, 255 } }  // Blue
    };

    /* Clear the screen */
    clear_screen();

    /* Clear frame buffer and Z-buffer */
    clear_fb();

    /* Rasterize the triangle */
    rasterize_triangle(triangle);

    /* Render the frame buffer to terminal */
    // Choose one of the rendering methods:
    render_fb_fixed_char(); // Use this for fixed character rendering
    // render_fb(); // Use this for brightness-based shading, TODO: Implement

    return 0;
}
```