Add skybox cubemap

This commit is contained in:
2024-10-02 13:38:44 +02:00
parent 2fdc6c244d
commit de6a305e15
11 changed files with 8196 additions and 34 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

+14
View File
@@ -1,4 +1,5 @@
#include <math.h>
#include <assert.h>
#include "camera.h"
static bool first_mouse = true;
@@ -75,19 +76,32 @@ Matrix4 camera_pov(void)
Ray ray_through_screen_at(float px, float py, float aspect_ratio)
{
assert(!isnan(aspect_ratio));
Vector3 w = normalize(scale(camera_front, -1));
Vector3 u = normalize(cross(camera_up, w));
Vector3 v = cross(w, u);
assert(!isnanv(w));
assert(!isnanv(u));
assert(!isnanv(v));
float screen_h = 2 * tan(fov / 2);
float screen_w = aspect_ratio * screen_h;
assert(!isnan(screen_h));
assert(!isnan(screen_w));
Vector3 horizontal = scale(u, screen_w);
Vector3 vertical = scale(v, screen_h);
assert(!isnanv(horizontal));
assert(!isnanv(vertical));
Vector3 lower_left_corner = combine4(camera_pos, horizontal, vertical, w, 1, -0.5, -0.5, -1);
assert(!isnanv(lower_left_corner));
Vector3 dir = combine4(lower_left_corner, horizontal, vertical, camera_pos, 1, px, py, -1);
assert(!isnanv(dir));
return (Ray) {.origin=camera_pos, .direction=dir};
}
+191 -34
View File
@@ -8,6 +8,9 @@
//#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "utils.h"
#include "camera.h"
#include "vector.h"
@@ -15,11 +18,11 @@
#include "sync.h"
#include "mesh.h"
typedef struct {
Vector3 albedo;
float roughness;
float reflectance;
float metallic;
float emission_power;
Vector3 emission_color;
} Material;
@@ -30,9 +33,130 @@ typedef struct {
int screen_w;
int screen_h;
os_mutex_t screen_mutex;
float maxf(float x, float y) { return x > y ? x : y; }
float minf(float x, float y) { return x < y ? x : y; }
float absf(float x) { return x < 0 ? -x : x; }
typedef struct {
uint8_t *data[6];
int w, h, chan;
} Cubemap;
typedef enum {
CF_FRONT,
CF_BACK,
CF_LEFT,
CF_RIGHT,
CF_TOP,
CF_BOTTOM,
} CubeFace;
void load_cubemap(Cubemap *c, const char *files[6])
{
for (int i = 0; i < 6; i++) {
c->data[i] = stbi_load(files[i], &c->w, &c->h, &c->chan, 0);
if (c->data[i] == NULL) {
fprintf(stderr, "Couldn't load image '%s'\n", files[i]);
abort();
}
}
}
void free_cubemap(Cubemap *c)
{
for (int i = 0; i < 6; i++) {
stbi_image_free(c->data[i]);
}
}
Vector3 sample_cubemap(Cubemap *c, Vector3 dir)
{
float abs_x = absf(dir.x);
float abs_y = absf(dir.y);
float abs_z = absf(dir.z);
CubeFace face;
float u;
float v;
float eps = 0.1;
if (abs_x > abs_y && abs_x > abs_z) {
// X dominant
if (dir.x > 0) {
// right face
face = CF_RIGHT;
u = -dir.z / (abs_x + eps);
v = -dir.y / (abs_x + eps);
assert(!isnan(u) && !isnan(v));
} else {
// left face
face = CF_LEFT;
u = dir.z / (abs_x + eps);
v = -dir.y / (abs_x + eps);
assert(!isnan(u) && !isnan(v));
}
} else if (abs_y > abs_x && abs_y > abs_z) {
// Y dominant
assert(abs_y > 0);
if (dir.y > 0) {
// top face
face = CF_TOP;
u = dir.x / (abs_y + eps);
v = dir.z / (abs_y + eps);
assert(!isnan(u) && !isnan(v));
} else {
// bottom face
face = CF_BOTTOM;
u = dir.x / (abs_y + eps);
v = -dir.z / (abs_y + eps);
assert(!isnan(u) && !isnan(v));
}
} else {
// Z dominant
if (dir.z > 0) {
// front face
face = CF_FRONT;
u = dir.x / (abs_z + eps);
v = -dir.y / (abs_z + eps);
assert(!isnan(u) && !isnan(v));
} else {
// back face
face = CF_BACK;
u = -dir.x / (abs_z + eps);
v = -dir.y / (abs_z + eps);
if (isnan(u) || isnan(v))
fprintf(stderr, "dir={x: %f, y: %f, z: %f}\n", dir.x, dir.y, dir.z);
assert(!isnan(u) && !isnan(v));
}
}
u = 0.5f * (u + 1.0f);
v = 0.5f * (v + 1.0f);
// Pixel coordinates
int x = u * (c->w - 1);
int y = v * (c->h - 1);
if (x < 0 || y < 0 || x >= c->w || y >= c->h) {
fprintf(stderr, "u=%f, v=%f, x=%d, y=%d\n", u, v, x, y);
}
assert(x >= 0);
assert(x < c->w);
assert(y >= 0);
assert(y < c->h);
uint8_t *color = &c->data[face][(y * c->w + x) * c->chan];
return (Vector3) {
(float) color[0] / 255,
(float) color[1] / 255,
(float) color[2] / 255,
};
}
static unsigned int
compile_shader(const char *vertex_file,
@@ -351,7 +475,7 @@ Vector3 reflect(Vector3 dir, Vector3 normal)
return combine(dir, normal, 1, f);
}
#define MAX_OBJECTS 32
#define MAX_OBJECTS 1024
Object objects[MAX_OBJECTS];
int num_objects = 0;
@@ -415,9 +539,9 @@ float clamp(float x, float min, float max)
Vector3 maxv(Vector3 a, Vector3 b)
{
return (Vector3) {
max(a.x, b.x),
max(a.y, b.y),
max(a.z, b.z),
maxf(a.x, b.x),
maxf(a.y, b.y),
maxf(a.z, b.z),
};
}
@@ -444,36 +568,49 @@ float distribGGX(float NoH, float roughness) {
return k * k * (1.0 / M_PI);
}
Vector3 pixel(float x, float y)
{
Ray in_ray = ray_through_screen_at(x, y, (float) screen_w/screen_h);
Cubemap skybox;
Vector3 sky_color = {0.6, 0.7, 0.9};
Vector3 pixel(float x, float y, float aspect_ratio)
{
assert(!isnan(aspect_ratio));
Ray in_ray = ray_through_screen_at(x, y, aspect_ratio);
assert(!isnanv(in_ray.direction));
//Vector3 sky_color = {0.6, 0.7, 0.9};
//Vector3 sky_color = {0, 0, 0};
//Vector3 sky_color = {1, 1, 1};
Vector3 sky_color = {1, 0.2, 0.2};
Vector3 contrib = {1, 1, 1};
Vector3 result = {0, 0, 0};
for (int i = 0; i < 1000; i++) {
assert(!isnanv(in_ray.direction));
HitInfo hit = trace_ray(in_ray);
if (hit.object == -1) {
Vector3 sky_color = sample_cubemap(&skybox, normalize(in_ray.direction));
result = combine(result, mulv(sky_color, contrib), 1, 1);
break;
}
Material material = objects[hit.object].material;
Vector3 reflect_dir = reflect(in_ray.direction, scale(hit.normal, -1));
assert(!isnanv(reflect_dir));
Vector3 rand_dir = random_direction();
assert(!isnanv(rand_dir));
if (dotv(rand_dir, hit.normal) < 0)
rand_dir = scale(rand_dir, -1);
Vector3 out_dir = normalize(combine(rand_dir, reflect_dir, material.roughness, 1));
assert(!isnanv(out_dir));
Ray out_ray = (Ray) { combine(hit.point, out_dir, 1, 0.001), out_dir };
{
float perceptualRoughness = max(material.roughness, 0.089);
float perceptualRoughness = maxf(material.roughness, 0.089);
float roughness = perceptualRoughness * perceptualRoughness;
Vector3 v = scale(in_ray.direction, -1);
@@ -485,14 +622,14 @@ Vector3 pixel(float x, float y)
float NoV = dotv(n, v);
float NoL = dotv(n, l);
Vector3 f0 = vec_from_scalar(0.16 * material.reflectance * material.reflectance);
Vector3 f0 = combine(vec_from_scalar(0.16 * material.reflectance * material.reflectance * (1 - material.metallic)), material.albedo, 1, material.metallic);
float D = distribGGX(NoH, roughness);
Vector3 F = fresnelSchlickRoughness(LoH, f0, roughness);
float V = geometrySmith(NoV, NoL, roughness);
Vector3 specular = scale(F, (D * V) / (4.0 * NoV * NoL + 0.0001));
Vector3 diffuse = mulv(combine((Vector3) {1, 1, 1}, F, 1, -1), material.albedo);
Vector3 diffuse = mulv(combine((Vector3) {1, 1, 1}, F, 1, -1), scale(material.albedo, 1 - material.metallic));
result = combine(result, mulv(contrib, material.emission_color), 1, material.emission_power);
contrib = mulv(contrib, scale(combine(diffuse, specular, 1, 1), NoL));
@@ -559,6 +696,13 @@ os_threadreturn worker(void*)
}
if (local_accum) {
/*
float aspect_ratio = (float) screen_w/screen_h;
if (isnan(aspect_ratio)) {
fprintf(stderr, "screen_w=%d, screen_h=%d\n", screen_w, screen_h);
}
assert(!isnan(aspect_ratio));
*/
for (int k = 0; k < 1; k++) {
for (int j = 0; j < local_frame_h; j++)
@@ -568,7 +712,7 @@ os_threadreturn worker(void*)
u = 1 - u;
v = 1 - v;
Vector3 color = pixel(u, v);
Vector3 color = pixel(u, v, (float) local_frame_w/local_frame_h);
int pixel_index = j * local_frame_w + i;
local_accum[pixel_index] = combine(local_accum[pixel_index], color, 1, 1);
@@ -617,7 +761,7 @@ void update_frame_texture(float s)
u = 1 - u;
v = 1 - v;
int pixel_index = j * frame_w + i;
accum[pixel_index] = pixel(u, v);
accum[pixel_index] = pixel(u, v, (float) frame_w/frame_h);
}
accum_count++;
@@ -735,24 +879,26 @@ int main(void)
));
#elif 1
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {1, 0.3, 0.3}}, (Vector3) {0, 0, 0}, (Vector3) {10, 5, 0.1}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=0.6, .albedo=(Vector3) {0.3, 1, 0.3}}, (Vector3) {0, 0, 0}, (Vector3) {0.1, 5, 10}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=1, .albedo=(Vector3) {0.4, 0.3, 0.9}}, (Vector3) {0, -0.1, 0}, (Vector3) {10, 0.1, 10}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {7, 0, 8}, (Vector3) {1, 1, 1}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {1, 0, 1}}, (Vector3) {6, 0, 7}, (Vector3) {1, 1, 1}));
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=0.5, .albedo=(Vector3) {1, 0.4, 0}}, (Vector3) {3, 1, 3}, 1));
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {0, 1, 0}}, (Vector3) {5, 1, 3}, 1));
add_object(sphere((Material) {.emission_color={1, 0.4, 0.2}, .emission_power=3, .reflectance=0, .roughness=1, .albedo=(Vector3) {1, 0.4, 0}}, (Vector3) {3, 5, 3}, 1));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {1, 0.3, 0.3}}, (Vector3) {0, 0, 0}, (Vector3) {10, 5, 0.1}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=0.6, .albedo=(Vector3) {0.3, 1, 0.3}}, (Vector3) {0, 0, 0}, (Vector3) {0.1, 5, 10}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=1, .albedo=(Vector3) {0.4, 0.3, 0.9}}, (Vector3) {0, -0.1, 0}, (Vector3) {10, 0.1, 10}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {7, 0, 8}, (Vector3) {1, 1, 1}));
add_object(cube ((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {1, 0, 1}}, (Vector3) {6, 0, 7}, (Vector3) {1, 1, 1}));
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=0.5, .albedo=(Vector3) {1, 0.4, 0}}, (Vector3) {3, 1, 3}, 1));
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .reflectance=0, .roughness=0, .albedo=(Vector3) {0, 1, 0}}, (Vector3) {5, 1, 3}, 1));
add_object(sphere((Material) {.emission_color={1, 0.4, 0.2}, .emission_power=5, .metallic=0, .reflectance=0, .roughness=1, .albedo=(Vector3) {1, 0.4, 0}}, (Vector3) {3, 5, 3}, 1));
#endif
os_mutex_create(&frame_mutex);
os_thread workers[16];
int num_workers = 0;
for (int i = 0; i < 16; i++) {
os_thread_create(&workers[i], NULL, worker);
num_workers++;
{
const char *faces[] = {
[CF_RIGHT] = "assets/skybox/right.jpg",
[CF_LEFT] = "assets/skybox/left.jpg",
[CF_TOP] = "assets/skybox/top.jpg",
[CF_BOTTOM] = "assets/skybox/bottom.jpg",
[CF_FRONT] = "assets/skybox/front.jpg",
[CF_BACK] = "assets/skybox/back.jpg",
};
load_cubemap(&skybox, faces);
}
glfwSetErrorCallback(error_callback);
@@ -772,8 +918,6 @@ int main(void)
return -1;
}
glfwGetWindowSize(window, &screen_w, &screen_h);
glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, cursor_pos_callback);
@@ -788,6 +932,18 @@ int main(void)
glfwSwapInterval(1);
glfwGetWindowSize(window, &screen_w, &screen_h);
os_mutex_create(&frame_mutex);
os_thread workers[16];
int num_workers = 0;
for (int i = 0; i < 16; i++) {
os_thread_create(&workers[i], NULL, worker);
num_workers++;
}
unsigned int screen_program = compile_shader("assets/screen.vs", "assets/screen.fs");
if (!screen_program) { printf("Couldn't compile program\n"); return -1; }
set_uniform_i(screen_program, "screenTexture", 0);
@@ -841,7 +997,7 @@ int main(void)
Vector3 clear_color = {1, 1, 1};
update_frame_texture(0.6);
update_frame_texture(0.2);
glViewport(0, 0, screen_w, screen_h);
glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f);
@@ -859,6 +1015,7 @@ int main(void)
glfwPollEvents();
}
free_cubemap(&skybox);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
+7985
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -7,6 +7,11 @@
#define EPSILON 0.00001
bool isnanv(Vector3 v)
{
return isnan(v.x) || isnan(v.y) || isnan(v.z);
}
float deg2rad(float deg)
{
return 3.14159265358979323846 * deg / 180;
+1
View File
@@ -35,6 +35,7 @@ typedef struct {
} Sphere;
float deg2rad(float deg);
bool isnanv(Vector3 v);
Matrix4 translate_matrix(Vector3 v, float f);
Matrix4 identity_matrix(void);