diff options
| author | Mistivia <i@mistivia.com> | 2025-06-10 02:42:44 +0800 |
|---|---|---|
| committer | Mistivia <i@mistivia.com> | 2025-06-10 02:42:44 +0800 |
| commit | 07f3a1a5751e141c414d77e57c0e631feb441ef3 (patch) | |
| tree | 9d15ef0ec323ce91972463fd7730f2543ca917a6 /src | |
| parent | c65dc6eab16410deb741797918df58348d7a0d04 (diff) | |
restructure
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.c | 160 | ||||
| -rw-r--r-- | src/picture.c | 132 | ||||
| -rw-r--r-- | src/picture.h | 24 | ||||
| -rw-r--r-- | src/vecmath.c | 48 | ||||
| -rw-r--r-- | src/vecmath.h | 33 |
5 files changed, 397 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..6e37e37 --- /dev/null +++ b/src/main.c @@ -0,0 +1,160 @@ +#include <limits.h> +#include <float.h> +#include <stdint.h> +#include <math.h> + +#include "vecmath.h" +#include "picture.h" + +typedef enum { + kAmbientLight, + kPointLight, + kDirectionalLight, +} LightType; + +typedef struct { + LightType type; + float intensity; + Vec3f vec; +} Light; + +Vec3f screen_proj(int width, int height, Vec2i sp) { + Vec3f ret; + float maxlen = width > height ? width : height; + ret.z = 1.0; + ret.x = (sp.x - width / 2.0) / maxlen; + ret.y = (-sp.y + height / 2.0) / maxlen; + return ret; +} + +typedef struct { + Vec3f center; + float r; + Color color; +} Ball; + +Ball balls[] = { + { + .center = {.x = 0, .y = -1, .z = 3}, + .r = 1, + }, + { + .center = {.x = 2, .y = 0, .z = 4}, + .r = 1, + }, + { + .center = {.x = -2, .y = 0, .z = 4}, + .r = 1, + }, + { + .center = {0, -5001, 0}, + .r = 5000, + }, +}; + +Light lights[] = { + { + .type = kAmbientLight, + .intensity = 0.2, + }, + { + .type = kPointLight, + .intensity = 0.6, + .vec = {2.0, 1.0, 0.0}, + }, + { + .type = kDirectionalLight, + .intensity = 0.2, + .vec = {1.0, 4.0, 4.0} + }, +}; + + +Color gBackgroupColor = {0,0,0}; + +void init_color() { + balls[0].color = icolor(0x95e1d3); + balls[1].color = icolor(0xfce38a); + balls[2].color = icolor(0xf38181); + balls[3].color = icolor(0xeaffd0); + gBackgroupColor = icolor(0xeaeaea); +} + +float ball_intersect(Vec3f start, Vec3f ray, Ball *ball) { + Vec3f sc = vec3f_sub(start, ball->center); + float a = vec3f_dot(ray, ray); + float b = 2 * vec3f_dot(sc, ray); + float c = vec3f_dot(sc, sc) - ball->r * ball->r; + float delta = b*b - 4*a*c; + if (delta < 0) { + return -1; + } + float t1 = (-b + sqrt(delta)) / (2*a); + float t2 = (-b - sqrt(delta)) / (2*a); + return t1 < t2 ? t1 : t2; +} + + +Color ball_surface_color(Ball *ball, Vec3f point) { + Vec3f norm = vec3f_normalize(vec3f_sub(point, ball->center)); + float amp = 0; + for (int i = 0; i < sizeof(lights) / sizeof(Light); i++) { + if (lights[i].type == kAmbientLight) { + amp += lights[i].intensity; + } else if (lights[i].type == kPointLight) { + Vec3f l = vec3f_normalize(vec3f_sub(lights[i].vec, point)); + float prod = vec3f_dot(l, norm); + if (prod > 0) amp += prod * lights[i].intensity; + } else if (lights[i].type == kDirectionalLight) { + Vec3f l = vec3f_normalize(lights[i].vec); + float prod = vec3f_dot(l, norm); + if (prod > 0) amp += prod * lights[i].intensity; + } + } + return (Color){ + ball->color.r * amp, + ball->color.g * amp, + ball->color.b * amp + }; +} + +Color calc_color(Vec3f v) { + Vec3f start = {.x = 0, .y = 0, .z = 0}; + float tmin = 0.1; + float tmax = FLT_MAX; + + int nearest_idx = -1; + float t_nearest = FLT_MAX; + for (int i = 0; i < sizeof(balls) / sizeof(Ball); i++) { + float t = ball_intersect(start, v, &balls[i]); + if (t < t_nearest && t < tmax && t > tmin) { + t_nearest = t; + nearest_idx = i; + } + } + if (nearest_idx >= 0) { + Vec3f intersection = vec3f_add(start, vec3f_mul(t_nearest, v)); + return ball_surface_color(&balls[nearest_idx], intersection); + } else { + return gBackgroupColor; + } +} + + +int main() { + init_color(); + int img_w = 1280; + int img_h = 720; + Picture pic = new_picture(img_w, img_h); + for (int x = 0; x < img_w; x++) { + for (int y = 0; y < img_h; y++) { + Vec2i screen_pos = {x, y}; + Vec3f v = screen_proj(img_w, img_h, screen_pos); + set_pixel(pic, screen_pos, calc_color(v)); + } + } + writeBMP("test.bmp", pic); + delete_picture(pic); + return 0; +} + diff --git a/src/picture.c b/src/picture.c new file mode 100644 index 0000000..52deb0e --- /dev/null +++ b/src/picture.c @@ -0,0 +1,132 @@ +#include "picture.h" + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +Picture new_picture(int width, int height) { + Picture ret; + ret.width = width; + ret.height = height; + ret.buffer = malloc(sizeof(float) * width * height * 3); + return ret; +} + +void delete_picture(Picture pic) { + free(pic.buffer); +} + +void set_pixel(Picture pic, Vec2i pos, Color c) { + int idx = pos.y * pic.width * 3 + pos.x * 3; + pic.buffer[idx] = c.b; + pic.buffer[idx+1] = c.g; + pic.buffer[idx+2] = c.r; +} + +void normalize_picture(Picture pic) { + float maxval = 0; + for (size_t i = 0; i < pic.width * pic.height * 3; i++) { + float val = pic.buffer[i]; + if (val < 0) val = 0; + if (val > maxval) maxval = val; + } + if (maxval < 1.0) return; + for (size_t i = 0; i < pic.width * pic.height * 3; i++) { + pic.buffer[i] = pic.buffer[i] / maxval; + } +} + + +static bool fwrite_word16le(FILE* fp, uint16_t x) { + uint8_t buf[2]; + buf[0] = x & 0xff; + buf[1] = (x >> 8) & 0xff; + int r = fwrite(buf, 1, 2, fp); + if (r != 2) return false; + return true; +} + +static bool fwrite_word32le(FILE* fp, uint32_t x) { + bool ret = false; + uint16_t buf[2]; + + buf[0] = x & 0xffff; + buf[1] = (x >> 16) & 0xffff; + ret = fwrite_word16le(fp, buf[0]); + if (!ret) return ret; + ret = fwrite_word16le(fp, buf[1]); + if (!ret) return ret; + return true; +} + + +bool writeBMP(const char* filename, Picture pic) { + bool ret = false; + FILE *fp = NULL; + uint8_t *databuf = NULL; + + fp = fopen(filename, "wb"); + if (!fp) goto end; + + int row_data_len = 3 * pic.width; + int padding = (4 - row_data_len % 4) % 4; + int row_len = row_data_len + padding; + int img_len = row_len * pic.height; + int file_len = 53 + img_len; + + ret = fwrite_word16le(fp, 0x4d42); + if (!ret) goto end; + ret = fwrite_word32le(fp, file_len); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); + if (!ret) goto end; + ret = fwrite_word32le(fp, 54); + if (!ret) goto end; + + ret = fwrite_word32le(fp, 40); + if (!ret) goto end; + ret = fwrite_word32le(fp, pic.width); + if (!ret) goto end; + ret = fwrite_word32le(fp, -pic.height); + if (!ret) goto end; + ret = fwrite_word16le(fp, 1); + if (!ret) goto end; + ret = fwrite_word16le(fp, 24); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); // BI_RGB + if (!ret) goto end; + ret = fwrite_word32le(fp, img_len); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); + if (!ret) goto end; + ret = fwrite_word32le(fp, 0); + if (!ret) goto end; + databuf = malloc(img_len); + memset(databuf, 0, img_len); + for (int i = 0; i < pic.height; i++) { + for (int j = 0; j < pic.width * 3; j++) { + int fromidx = i * pic.width * 3 + j; + int toidx = i * row_len + j; + float value = pic.buffer[fromidx]; + if (value < 0) databuf[toidx] = 0; + else if (value > 1.0) { + databuf[toidx] = 255; + } else databuf[toidx] = 255 * value; + } + } + if (fwrite(databuf, 1, img_len, fp) != img_len) { + ret = false; + goto end; + } + ret = true; + +end: + if (databuf != NULL) free(databuf); + if (fp != NULL) fclose(fp); + return ret; +} diff --git a/src/picture.h b/src/picture.h new file mode 100644 index 0000000..fec5d36 --- /dev/null +++ b/src/picture.h @@ -0,0 +1,24 @@ +#ifndef PICTURE_H_ +#define PICTURE_H_ + +#include <stdbool.h> + +#include "vecmath.h" + + +typedef struct { + int width; + int height; + float *buffer; +} Picture; + +Picture new_picture(int width, int height); +void delete_picture(Picture pic); + +void set_pixel(Picture pic, Vec2i pos, Color c); +void normalize_picture(Picture pic); + +bool writeBMP(const char* filename, Picture pic); + +#endif + diff --git a/src/vecmath.c b/src/vecmath.c new file mode 100644 index 0000000..8f1c43d --- /dev/null +++ b/src/vecmath.c @@ -0,0 +1,48 @@ +#include "vecmath.h" + +#include <math.h> +#include <stdio.h> + +Vec3f vec3f_sub(Vec3f lhs, Vec3f rhs) { + return (Vec3f){ + .x = lhs.x - rhs.x, + .y = lhs.y - rhs.y, + .z = lhs.z - rhs.z, + }; +} + +Vec3f vec3f_add(Vec3f lhs, Vec3f rhs) { + return (Vec3f){ + .x = lhs.x + rhs.x, + .y = lhs.y + rhs.y, + .z = lhs.z + rhs.z, + }; +} + +float vec3f_dot(Vec3f lhs, Vec3f rhs) { + return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; +} + +Vec3f vec3f_neg(Vec3f v) { + return (Vec3f){-v.x, -v.y, -v.z}; +} + +Vec3f vec3f_normalize(Vec3f vec) { + float len = sqrt(vec3f_dot(vec, vec)); + return (Vec3f){vec.x/len, vec.y/len, vec.z/len}; +} + +Vec3f vec3f_mul(float a, Vec3f v) { + return (Vec3f){v.x * a, v.y * a, v.z * a}; +} + +Color icolor(int32_t rgb) { + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = rgb & 0xff; + return (Color){.r = r/255.0, .g = g/255.0, .b = b/255.0}; +} + +void vec3f_show(const char *name, Vec3f v) { + printf("%s(%f,%f,%f)\n", name, v.x, v.y, v.z); +} diff --git a/src/vecmath.h b/src/vecmath.h new file mode 100644 index 0000000..3b6f5b6 --- /dev/null +++ b/src/vecmath.h @@ -0,0 +1,33 @@ +#ifndef VECMATH_H_ +#define VECMATH_H_ + +#include <stdint.h> + +typedef struct { + int x; + int y; +} Vec2i; + +typedef struct { + float x; + float y; + float z; +} Vec3f; + +typedef struct { + float r; + float g; + float b; +} Color; + + +Vec3f vec3f_sub(Vec3f lhs, Vec3f rhs); +Vec3f vec3f_add(Vec3f lhs, Vec3f rhs); +float vec3f_dot(Vec3f lhs, Vec3f rhs); +Vec3f vec3f_neg(Vec3f v); +Vec3f vec3f_normalize(Vec3f vec); +Vec3f vec3f_mul(float a, Vec3f v); +void vec3f_show(const char *name, Vec3f v); +Color icolor(int32_t rgb); + +#endif |
