Skip to content

Instantly share code, notes, and snippets.

@liangfu
Created March 18, 2025 18:44
Show Gist options
  • Save liangfu/085738ee8004374967482f29f5397ad3 to your computer and use it in GitHub Desktop.
Save liangfu/085738ee8004374967482f29f5397ad3 to your computer and use it in GitHub Desktop.
Rasterization (aka Rendering triangles in framebuffer) in C
# Compiler settings
CC = gcc
CFLAGS = -Wall -Wextra -O2
LDFLAGS = -lm
# Project files
SRC = rasterizer.c
OBJ = $(SRC:.c=.o)
TARGET = rasterizer
# Default target
all: $(TARGET)
# Linking
$(TARGET): $(OBJ)
$(CC) $(OBJ) -o $(TARGET) $(LDFLAGS)
# Compilation
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Clean build files
clean:
rm -f $(OBJ) $(TARGET)
# Run the program
run: $(TARGET)
./$(TARGET)
# Display the output image (requires ImageMagick)
show: run
open output.ppm
.PHONY: all clean run show
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <string.h>
typedef struct {
float x, y, z;
} Vec3;
typedef struct {
Vec3 vertices[3];
uint32_t color;
} Triangle;
typedef struct {
int width;
int height;
uint32_t* pixels;
} FrameBuffer;
// Create frame buffer
FrameBuffer* createFrameBuffer(int width, int height) {
FrameBuffer* fb = malloc(sizeof(FrameBuffer));
fb->width = width;
fb->height = height;
fb->pixels = calloc(width * height, sizeof(uint32_t));
return fb;
}
// Clear frame buffer
void clearFrameBuffer(FrameBuffer* fb, uint32_t color) {
for (int i = 0; i < fb->width * fb->height; i++) {
fb->pixels[i] = color;
}
}
// Check if point is inside triangle using barycentric coordinates
int isInsideTriangle(Vec3 v0, Vec3 v1, Vec3 v2, float px, float py) {
// Calculate vectors
float v0v1x = v1.x - v0.x;
float v0v1y = v1.y - v0.y;
float v0v2x = v2.x - v0.x;
float v0v2y = v2.y - v0.y;
float v0px = px - v0.x;
float v0py = py - v0.y;
// Calculate dot products
float dot00 = v0v1x * v0v1x + v0v1y * v0v1y;
float dot01 = v0v1x * v0v2x + v0v1y * v0v2y;
float dot02 = v0v1x * v0px + v0v1y * v0py;
float dot11 = v0v2x * v0v2x + v0v2y * v0v2y;
float dot12 = v0v2x * v0px + v0v2y * v0py;
// Calculate barycentric coordinates
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is inside triangle
return (u >= 0) && (v >= 0) && (u + v <= 1);
}
void drawTriangle(FrameBuffer* fb, Triangle triangle) {
// Find bounding box
float minX = fminf(fminf(triangle.vertices[0].x, triangle.vertices[1].x), triangle.vertices[2].x);
float maxX = fmaxf(fmaxf(triangle.vertices[0].x, triangle.vertices[1].x), triangle.vertices[2].x);
float minY = fminf(fminf(triangle.vertices[0].y, triangle.vertices[1].y), triangle.vertices[2].y);
float maxY = fmaxf(fmaxf(triangle.vertices[0].y, triangle.vertices[1].y), triangle.vertices[2].y);
// Clip to screen bounds
minX = fmaxf(minX, 0);
maxX = fminf(maxX, fb->width - 1);
minY = fmaxf(minY, 0);
maxY = fminf(maxY, fb->height - 1);
// Rasterize
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
if (isInsideTriangle(triangle.vertices[0],
triangle.vertices[1],
triangle.vertices[2],
x + 0.5f, y + 0.5f)) {
fb->pixels[y * fb->width + x] = triangle.color;
}
}
}
}
// Save frame buffer to PPM image
void saveToPPM(FrameBuffer* fb, const char* filename) {
FILE* file = fopen(filename, "wb");
fprintf(file, "P6\n%d %d\n255\n", fb->width, fb->height);
for (int i = 0; i < fb->width * fb->height; i++) {
uint8_t r = (fb->pixels[i] >> 16) & 0xFF;
uint8_t g = (fb->pixels[i] >> 8) & 0xFF;
uint8_t b = fb->pixels[i] & 0xFF;
fwrite(&r, 1, 1, file);
fwrite(&g, 1, 1, file);
fwrite(&b, 1, 1, file);
}
fclose(file);
}
int main() {
// Create 800x600 frame buffer
FrameBuffer* fb = createFrameBuffer(800, 600);
// Clear to black
clearFrameBuffer(fb, 0x000000);
// Define some triangles
Triangle triangles[] = {
{{{100, 100, 0}, {300, 100, 0}, {200, 300, 0}}, 0xFF0000}, // Red
{{{500, 200, 0}, {700, 200, 0}, {600, 400, 0}}, 0x00FF00}, // Green
{{{300, 400, 0}, {500, 400, 0}, {400, 100, 0}}, 0x0000FF} // Blue
};
// Draw triangles
for (int i = 0; i < 3; i++) {
drawTriangle(fb, triangles[i]);
}
// Save result
saveToPPM(fb, "output.ppm");
// Cleanup
free(fb->pixels);
free(fb);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment