aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMistivia <i@mistivia.com>2025-06-10 02:42:44 +0800
committerMistivia <i@mistivia.com>2025-06-10 02:42:44 +0800
commit07f3a1a5751e141c414d77e57c0e631feb441ef3 (patch)
tree9d15ef0ec323ce91972463fd7730f2543ca917a6 /src
parentc65dc6eab16410deb741797918df58348d7a0d04 (diff)
restructure
Diffstat (limited to 'src')
-rw-r--r--src/main.c160
-rw-r--r--src/picture.c132
-rw-r--r--src/picture.h24
-rw-r--r--src/vecmath.c48
-rw-r--r--src/vecmath.h33
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