Created
March 18, 2025 18:44
-
-
Save liangfu/085738ee8004374967482f29f5397ad3 to your computer and use it in GitHub Desktop.
Rasterization (aka Rendering triangles in framebuffer) in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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