diff --git a/color.c b/color.c index 09e195d..1a39794 100644 --- a/color.c +++ b/color.c @@ -12,6 +12,10 @@ Color color_mul(Color c1, Color c2) { return (Color){c1.r * c2.r, c1.g * c2.g, c1.b * c2.b}; } +Color color_mul_const(double t, Color c) { + return (Color){t * c.r, t * c.g, t * c.b}; +} + Color color_random(void) { return (Color){random_double_in_range(0.0, 1.0), random_double_in_range(0.0, 1.0), diff --git a/color.h b/color.h index ad5ca59..1fb44b9 100644 --- a/color.h +++ b/color.h @@ -9,6 +9,7 @@ typedef struct Color { Color color_add(Color c1, Color c2); Color color_mul(Color c1, Color c2); +Color color_mul_const(double t, Color c); Color color_random(void); Color color_random_in_range(double min, double max); diff --git a/main.c b/main.c index 3097f2e..ef68576 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ #include "color.h" #include "hittable.h" #include "material.h" +#include "perlin.h" #include "point3.h" #include "ray.h" #include "texture.h" @@ -17,8 +18,9 @@ #define SCENE_RANDOM 0 #define SCENE_TWO_SPHERES 1 +#define SCENE_TWO_PERLIN_SPHERES 2 -#define SCENE_SELECT SCENE_TWO_SPHERES +#define SCENE_SELECT SCENE_TWO_PERLIN_SPHERES static Color ray_color(Ray r, const Hittable *world, int depth) { if (depth <= 0) @@ -120,6 +122,27 @@ static Hittable *two_spheres(Arena *arena) { return world; } +static Hittable *two_perlin_spheres(Arena *arena) { + Hittable *world = hittable_create_hittable_list(arena); + + Texture *perlin_texture = + texture_create_perlin_noise(4.0, PERLIN_DEFAULT_POINT_COUNT, arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, -1000.0, 0.0}, 1000.0, + material_create_lambertian(perlin_texture, arena), + arena), + arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, 2.0, 0.0}, 2.0, + material_create_lambertian(perlin_texture, arena), + arena), + arena); + + return world; +} + int main(void) { srand(42); @@ -161,6 +184,11 @@ int main(void) { look_from = (Point3){13.0, 2.0, 3.0}; look_at = (Point3){0.0, 0.0, 0.0}; vfov = 20.0; +#elif SCENE_SELECT == SCENE_TWO_PERLIN_SPHERES + world = two_perlin_spheres(&arena); + look_from = (Point3){13.0, 2.0, 3.0}; + look_at = (Point3){0.0, 0.0, 0.0}; + vfov = 20.0; #endif Vec3 up = {0.0, 1.0, 0.0}; @@ -198,6 +226,7 @@ int main(void) { #include "color.c" #include "hittable.c" #include "material.c" +#include "perlin.c" #include "point3.c" #include "ray.c" #include "texture.c" diff --git a/perlin.c b/perlin.c new file mode 100644 index 0000000..dd3eb5b --- /dev/null +++ b/perlin.c @@ -0,0 +1,86 @@ +#include "perlin.h" +#include "arena.h" +#include "utils.h" +#include "vec3.h" + +#include + +struct PerlinData { + Vec3 *random_vectors; + int *perm_x; + int *perm_y; + int *perm_z; +}; + +static void permute(int *p, int n) { + for (int i = n - 1; i > 0; --i) { + int target = random_int_in_range(0, i); + int tmp = p[i]; + p[i] = p[target]; + p[target] = tmp; + } +} + +static int *perlin_generate_perm(int point_count, Arena *arena) { + int *p = arena_alloc(arena, point_count * sizeof(int)); + + for (int i = 0; i < point_count; ++i) + p[i] = i; + permute(p, point_count); + return p; +} + +PerlinData *perlin_init(int point_count, Arena *arena) { + PerlinData *data = arena_alloc(arena, sizeof(PerlinData)); + data->random_vectors = arena_alloc(arena, point_count * sizeof(Vec3)); + for (int i = 0; i < point_count; ++i) + data->random_vectors[i] = vec3_normalize(vec3_random_in_range(-1.0, 1.0)); + + data->perm_x = perlin_generate_perm(point_count, arena); + data->perm_y = perlin_generate_perm(point_count, arena); + data->perm_z = perlin_generate_perm(point_count, arena); + + return data; +} + +static double perlin_interp(const Vec3 c[2][2][2], double u, double v, + double w) { + double uu = u * u * (3 - 2 * u); + double vv = v * v * (3 - 2 * v); + double ww = w * w * (3 - 2 * w); + double accum = 0.0; + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + Vec3 weight_v = {u - i, v - j, w - k}; + accum += (i * uu + (1 - i) * (1 - uu)) * (j * vv + (1 - j) * (1 - vv)) * + (k * ww + (1 - k) * (1 - ww)) * vec3_dot(c[i][j][k], weight_v); + } + } + } + return accum; +} + +double perlin_noise(const PerlinData *data, Point3 p) { + double u = p.x - floor(p.x); + double v = p.y - floor(p.y); + double w = p.z - floor(p.z); + + int i = (int)floor(p.x); + int j = (int)floor(p.y); + int k = (int)floor(p.z); + + Vec3 c[2][2][2] = {0}; + for (int di = 0; di < 2; ++di) { + for (int dj = 0; dj < 2; ++dj) { + for (int dk = 0; dk < 2; ++dk) { + c[di][dj][dk] = data->random_vectors[data->perm_x[(i + di) & 0xff] ^ + data->perm_y[(j + dj) & 0xff] ^ + data->perm_z[(k + dk) & 0xff]]; + } + } + } + + return perlin_interp(c, u, v, w); +} diff --git a/perlin.h b/perlin.h new file mode 100644 index 0000000..9261786 --- /dev/null +++ b/perlin.h @@ -0,0 +1,14 @@ +#ifndef INCLUDED_PERLIN_H +#define INCLUDED_PERLIN_H + +#include "arena.h" +#include "point3.h" + +#define PERLIN_DEFAULT_POINT_COUNT 256 + +typedef struct PerlinData PerlinData; + +PerlinData *perlin_init(int point_count, Arena *arena); +double perlin_noise(const PerlinData *data, Point3 p); + +#endif /* INCLUDED_PERLIN_H */ diff --git a/texture.c b/texture.c index 773346b..c85276d 100644 --- a/texture.c +++ b/texture.c @@ -1,5 +1,7 @@ #include "texture.h" #include "arena.h" +#include "color.h" +#include "perlin.h" #include @@ -20,6 +22,15 @@ Texture *texture_create_checker(const Texture *odd, const Texture *even, return result; } +Texture *texture_create_perlin_noise(double scale, int point_count, + Arena *arena) { + Texture *result = arena_alloc(arena, sizeof(Texture)); + result->type = TEXTURE_PERLIN_NOISE; + result->noise.data = perlin_init(point_count, arena); + result->noise.scale = scale; + return result; +} + Texture *texture_create_checker_solid_color(Color odd, Color even, double size, Arena *arena) { Texture *odd_texture = texture_create_solid_color(odd, arena); @@ -30,16 +41,16 @@ Texture *texture_create_checker_solid_color(Color odd, Color even, double size, Color texture_value(const Texture *texture, double u, double v, Point3 p) { switch (texture->type) { case TEXTURE_SOLID_COLOR: - return solid_color_value(&texture->solid_color, u, v, p); + return solid_color_value(&texture->solid_color); case TEXTURE_CHECKER: return checker_value(&texture->checker, u, v, p); + case TEXTURE_PERLIN_NOISE: + return perlin_value(&texture->noise, p); } return (Color){0.0, 0.0, 0.0}; } -Color solid_color_value(const SolidColor *solid_color, double u, double v, - Point3 p) { - (void)u, (void)v, (void)p; +Color solid_color_value(const SolidColor *solid_color) { return solid_color->value; } @@ -51,3 +62,11 @@ Color checker_value(const CheckerTexture *checker, double u, double v, return texture_value(checker->odd, u, v, p); return texture_value(checker->even, u, v, p); } + +Color perlin_value(const PerlinNoiseTexture *perlin, Point3 p) { + double noise_value = + 0.5 * (1.0 + perlin_noise(perlin->data, (Point3){perlin->scale * p.x, + perlin->scale * p.y, + perlin->scale * p.z})); + return color_mul_const(noise_value, (Color){1.0, 1.0, 1.0}); +} diff --git a/texture.h b/texture.h index f45d358..b93d834 100644 --- a/texture.h +++ b/texture.h @@ -3,11 +3,13 @@ #include "arena.h" #include "color.h" +#include "perlin.h" #include "point3.h" typedef enum TextureType { TEXTURE_SOLID_COLOR, TEXTURE_CHECKER, + TEXTURE_PERLIN_NOISE, } TextureType; typedef struct Texture Texture; @@ -22,11 +24,17 @@ typedef struct CheckerTexture { double size; } CheckerTexture; +typedef struct PerlinNoiseTexture { + const PerlinData *data; + double scale; +} PerlinNoiseTexture; + struct Texture { TextureType type; union { SolidColor solid_color; CheckerTexture checker; + PerlinNoiseTexture noise; }; }; @@ -35,13 +43,16 @@ Texture *texture_create_checker(const Texture *odd, const Texture *even, double size, Arena *arena); Texture *texture_create_checker_solid_color(Color odd, Color even, double size, Arena *arena); +Texture *texture_create_perlin_noise(double scale, int point_count, + Arena *arena); Color texture_value(const Texture *texture, double u, double v, Point3 p); -Color solid_color_value(const SolidColor *solid_color, double u, double v, - Point3 p); +Color solid_color_value(const SolidColor *solid_color); Color checker_value(const CheckerTexture *checker, double u, double v, Point3 p); +Color perlin_value(const PerlinNoiseTexture *perlin, Point3 p); + #endif /* INCLUDED_TEXTURE_H */