aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile2
-rw-r--r--main.c272
-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
8 files changed, 402 insertions, 272 deletions
diff --git a/.gitignore b/.gitignore
index 41cd10b..6809581 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
*.bmp
a.out
+main
+.cache
+compile_commands.json
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..402d621
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+all:
+ gcc -g -fsanitize=address -lm src/*.c -o main
diff --git a/main.c b/main.c
deleted file mode 100644
index b25c212..0000000
--- a/main.c
+++ /dev/null
@@ -1,272 +0,0 @@
-#include <limits.h>
-#include <float.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
-
-typedef enum {
- kRetSucc,
- kRetFail,
-} Ret;
-
-typedef struct {
- int width;
- int height;
- float *buffer;
-} Picture;
-
-typedef struct {
- float r;
- float g;
- float b;
-} Color;
-
-typedef struct {
- int x;
- int y;
-} Vec2i;
-
-
-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;
-}
-
-Ret 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 kRetFail;
- return kRetSucc;
-}
-
-Ret fwrite_word32le(FILE* fp, uint32_t x) {
- Ret ret;
- uint16_t buf[2];
-
- buf[0] = x & 0xffff;
- buf[1] = (x >> 16) & 0xffff;
- ret = fwrite_word16le(fp, buf[0]);
- if (ret != kRetSucc) return ret;
- ret = fwrite_word16le(fp, buf[1]);
- if (ret != kRetSucc) return ret;
- return kRetSucc;
-}
-
-
-Ret writeBMP(const char* filename, Picture pic) {
- Ret ret = kRetFail;
- 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 != kRetSucc) goto end;
- ret = fwrite_word32le(fp, file_len);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 54);
- if (ret != kRetSucc) goto end;
-
- ret = fwrite_word32le(fp, 40);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, pic.width);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, -pic.height);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word16le(fp, 1);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word16le(fp, 24);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0); // BI_RGB
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, img_len);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0);
- if (ret != kRetSucc) goto end;
- ret = fwrite_word32le(fp, 0);
- if (ret != kRetSucc) 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 = kRetFail;
- goto end;
- }
- ret = kRetSucc;
-
-end:
- if (databuf != NULL) free(databuf);
- if (fp != NULL) fclose(fp);
- return ret;
-}
-
-typedef struct {
- float x;
- float y;
- float z;
-} Vec3f;
-
-Vec3f screen_proj(int width, int height, Vec2i sp) {
- Vec3f ret;
- float maxlen = width > height ? width : height;
- ret.z = 1;
- ret.x = (sp.x - width / 2.0) / maxlen;
- ret.y = (-sp.y + height / 2.0) / maxlen;
- return ret;
-}
-
-Color icolor(int r, int g, int b) {
- return (Color){.r = r/255.0, .g = g/255.0, .b = b/255.0};
-}
-
-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,
- },
-};
-
-
-Color gBackgroupColor = {0,0,0};
-
-void init_color() {
- balls[0].color = icolor(0x95, 0xe1, 0xe3);
- balls[1].color = icolor(0xfc, 0xe3, 0x8a);
- balls[2].color = icolor(0xf3, 0x81, 0x81);
- gBackgroupColor = icolor(0xea, 0xea, 0xea);
-}
-
-Vec3f vec3f_sub(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;
-}
-
-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 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]);
- // printf("t:%f\n", t);
- if (t < t_nearest && t < tmax && t > tmin) {
- t_nearest = t;
- nearest_idx = i;
- }
- }
- if (nearest_idx >= 0) {
- return balls[nearest_idx].color;
- } else {
- return gBackgroupColor;
- }
-}
-
-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;
- }
-}
-
-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/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