Add solid textures

This commit is contained in:
Jean-Michel Gorius 2022-11-13 15:18:29 +01:00
parent 9b0abbc5bb
commit 20a77499bc
8 changed files with 205 additions and 34 deletions

View File

@ -1,6 +1,7 @@
#include "arena.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -46,6 +47,7 @@ void *arena_alloc_align(Arena *arena, size_t size, size_t alignment) {
return ptr;
}
fprintf(stderr, "Arena is out of memory");
abort();
}

View File

@ -191,6 +191,13 @@ bool hittable_list_bounding_box(const HittableList *list, double time_start,
return true;
}
static void get_sphere_uv(Point3 p, double *u, double *v) {
double theta = acos(-p.x);
double phi = atan2(-p.z, p.x) + M_PI;
*u = phi / (2 * M_PI);
*v = theta / M_PI;
}
bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
HitRecord *record) {
Vec3 oc = point3_sub(r.origin, sphere->center);
@ -214,6 +221,8 @@ bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
Vec3 outward_normal =
vec3_div(point3_sub(record->p, sphere->center), sphere->radius);
hit_record_set_face_normal(record, r, outward_normal);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}
@ -270,6 +279,8 @@ bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min,
vec3_div(point3_sub(record->p, moving_sphere_center(sphere, r.time)),
sphere->radius);
hit_record_set_face_normal(record, r, outward_normal);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}

View File

@ -15,6 +15,7 @@ typedef struct HitRecord {
const Material *material;
Point3 p;
Vec3 normal;
double u, v;
double t;
bool front_face;
} HitRecord;

90
main.c
View File

@ -11,9 +11,15 @@
#include "material.h"
#include "point3.h"
#include "ray.h"
#include "texture.h"
#include "utils.h"
#include "vec3.h"
#define SCENE_RANDOM 0
#define SCENE_TWO_SPHERES 1
#define SCENE_SELECT SCENE_TWO_SPHERES
static Color ray_color(Ray r, const Hittable *world, int depth) {
if (depth <= 0)
return (Color){0, 0, 0};
@ -37,10 +43,11 @@ static Color ray_color(Ray r, const Hittable *world, int depth) {
static const Hittable *generate_random_scene(Arena *arena) {
Hittable *world = hittable_create_hittable_list(arena);
const Material *ground_material =
material_create_lambertian((Color){0.5, 0.5, 0.5}, arena);
const Hittable *ground_sphere = hittable_create_sphere((Point3){0.0, -1000.0, 0.0},
1000.0, ground_material, arena);
const Texture *checker = texture_create_checker_solid_color(
(Color){0.2, 0.3, 0.1}, (Color){0.9, 0.9, 0.9}, 10.0, arena);
const Material *ground_material = material_create_lambertian(checker, arena);
const Hittable *ground_sphere = hittable_create_sphere(
(Point3){0.0, -1000.0, 0.0}, 1000.0, ground_material, arena);
hittable_list_add(&world->list, ground_sphere, arena);
const Material *glass = material_create_dielectric(1.5, arena);
@ -53,21 +60,21 @@ static const Hittable *generate_random_scene(Arena *arena) {
if (vec3_length(point3_sub(center, (Point3){4.0, 0.2, 0.0})) > 0.9) {
if (choose_material < 0.8) {
const Material *material = material_create_lambertian(
const Material *material = material_create_lambertian_color(
color_mul(color_random(), color_random()), arena);
Point3 center2 = point3_add(
center, (Vec3){0, random_double_in_range(0.0, 0.5), 0});
const Hittable *sphere = hittable_create_moving_sphere(
center, center2, 0.0, 1.0, 0.2, material, arena);
const Hittable *sphere =
hittable_create_sphere(center, 0.2, material, arena);
hittable_list_add(&world->list, sphere, arena);
} else if (choose_material < 0.95) {
const Material *material =
material_create_metal(color_random_in_range(0.5, 1),
random_double_in_range(0.5, 1.0), arena);
const Hittable *sphere = hittable_create_sphere(center, 0.2, material, arena);
const Material *material = material_create_metal_color(
color_random_in_range(0.5, 1), random_double_in_range(0.5, 1.0),
arena);
const Hittable *sphere =
hittable_create_sphere(center, 0.2, material, arena);
hittable_list_add(&world->list, sphere, arena);
} else {
const Hittable *sphere = hittable_create_sphere(center, 0.2, glass, arena);
const Hittable *sphere =
hittable_create_sphere(center, 0.2, glass, arena);
hittable_list_add(&world->list, sphere, arena);
}
}
@ -75,9 +82,9 @@ static const Hittable *generate_random_scene(Arena *arena) {
}
const Material *lambertian =
material_create_lambertian((Color){0.4, 0.2, 0.1}, arena);
material_create_lambertian_color((Color){0.4, 0.2, 0.1}, arena);
const Material *metal =
material_create_metal((Color){0.7, 0.6, 0.5}, 0.0, arena);
material_create_metal_color((Color){0.7, 0.6, 0.5}, 0.0, arena);
const Hittable *sphere1 =
hittable_create_sphere((Point3){0.0, 1.0, 0.0}, 1.0, glass, arena);
@ -89,11 +96,30 @@ static const Hittable *generate_random_scene(Arena *arena) {
hittable_create_sphere((Point3){4.0, 1.0, 0.0}, 1.0, metal, arena);
hittable_list_add(&world->list, sphere3, arena);
Hittable *bvh_root = hittable_create_bvh_node(world->list.objects, 0, world->list.size,
0.0, 1.0, arena);
Hittable *bvh_root = hittable_create_bvh_node(
world->list.objects, 0, world->list.size, 0.0, 1.0, arena);
return bvh_root;
}
static Hittable *two_spheres(Arena *arena) {
Hittable *world = hittable_create_hittable_list(arena);
Texture *checker = texture_create_checker_solid_color(
(Color){0.2, 0.3, 0.1}, (Color){0.9, 0.9, 0.9}, 10.0, arena);
hittable_list_add(
&world->list,
hittable_create_sphere((Point3){0.0, -10.0, 0.0}, 10.0,
material_create_lambertian(checker, arena), arena),
arena);
hittable_list_add(
&world->list,
hittable_create_sphere((Point3){0.0, 10.0, 0.0}, 10.0,
material_create_lambertian(checker, arena), arena),
arena);
return world;
}
int main(void) {
srand(42);
@ -116,18 +142,31 @@ int main(void) {
/* World */
const Hittable *world = generate_random_scene(&arena);
/* Camera */
Point3 look_from = {13.0, 2.0, 3.0};
Point3 look_at = {0.0, 0.0, 0.0};
Vec3 up = {0.0, 1.0, 0.0};
double vfov = 40.0;
double aperture = 0.0;
double dist_to_focus = 10.0;
double aperture = 0.1;
const Hittable *world = 0;
#if SCENE_SELECT == SCENE_RANDOM
world = generate_random_scene(&arena);
look_from = (Point3){13.0, 2.0, 3.0};
look_at = (Point3){0.0, 0.0, 0.0};
vfov = 20.0;
aperture = 0.1;
#elif SCENE_SELECT == SCENE_TWO_SPHERES
world = two_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};
Camera camera;
camera_init(&camera, look_from, look_at, up, 20, aspect_ratio, aperture,
camera_init(&camera, look_from, look_at, up, vfov, aspect_ratio, aperture,
dist_to_focus, 0.0, 1.0);
printf("P3\n%u %u\n255\n", image_width, image_height);
@ -161,5 +200,6 @@ int main(void) {
#include "material.c"
#include "point3.c"
#include "ray.c"
#include "texture.c"
#include "utils.c"
#include "vec3.c"

View File

@ -23,13 +23,18 @@ bool material_scatter(const Material *material, Ray r,
return false;
}
Material *material_create_lambertian(Color albedo, Arena *arena) {
Material *material_create_lambertian(const Texture *albedo, Arena *arena) {
Material *result = arena_alloc(arena, sizeof(Material));
result->type = MATERIAL_LAMBERTIAN;
result->lambertian.albedo = albedo;
return result;
}
Material *material_create_lambertian_color(Color albedo, Arena *arena) {
return material_create_lambertian(texture_create_solid_color(albedo, arena),
arena);
}
bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const HitRecord *record, Color *attenuation,
Ray *scattered) {
@ -41,11 +46,13 @@ bool lambertian_scatter(const Lambertian *lambertian, Ray r,
scatter_direction = record->normal;
*scattered = (Ray){record->p, scatter_direction, r.time};
*attenuation = lambertian->albedo;
*attenuation =
texture_value(lambertian->albedo, record->u, record->v, record->p);
return true;
}
Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) {
Material *material_create_metal(const Texture *albedo, double fuzziness,
Arena *arena) {
Material *result = arena_alloc(arena, sizeof(Material));
result->type = MATERIAL_METAL;
result->metal.albedo = albedo;
@ -53,6 +60,12 @@ Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) {
return result;
}
Material *material_create_metal_color(Color albedo, double fuzziness,
Arena *arena) {
return material_create_metal(texture_create_solid_color(albedo, arena),
fuzziness, arena);
}
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
Color *attenuation, Ray *scattered) {
Vec3 reflected = vec3_reflect(vec3_normalize(r.direction), record->normal);
@ -63,7 +76,7 @@ bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
vec3_mul(metal->fuzziness, vec3_random_in_unit_sphere())),
r.time,
};
*attenuation = metal->albedo;
*attenuation = texture_value(metal->albedo, record->u, record->v, record->p);
return vec3_dot(scattered->direction, record->normal) > 0;
}

View File

@ -2,8 +2,8 @@
#define INCLUDED_MATERIAL_H
#include "arena.h"
#include "color.h"
#include "ray.h"
#include "texture.h"
#include <stdbool.h>
@ -16,11 +16,11 @@ typedef enum MaterialType {
} MaterialType;
typedef struct Lambertian {
Color albedo;
const Texture *albedo;
} Lambertian;
typedef struct Metal {
Color albedo;
const Texture *albedo;
double fuzziness;
} Metal;
@ -41,12 +41,16 @@ bool material_scatter(const Material *material, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered);
Material *material_create_lambertian(Color albedo, Arena *arena);
Material *material_create_lambertian(const Texture *albedo, Arena *arena);
Material *material_create_lambertian_color(Color albedo, Arena *arena);
bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered);
Material *material_create_metal(Color albedo, double fuzziness, Arena *arena);
Material *material_create_metal(const Texture *albedo, double fuzziness,
Arena *arena);
Material *material_create_metal_color(Color albedo, double fuzziness,
Arena *arena);
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
Color *attenuation, Ray *scattered);

53
texture.c Normal file
View File

@ -0,0 +1,53 @@
#include "texture.h"
#include "arena.h"
#include <math.h>
Texture *texture_create_solid_color(Color value, Arena *arena) {
Texture *result = arena_alloc(arena, sizeof(Texture));
result->type = TEXTURE_SOLID_COLOR;
result->solid_color.value = value;
return result;
}
Texture *texture_create_checker(const Texture *odd, const Texture *even,
double size, Arena *arena) {
Texture *result = arena_alloc(arena, sizeof(Texture));
result->type = TEXTURE_CHECKER;
result->checker.odd = odd;
result->checker.even = even;
result->checker.size = size;
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);
Texture *even_texture = texture_create_solid_color(even, arena);
return texture_create_checker(odd_texture, even_texture, size, arena);
}
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);
case TEXTURE_CHECKER:
return checker_value(&texture->checker, u, v, 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;
return solid_color->value;
}
Color checker_value(const CheckerTexture *checker, double u, double v,
Point3 p) {
double sines = sin(checker->size * p.x) * sin(checker->size * p.y) *
sin(checker->size * p.z);
if (sines < 0.0)
return texture_value(checker->odd, u, v, p);
return texture_value(checker->even, u, v, p);
}

47
texture.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef INCLUDED_TEXTURE_H
#define INCLUDED_TEXTURE_H
#include "arena.h"
#include "color.h"
#include "point3.h"
typedef enum TextureType {
TEXTURE_SOLID_COLOR,
TEXTURE_CHECKER,
} TextureType;
typedef struct Texture Texture;
typedef struct SolidColor {
Color value;
} SolidColor;
typedef struct CheckerTexture {
const Texture *odd;
const Texture *even;
double size;
} CheckerTexture;
struct Texture {
TextureType type;
union {
SolidColor solid_color;
CheckerTexture checker;
};
};
Texture *texture_create_solid_color(Color value, Arena *arena);
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);
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 checker_value(const CheckerTexture *checker, double u, double v,
Point3 p);
#endif /* INCLUDED_TEXTURE_H */