Set C standard to C11 and add new assets

Changed CMakeLists.txt to set the C standard to C11.
Added multiple binary image files for new visual assets.
Added several new image files to enhance rendering capabilities.
Changed stb_image.h to improve support for various image formats.
Changed ray tracing engine to enhance ray creation and intersection.
Changed triangle structure to use a vertex array for better attribute handling.
Changed scene initialization to accommodate new texture management.
This commit is contained in:
2025-04-29 01:43:52 +09:00
parent 4db14ffdb0
commit 3de6b83d32
53 changed files with 8939 additions and 162671 deletions

View File

@@ -1,6 +1,7 @@
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
cmake_minimum_required(VERSION 3.10)
set(CMAKE_C_STANDARD 11)
set(PROJECT_NAME SimpleRayTracer)
project(${PROJECT_NAME} LANGUAGES C)

BIN
assets/00_skap.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
assets/01_STUB-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
assets/01_STUB.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
assets/01_S_ba.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/01_S_kap-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/01_S_kap.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
assets/01_St_kp-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/01_St_kp.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
assets/KAMEN-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
assets/KAMEN-stup.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
assets/KAMEN.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
assets/prozor1.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/reljef-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/reljef.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
assets/sp_luk-bump.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets/sp_luk.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/sponza.fbx Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
assets/vrata_ko.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
assets/vrata_kr.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
assets/x01_st-bump.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/x01_st.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

7988
external/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,26 +5,34 @@
#include "Geometry/Triangle.h"
#include "Rendering/Scene.h"
#define SIGN_BIT(sign, axis) (((sign) >> (axis)) & 1)
typedef struct
{
vec3s origin;
vec3s direction;
vec3s inverse_direction;
uint8_t sign;
} ray_t;
typedef struct
{
vec3s point;
vec3s normal;
vec2s uv;
uint64_t triangle_id;
float distance;
bool hit;
} hit_result_t;
hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle);
bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_out);
void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes,
ray_t ray_create(vec3s origin, vec3s direction);
vec3s offset_ray_origin(vec3s point, vec3s normal, vec3s wo);
hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle);
bool ray_intersect_aabb(const ray_t* ray, aabb_t aabb, float* enter_out, float* exit_out);
void ray_intersect_bvh(const ray_t* ray, const bvh_node_t* bvh_nodes,
const uint64_t* primitive_indices, const triangle_collection_t* all_triangles, uint64_t node_index,
float* closest_out, hit_result_t* best_hit_out);
hit_result_t ray_intersect_scene(ray_t ray, const scene_t* scene);
hit_result_t ray_intersect_scene(const ray_t* ray, const scene_t* scene);
#endif // RAY_INTERSECTION_H

View File

@@ -6,9 +6,10 @@
#include "cglm/struct/vec3.h"
#include <math.h>
#define RAY_EPSILON 5.192092896e-05F
#define RAY_EPSILON 6.192092896e-05F
#define COLOR_CLAMP(color) (unsigned char)fminf(fmaxf(color, 0.0f), 255.0f)
// Any better way to do this?
#define BIAS_RAY_ORIGION(positon, normal) glms_vec3_add(positon, glms_vec3_scale(normal, RAY_EPSILON))
inline float random_float()
@@ -16,7 +17,13 @@ inline float random_float()
return (float)rand() / (float)RAND_MAX;
}
inline uint32_t hash_uint32(uint32_t x) {
inline float gamma(int n)
{
return (n * FLT_EPSILON) / (1.0f - n * FLT_EPSILON);
}
inline uint32_t hash_uint32(uint32_t x)
{
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
@@ -28,7 +35,6 @@ inline bool has_flag(int flags, int flag)
return (flags & flag) != 0;
}
inline versors euler_to_quat(float x, float y, float z)
{
versors qx = glms_quatv(glm_rad(x), (vec3s){1.0f, 0.0f, 0.0f});

77
header/Common/String.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef STRING_H
#define STRING_H
#include <ctype.h>
#include <string.h>
static inline bool is_absolute_path(const char* path)
{
if (!path || !*path) return false;
#ifdef _WIN32
// 1) Drive-letter paths: "C:\…", "D:/…"
if (isalpha((unsigned char)path[0]) &&
path[1] == ':' &&
(path[2] == '\\' || path[2] == '/'))
{
return true;
}
// 2) UNC paths: "\\server\share\…"
if (path[0] == '\\' && path[1] == '\\')
{
return true;
}
return false;
#else
// POSIX: anything starting with '/'
return path[0] == '/';
#endif
}
static inline void get_path_directory(const char* path, char* out, size_t out_size)
{
if (path == NULL || out == NULL || out_size == 0)
{
if (out && out_size > 0)
{
out[0] = '\0'; // ensure output is empty on invalid input
}
return;
}
const char* last_slash = strrchr(path, '/');
if (!last_slash)
{
if (out_size > 0)
out[0] = '\0';
return;
}
size_t dir_len = (size_t)(last_slash - path);
if (dir_len >= out_size)
{
dir_len = out_size - 1;
}
memcpy(out, path, dir_len);
out[dir_len] = '/';
out[dir_len + 1] = '\0';
}
static inline void string_join(const char* a, const char* b, char* out, size_t out_size)
{
if (a == NULL || b == NULL || out == NULL || out_size == 0) return;
size_t a_len = strlen(a);
size_t b_len = strlen(b);
if (a_len + b_len + 1 >= out_size) return; // +1 for null terminator
strcpy(out, a);
strcat(out, b);
}
#endif // STRING_H

View File

@@ -19,57 +19,62 @@ inline void quad_create(vec3s center, vec3s forward, vec3s up, float size, uint8
vec3s bottom_right = glms_vec3_sub(temp_add, scaled_up);
vec3s bottom_left = glms_vec3_sub(temp_sub, scaled_up);
triangle_create(top_left, top_right, bottom_left, material_id, collection);
triangle_create(top_right, bottom_right, bottom_left, material_id, collection);
vertex_t vertex_1 = {.position = top_left, .normal = forward, .uv = {0.0f, 0.0f}};
vertex_t vertex_2 = {.position = top_right, .normal = forward, .uv = {1.0f, 0.0f}};
vertex_t vertex_3 = {.position = bottom_right, .normal = forward, .uv = {1.0f, 1.0f}};
vertex_t vertex_4 = {.position = bottom_left, .normal = forward, .uv = {0.0f, 1.0f}};
triangle_create(vertex_1, vertex_2, vertex_4, material_id, collection);
triangle_create(vertex_2, vertex_3, vertex_4, material_id, collection);
}
inline void sphere_create(vec3s center, float radius, uint8_t material_id, triangle_collection_t* collection)
{
int segments = 16;
int rings = 16;
for (int i = 0; i < rings; i++)
{
float theta1 = (float)i / (float)rings * (float)M_PI;
float theta2 = (float)(i + 1) / (float)rings * (float)M_PI;
for (int j = 0; j < segments; j++)
{
float phi1 = (float)j / (float)segments * 2.0f * (float)M_PI;
float phi2 = (float)(j + 1) / (float)segments * 2.0f * (float)M_PI;
vec3s p1 = {
center.x + radius * sinf(theta1) * cosf(phi1),
center.y + radius * cosf(theta1),
center.z + radius * sinf(theta1) * sinf(phi1)
};
vec3s p2 = {
center.x + radius * sinf(theta2) * cosf(phi1),
center.y + radius * cosf(theta2),
center.z + radius * sinf(theta2) * sinf(phi1)
};
vec3s p3 = {
center.x + radius * sinf(theta2) * cosf(phi2),
center.y + radius * cosf(theta2),
center.z + radius * sinf(theta2) * sinf(phi2)
};
vec3s p4 = {
center.x + radius * sinf(theta1) * cosf(phi2),
center.y + radius * cosf(theta1),
center.z + radius * sinf(theta1) * sinf(phi2)
};
// vec3s n1 = glms_vec3_normalize(glms_vec3_sub(p1, center));
// vec3s n2 = glms_vec3_normalize(glms_vec3_sub(p2, center));
// vec3s n3 = glms_vec3_normalize(glms_vec3_sub(p3, center));
// vec3s n4 = glms_vec3_normalize(glms_vec3_sub(p4, center));
// triangle_create_with_normals(p3, p2, p1, n3, n2, n1, material_id, collection);
// triangle_create_with_normals(p3, p1, p4, n3, n1, n4, material_id, collection);
triangle_create(p3, p2, p1, material_id, collection);
triangle_create(p3, p1, p4, material_id, collection);
}
}
}
// inline void sphere_create(vec3s center, float radius, uint8_t material_id, triangle_collection_t* collection)
// {
// int segments = 16;
// int rings = 16;
//
// for (int i = 0; i < rings; i++)
// {
// float theta1 = (float)i / (float)rings * (float)M_PI;
// float theta2 = (float)(i + 1) / (float)rings * (float)M_PI;
//
// for (int j = 0; j < segments; j++)
// {
// float phi1 = (float)j / (float)segments * 2.0f * (float)M_PI;
// float phi2 = (float)(j + 1) / (float)segments * 2.0f * (float)M_PI;
//
// vec3s p1 = {
// center.x + radius * sinf(theta1) * cosf(phi1),
// center.y + radius * cosf(theta1),
// center.z + radius * sinf(theta1) * sinf(phi1)
// };
// vec3s p2 = {
// center.x + radius * sinf(theta2) * cosf(phi1),
// center.y + radius * cosf(theta2),
// center.z + radius * sinf(theta2) * sinf(phi1)
// };
// vec3s p3 = {
// center.x + radius * sinf(theta2) * cosf(phi2),
// center.y + radius * cosf(theta2),
// center.z + radius * sinf(theta2) * sinf(phi2)
// };
// vec3s p4 = {
// center.x + radius * sinf(theta1) * cosf(phi2),
// center.y + radius * cosf(theta1),
// center.z + radius * sinf(theta1) * sinf(phi2)
// };
//
// // vec3s n1 = glms_vec3_normalize(glms_vec3_sub(p1, center));
// // vec3s n2 = glms_vec3_normalize(glms_vec3_sub(p2, center));
// // vec3s n3 = glms_vec3_normalize(glms_vec3_sub(p3, center));
// // vec3s n4 = glms_vec3_normalize(glms_vec3_sub(p4, center));
//
// // triangle_create_with_normals(p3, p2, p1, n3, n2, n1, material_id, collection);
// // triangle_create_with_normals(p3, p1, p4, n3, n1, n4, material_id, collection);
// triangle_create(p3, p2, p1, material_id, collection);
// triangle_create(p3, p1, p4, material_id, collection);
// }
// }
// }
#endif // GEOMETRY_UTILITIES_H

View File

@@ -3,22 +3,25 @@
#include "Material/Material.h"
#include "Geometry/Triangle.h"
#include "Rendering/Scene.h"
#include <stdint.h>
// TODO: Currently transformation does not work because we store every triangle in to the same buffer and when bulding the bvh, we only consider that triangle buffer.
// One solution for this is we can have two levels of bvh, one for scene and one for each mesh. The mesh level bvh will apply the transformation to the triangles.
// The scene level bvh onlt tells the ray which mesh to check and the mesh level bvh will tell the ray which triangle to check.
// The scene level bvh only tells the ray which mesh to check and the mesh level bvh will tell the ray which triangle to check.
// For instancing, we may need another mesh_model_t struct to store the actual bvh and triangle data(like id and size), and each mesh_entity_t will have a transformation matrix and the id to that mesh_model_t.
// This way we can share the same triangle and bvh for multiple instances of the same mesh, and we can also apply different transformations to each instance.
typedef struct
{
mat4s local_to_world;
uint64_t id;
uint64_t size;
uint64_t triangle_id;
uint64_t triangle_count;
uint16_t material_id;
uint16_t material_count;
} mesh_entity_t;
//HACK: We should handle the material from mesh, not user input.
mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_collection_t* triangles, material_collection_t* materials);
mesh_entity_t mesh_load(const char* filename, scene_t* scene);
#endif // MESH_H

View File

@@ -7,16 +7,19 @@
typedef struct
{
vec3s point_1;
vec3s point_2;
vec3s point_3;
vec3s normal_1;
vec3s normal_2;
vec3s normal_3;
vec3s normal_face;
vec3s position;
vec3s normal;
vec3s color;
vec2s uv;
} vertex_t;
typedef struct
{
vertex_t vertices[3];
vec3s face_normal;
uint8_t material_id;
}triangle_t;
} triangle_t;
//TODO: Handle triangle remove, we can use something like sparse set.
typedef struct
@@ -25,27 +28,24 @@ typedef struct
uint64_t size;
triangle_t* buffer;
}triangle_collection_t;
} triangle_collection_t;
bool triangle_collection_init(triangle_collection_t* triangles, size_t size);
bool triangle_collection_init(size_t size, triangle_collection_t* triangles);
void triangle_collection_resize(triangle_collection_t* collection, size_t size);
void triangle_collection_free(triangle_collection_t* collection);
void triangle_create_with_normals(vec3s point1, vec3s point2, vec3s point3,
vec3s normal1, vec3s normal2, vec3s normal3,
uint8_t material_id, triangle_collection_t* collection);
void triangle_create(vec3s point1, vec3s point2, vec3s point3, uint8_t material_id, triangle_collection_t* collection);
void triangle_create(vertex_t v1, vertex_t v2, vertex_t v3, uint8_t material_id, triangle_collection_t* collection);
inline vec3s triangle_centroid(const triangle_t triangle)
{
return glms_vec3_scale(glms_vec3_add(glms_vec3_add(triangle.point_1, triangle.point_2), triangle.point_3), 1.0f / 3.0f);
return glms_vec3_scale(glms_vec3_add(glms_vec3_add(triangle.vertices[0].position, triangle.vertices[1].position), triangle.vertices[2].position), 1.0f / 3.0f);
}
inline aabb_t compute_bounds_triangle(triangle_t triangle)
{
aabb_t bounds;
bounds.min = glms_vec3_minv(triangle.point_1, glms_vec3_minv(triangle.point_2, triangle.point_3));
bounds.max = glms_vec3_maxv(triangle.point_1, glms_vec3_maxv(triangle.point_2, triangle.point_3));
bounds.min = glms_vec3_minv(triangle.vertices[0].position, glms_vec3_minv(triangle.vertices[1].position, triangle.vertices[2].position));
bounds.max = glms_vec3_maxv(triangle.vertices[0].position, glms_vec3_maxv(triangle.vertices[1].position, triangle.vertices[2].position));
return bounds;
}

View File

@@ -7,11 +7,18 @@
#include "Material/Material.h"
#include "cglm/struct/vec3.h"
#define SKY_DATA_CAPACITY 64
typedef enum {
SKY_TYPE_CONSTANT,
} sky_type_t;
typedef struct
{
vec3s normal;
vec3s hit_point;
vec3s wo;
vec2s uv;
uint16_t bounce_depth;
@@ -19,12 +26,22 @@ typedef struct
const material_t* material;
} light_shading_context_t;
#ifdef __STDC_ALIGN__
#define SKY_ALIGN _Alignof(max_align_t)
#else
#define SKY_ALIGN (sizeof(void*))
#endif
typedef vec3s (*evaluate_bsdf_sky_f) (const void* data, const light_shading_context_t* context, vec3s throughput, uint32_t index);
typedef struct
{
evaluate_bsdf_sky_f evaluate_bsdf_sky;
const void* data;
sky_type_t type;
uint16_t data_size;
_Alignas(SKY_ALIGN)
char data[SKY_DATA_CAPACITY];
} sky_light_t;
typedef enum

View File

@@ -2,6 +2,7 @@
#define SKY_LIGHT_H
#include "Light.h"
#include <string.h>
typedef struct
{
@@ -13,10 +14,14 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
inline sky_light_t sky_create_constant_sky(const constant_sky_data_t* data)
{
return (sky_light_t){
sky_light_t light = {
.evaluate_bsdf_sky = evaluate_bsdf_const_sky,
.data = data,
.type = SKY_TYPE_CONSTANT,
.data_size = sizeof(constant_sky_data_t),
};
memcpy(light.data, data, sizeof(constant_sky_data_t));
return light;
}
#endif // SKY_LIGHT_H

View File

@@ -4,26 +4,33 @@
#include "Algorithm/Sobol.h"
#include "Common.h"
#define PROPERTY_SIZE 64
#define INVALID_MATERIAL_ID 255
typedef struct
{
vec3s normal;
vec3s position;
vec3s wi;
vec3s wo;
vec2s uv;
} shading_context_t;
typedef vec3s (*sample_bsdf_f)(const void* data, vec3s normal, vec3s wo, uint32_t index, uint32_t bounce, float* pdf_out);
typedef float (*sample_bsdf_pdf_f)(const void* data, vec3s normal, vec3s wo, vec3s wi);
typedef vec3s (*evaluate_bsdf_f)(const shading_context_t* context, const void* data);
typedef void (*compute_surface_data_f)(const shading_context_t* context, const void* properties, void* surface_data_out);
typedef vec3s (*sample_bsdf_f)(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data, uint32_t index, uint32_t bounce, float* pdf_out);
typedef float (*sample_bsdf_pdf_f)(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data);
typedef vec3s (*evaluate_bsdf_f)(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data);
typedef struct
{
char properties[PROPERTY_SIZE];
// TODO: alpha, displacement, etc.
vec3s emission; // We have to have emission outside of data because data is only for bsdf, and emission is not part of bsdf. Maybe another shading properties struct is needed if the number of properties increases.
compute_surface_data_f compute_surface_data;
sample_bsdf_f sample_bsdf;
sample_bsdf_pdf_f sample_bsdf_pdf;
evaluate_bsdf_f evaluate_bsdf;
void* data;
} material_t;
typedef struct
@@ -40,14 +47,14 @@ typedef struct
material_t* buffer;
} material_collection_t;
bool material_collection_init(size_t size, material_collection_t* materials);
bool material_collection_init(uint8_t size, material_collection_t* materials);
void material_collection_resize(material_collection_t* materials, size_t size);
void material_collection_free(material_collection_t* materials);
material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, void* data, size_t size_of_data, material_collection_t* collection);
material_entity_t material_create(const void* properties, size_t properties_size, compute_surface_data_f surface_data, sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, material_collection_t* collection);
vec3s sample_material_bsdf(const material_t* material, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out);
float sample_material_bsdf_pdf(const material_t* material, vec3s normal, vec3s wo, vec3s wi);
vec3s sample_material_bsdf(const material_t* material, vec3s normal, vec3s wo, vec2s uv, uint32_t sample_index, uint32_t bounce, float* pdf_out);
float sample_material_bsdf_pdf(const material_t* material, vec3s normal, vec3s wo, vec3s wi, vec2s uv);
vec3s evaluate_material_bsdf(const material_t* material, const shading_context_t* context);
#endif // MATERIAL_H

View File

@@ -2,22 +2,41 @@
#define SIMPLE_LIT_H
#include "Material.h"
#include "cglm/types-struct.h"
#include "Rendering/Texture.h"
#include "cglm/struct/vec3.h"
typedef struct
{
vec3s albedo;
float roughness;
float metallic;
}simple_lit_data_t;
} simple_lit_data_t;
vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out);
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* data);
float sample_bsdf_pdf_simple_lit(const void* data, vec3s normal, vec3s wo, vec3s wi);
inline material_entity_t material_create_simple_lit(simple_lit_data_t* lit_data, material_collection_t* collection)
typedef struct
{
return material_create(sample_bsdf_simple_lit, sample_bsdf_pdf_simple_lit, evaluate_bsdf_simple_lit, lit_data, sizeof(simple_lit_data_t), collection);
vec3s albedo;
float roughness;
float metallic;
const texture_t* albedo_texture;
const texture_t* roughness_texture;
const texture_t* metallic_texture;
} simple_lit_properties_t;
void simple_lit_data_default(const shading_context_t* context, const void* properties, void* data_out);
vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data, uint32_t sample_index, uint32_t bounce, float* pdf_out);
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data);
float sample_bsdf_pdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data);
inline material_entity_t material_create_simple_lit(const simple_lit_properties_t* properties, compute_surface_data_f surface_data, material_collection_t* collection)
{
return material_create(properties, sizeof(simple_lit_properties_t), surface_data, sample_bsdf_simple_lit, sample_bsdf_pdf_simple_lit, evaluate_bsdf_simple_lit, collection);
}
inline material_entity_t material_create_simple_lit_default(const simple_lit_properties_t* properties, material_collection_t* collection)
{
return material_create(properties, sizeof(simple_lit_properties_t), simple_lit_data_default, sample_bsdf_simple_lit, sample_bsdf_pdf_simple_lit, evaluate_bsdf_simple_lit, collection);
}
#endif // SIMPLE_LIT_H

View File

@@ -9,6 +9,7 @@ typedef enum
DEBUG_NONE = 0,
DEBUG_BVH = 1,
DEBUG_SOBOL = 2,
DEBUG_UV = 3,
} debug_flag_t;
static const vec4s DEBUG_COLOR_BVH = {0.0f, 1.0f, 0.0f, 0.005f}; // Green

View File

@@ -5,8 +5,9 @@
#include "Camera.h"
#include "Lighting/Light.h"
#include "Material/Material.h"
#include "RenderTarget.h"
#include "Geometry/Triangle.h"
#include "Rendering/RenderTarget.h"
#include "Rendering/Texture.h"
typedef struct
{
@@ -14,10 +15,11 @@ typedef struct
bvh_tree_t bvh_tree;
triangle_collection_t triangles;
material_collection_t materials;
texture_collection_t textures;
light_collection_t lights;
} scene_t;
bool scene_init(scene_t* scene, uint64_t triangle_count, uint8_t material_count, uint32_t punctual_light_count);
bool scene_init(scene_t* scene, uint64_t triangle_count, uint16_t texture_count, uint8_t material_count, uint32_t punctual_light_count);
bool scene_build_bvh(scene_t* scene);
void scene_free(scene_t* scene);

View File

@@ -0,0 +1,53 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include "cglm/struct/vec4.h"
#include <stdint.h>
#include <stdbool.h>
#define INVALID_TEXTURE_ID UINT16_MAX
typedef enum
{
REPEAT,
CLAMP,
} wrap_mode_t;
typedef enum
{
NEAREST,
LINEAR,
} filter_mode_t;
typedef struct
{
uint32_t width;
uint32_t height;
wrap_mode_t wrap_mode;
filter_mode_t filter_mode;
uint8_t channel_count;
uint8_t* data;
} texture_t;
typedef struct
{
uint16_t count;
uint16_t size;
texture_t* buffer;
} texture_collection_t;
typedef struct
{
uint16_t id;
} texture_entity_t;
bool texture_collection_init(uint16_t size, texture_collection_t* textures);
void texture_collection_resize(texture_collection_t* textures, uint16_t size);
void texture_collection_free(texture_collection_t* textures);
texture_entity_t texture_load(const char* filename, bool srgb, texture_collection_t* textures);
vec4s texture_sample(const texture_t* texture, float u, float v);
void texture_free(texture_t* texture);
#endif // TEXTURE_H

View File

@@ -62,9 +62,9 @@ static inline aabb_t compute_primitives_aabb(const triangle_collection_t* triang
for (uint64_t i = start + 1; i < start + count; ++i)
{
triangle_index = primitive_indices[i];
aabb_growth(&bounds, triangles->buffer[triangle_index].point_1);
aabb_growth(&bounds, triangles->buffer[triangle_index].point_2);
aabb_growth(&bounds, triangles->buffer[triangle_index].point_3);
aabb_growth(&bounds, triangles->buffer[triangle_index].vertices[0].position);
aabb_growth(&bounds, triangles->buffer[triangle_index].vertices[1].position);
aabb_growth(&bounds, triangles->buffer[triangle_index].vertices[2].position);
}
return bounds;

View File

@@ -22,7 +22,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
uint16_t depth = 0;
while (depth < max_depth)
{
hit_result_t closest_hit = ray_intersect_scene(active_ray, scene);
hit_result_t closest_hit = ray_intersect_scene(&active_ray, scene);
if (!closest_hit.hit)
{
@@ -34,7 +34,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
float weight = power_heuristic(pdf_bsdf, pdf_nee);
sky_light = glms_vec3_scale(sky_light, weight);
}
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f)); // TODO: Physical Skybox
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f));
break;
}
@@ -47,6 +47,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
.normal = closest_hit.normal,
.hit_point = closest_hit.point,
.wo = active_ray.direction,
.uv = closest_hit.uv,
.bounce_depth = depth,
@@ -67,7 +68,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
// Bounce and prepare for the next iteration
vec3s wo = glms_vec3_negate(active_ray.direction); // We need to negate the direction of the incoming ray
vec3s wi = sample_material_bsdf(hit_material, closest_hit.normal, wo, sample_index, depth, &pdf_bsdf);
vec3s wi = sample_material_bsdf(hit_material, closest_hit.normal, wo, closest_hit.uv, sample_index, depth, &pdf_bsdf);
if (pdf_bsdf <= 0.0f)
{
break;
@@ -75,8 +76,10 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
shading_context_t shading_context = {
.normal = closest_hit.normal,
.position = closest_hit.point,
.wi = wi,
.wo = wo
.wo = wo,
.uv = closest_hit.uv,
};
vec3s bsdf = evaluate_material_bsdf(hit_material, &shading_context);
float cos_theta = fmaxf(0.0f, glms_vec3_dot(wi, closest_hit.normal));
@@ -97,8 +100,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
throughput = glms_vec3_scale(throughput, 1.0f / q);
}
active_ray.origin = BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal);
active_ray.direction = wi;
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal), wi);
prev_normal = closest_hit.normal;
depth++;

View File

@@ -1,21 +1,87 @@
#include "algorithm/RayIntersection.h"
#include "Algorithm/RayIntersection.h"
#include "Common.h"
#include "Geometry/Triangle.h"
#include "cglm/struct/vec3.h"
ray_t ray_create(vec3s origin, vec3s direction)
{
return (ray_t)
{
.origin = origin,
.direction = direction,
.inverse_direction = glms_vec3_div(glms_vec3_one(), direction),
.sign =
((direction.x < 0.0f) ? 1 : 0) |
((direction.y < 0.0f) ? 2 : 0) |
((direction.z < 0.0f) ? 4 : 0)
,
};
}
static inline float next_float_up(float value)
{
if (isnan(value) || (isfinite(value) && value > 0))
{
return value;
}
return nextafterf(value, INFINITY);
}
static inline float next_float_down(float value)
{
if (isnan(value) || (isfinite(value) && value < 0))
{
return value;
}
return nextafterf(value, -INFINITY);
}
vec3s offset_ray_origin(vec3s point, vec3s normal, vec3s wo)
{
vec3s abs_normal = glms_vec3_abs(normal);
float c = glms_vec3_max(point) * gamma(3);
float d = glms_vec3_dot(abs_normal, (vec3s){c, c, c});
vec3s offset = glms_vec3_scale(abs_normal, d);
if (glms_vec3_dot(wo, normal) < 0.0f)
{
offset = glms_vec3_negate(offset);
}
vec3s position = glms_vec3_add(point, offset);
for (int i = 0; i < 3; i++)
{
if (offset.raw[i] > 0.0f)
{
position.raw[i] = next_float_up(position.raw[i]);
}
else if (offset.raw[i] < 0.0f)
{
position.raw[i] = next_float_down(position.raw[i]);
}
}
return position;
//return point;
}
#if 0
// TODO: We still have small amount of block dots in current implementation. It's because of floating point precision. May need to fall back to double or handle it in a different way.
hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle)
{
hit_result_t result = {0};
vec3s edge1 = glms_vec3_sub(triangle.point_2, triangle.point_1);
vec3s edge2 = glms_vec3_sub(triangle.point_3, triangle.point_2);
vec3s edge3 = glms_vec3_sub(triangle.point_1, triangle.point_3);
vec3s edge1 = glms_vec3_sub(triangle->vertices[1].position, triangle->vertices[0].position);
vec3s edge2 = glms_vec3_sub(triangle->vertices[2].position, triangle->vertices[1].position);
vec3s edge3 = glms_vec3_sub(triangle->vertices[0].position, triangle->vertices[2].position);
vec3s normal_face = triangle.normal_face;
float n_dot_r = glms_vec3_dot(normal_face, ray.direction);
vec3s normal = triangle->face_normal;
float n_dot_r = glms_vec3_dot(normal, ray->direction);
if (n_dot_r > 0.0f)
{
normal_face = glms_vec3_negate(normal_face);
normal = glms_vec3_negate(normal);
}
// triangle is parallel to the ray
@@ -25,85 +91,178 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
}
// Get distance from ray origin to triangle plane
float distance = (glms_vec3_dot(normal_face, triangle.point_1) - glms_vec3_dot(normal_face, ray.origin)) / glms_vec3_dot(normal_face, ray.direction);
float distance = (glms_vec3_dot(normal, triangle->vertices[0].position) - glms_vec3_dot(normal, ray->origin)) / glms_vec3_dot(normal, ray->direction);
if (distance < RAY_EPSILON)
float eps = gamma(3) * glms_vec3_max(glms_vec3_abs(ray->origin));
if (distance <= eps)
{
return result;
}
vec3s intersection_point = glms_vec3_add(ray.origin, glms_vec3_scale(ray.direction, distance));
vec3s intersection_point = glms_vec3_add(ray->origin, glms_vec3_scale(ray->direction, distance));
// Check if the intersection point is inside the triangle using barycentric coordinates
vec3s vp = glms_vec3_sub(intersection_point, triangle.point_1);
vec3s vp2 = glms_vec3_sub(intersection_point, triangle.point_2);
vec3s vp3 = glms_vec3_sub(intersection_point, triangle.point_3);
vec3s vp = glms_vec3_sub(intersection_point, triangle->vertices[0].position);
vec3s vp2 = glms_vec3_sub(intersection_point, triangle->vertices[1].position);
vec3s vp3 = glms_vec3_sub(intersection_point, triangle->vertices[2].position);
vec3s c1 = glms_vec3_cross(edge1, vp);
vec3s c2 = glms_vec3_cross(edge2, vp2);
vec3s c3 = glms_vec3_cross(edge3, vp3);
if (glms_vec3_dot(triangle.normal_face, c1) < 0.0f
|| glms_vec3_dot(triangle.normal_face, c2) < 0.0f
|| glms_vec3_dot(triangle.normal_face, c3) < 0.0f)
float n_dot_c1 = glms_vec3_dot(normal, c1);
float n_dot_c2 = glms_vec3_dot(normal, c2);
float n_dot_c3 = glms_vec3_dot(normal, c3);
bool r1 = n_dot_c1 > 0.0f && n_dot_c2 > 0.0f && n_dot_c3 > 0.0f;
bool r2 = n_dot_c1 < 0.0f && n_dot_c2 < 0.0f && n_dot_c3 < 0.0f;
if (!r1 && !r2)
{
return result;
}
// TODO: Normal interpolation
vec3s v0v1 = glms_vec3_sub(triangle->vertices[1].position, triangle->vertices[0].position);
vec3s v0v2 = glms_vec3_sub(triangle->vertices[2].position, triangle->vertices[0].position);
vec3s v0p = glms_vec3_sub(intersection_point, triangle->vertices[0].position);
float d00 = glms_vec3_dot(v0v1, v0v1); // dot(v0v1, v0v1)
float d01 = glms_vec3_dot(v0v1, v0v2); // dot(v0v1, v0v2)
float d11 = glms_vec3_dot(v0v2, v0v2); // dot(v0v2, v0v2)
float d20 = glms_vec3_dot(v0p, v0v1); // dot(v0p, v0v1)
float d21 = glms_vec3_dot(v0p, v0v2); // dot(v0p, v0v2)
float denom = d00 * d11 - d01 * d01;
//if (denom < FLT_EPSILON)
//{
// return result;
//}
float invDenom = 1.0f / denom;
float u = (d11 * d20 - d01 * d21) * invDenom; // This is b1 (weight for V1)
float v = (d00 * d21 - d01 * d20) * invDenom; // This is b2 (weight for V2)
float w = 1.0f - u - v;
//vec3s smooth_normal = glms_vec3_add(glms_vec3_scale(triangle->vertices[0].normal, u), glms_vec3_add(glms_vec3_scale(triangle->vertices[1].normal, v), glms_vec3_scale(triangle->vertices[2].normal, w)));
result.hit = true;
result.point = intersection_point;
result.normal = normal_face;
result.normal = normal;
result.uv = (vec2s)
{
.x = w * triangle->vertices[0].uv.x + u * triangle->vertices[1].uv.x + v * triangle->vertices[2].uv.x,
.y = w * triangle->vertices[0].uv.y + u * triangle->vertices[1].uv.y + v * triangle->vertices[2].uv.y,
};
result.distance = distance;
return result;
}
bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_out)
#else
hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle)
{
vec3s inv_dir = glms_vec3_div(glms_vec3_one(), ray.direction);
float t_near = -FLT_MIN;
float t_far = FLT_MAX;
hit_result_t result = {0};
// X axis
vec3s origin = ray->origin;
vec3s direction = ray->direction;
vec3s v0 = triangle->vertices[0].position;
vec3s v1 = triangle->vertices[1].position;
vec3s v2 = triangle->vertices[2].position;
vec3s E1 = glms_vec3_sub(v1, v0);
vec3s E2 = glms_vec3_sub(v2, v0);
// Begin MöllerTrumbore
vec3s P = glms_vec3_cross(direction, E2);
float det = glms_vec3_dot(E1, P);
if (fabsf(det) < FLT_EPSILON)
{
float t1 = (aabb.min.x - ray.origin.x) * inv_dir.x;
float t2 = (aabb.max.x - ray.origin.x) * inv_dir.x;
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
}
// Y axis
{
float t1 = (aabb.min.y - ray.origin.y) * inv_dir.y;
float t2 = (aabb.max.y - ray.origin.y) * inv_dir.y;
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
}
// Z axis
{
float t1 = (aabb.min.z - ray.origin.z) * inv_dir.z;
float t2 = (aabb.max.z - ray.origin.z) * inv_dir.z;
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
return result;
}
if (t_near > t_far || t_far < 0.0f)
float invDet = 1.0f / det;
// Calculate barycentric u
vec3s T = glms_vec3_sub(origin, v0);
float u = glms_vec3_dot(T, P) * invDet;
if (u < 0.0f || u > 1.0f)
{
return result;
}
// Calculate barycentric v
vec3s Q = glms_vec3_cross(T, E1);
float v = glms_vec3_dot(direction, Q) * invDet;
if (v < 0.0f || u + v > 1.0f)
{
return result;
}
// Distance along the ray
float t = glms_vec3_dot(E2, Q) * invDet;
if (t < RAY_EPSILON)
{
return result;
}
result.hit = true;
result.distance = t;
result.point = glms_vec3_add(origin, glms_vec3_scale(direction, t));
// Face normal (unchanged by windings)
vec3s n = triangle->face_normal;
result.normal = glms_vec3_dot(n, direction) < 0.0f ? n : glms_vec3_negate(n);
// Interpolate UVs
float w = 1.0f - u - v;
result.uv.x = w * triangle->vertices[0].uv.x
+ u * triangle->vertices[1].uv.x
+ v * triangle->vertices[2].uv.x;
result.uv.y = w * triangle->vertices[0].uv.y
+ u * triangle->vertices[1].uv.y
+ v * triangle->vertices[2].uv.y;
return result;
}
#endif
bool ray_intersect_aabb(const ray_t* ray, aabb_t aabb, float* enter_out, float* exit_out)
{
// select slab min/max per axis based on sign:
float tx_min = ((SIGN_BIT(ray->sign, 0) ? aabb.max.x : aabb.min.x) - ray->origin.x ) * ray->inverse_direction.x;
float tx_max = ((SIGN_BIT(ray->sign, 0) ? aabb.min.x : aabb.max.x) - ray->origin.x ) * ray->inverse_direction.x;
float ty_min = ((SIGN_BIT(ray->sign, 1) ? aabb.max.y : aabb.min.y) - ray->origin.y ) * ray->inverse_direction.y;
float ty_max = ((SIGN_BIT(ray->sign, 1) ? aabb.min.y : aabb.max.y) - ray->origin.y ) * ray->inverse_direction.y;
// early exit if slabs miss
if ((tx_min > ty_max) || (ty_min > tx_max))
{
return false;
}
if (enter_out)
*enter_out = t_near;
if (exit_out)
*exit_out = t_far;
// merge X and Y
float t0 = tx_min > ty_min ? tx_min : ty_min;
float t1 = tx_max < ty_max ? tx_max : ty_max;
float tz_min = ( (SIGN_BIT(ray->sign, 2) ? aabb.max.z : aabb.min.z) - ray->origin.z ) * ray->inverse_direction.z;
float tz_max = ( (SIGN_BIT(ray->sign, 2) ? aabb.min.z : aabb.max.z) - ray->origin.z ) * ray->inverse_direction.z;
// final overlap test
if ((t0 > tz_max) || (tz_min > t1))
{
return false;
}
// update entry/exit
if (enter_out != NULL)
{
*enter_out = t0 > tz_min ? t0 : tz_min;
}
if (exit_out != NULL)
{
*exit_out = t1 < tz_max ? t1 : tz_max;
}
return true;
}
@@ -117,7 +276,7 @@ static inline float distance_to_aabb(vec3s point, aabb_t aabb)
}
// TODO: Use a stack to avoid recursion.
void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes, const uint64_t* primitive_indices, const triangle_collection_t* all_triangles, uint64_t node_index, float* closest_out,
void ray_intersect_bvh(const ray_t* ray, const bvh_node_t* bvh_nodes, const uint64_t* primitive_indices, const triangle_collection_t* all_triangles, uint64_t node_index, float* closest_out,
hit_result_t* best_hit_out)
{
const bvh_node_t* node = &bvh_nodes[node_index];
@@ -139,7 +298,7 @@ void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes, const uint6
for (uint32_t i = 0; i < node->primitive_count; i++)
{
uint64_t triangle_index = primitive_indices[node->start_index + i];
hit_result_t hit_result = ray_intersect_triangle(ray, all_triangles->buffer[triangle_index]);
hit_result_t hit_result = ray_intersect_triangle(ray, &all_triangles->buffer[triangle_index]);
if (hit_result.hit && hit_result.distance < *closest_out)
{
hit_result.triangle_id = triangle_index;
@@ -191,7 +350,7 @@ void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes, const uint6
}
}
hit_result_t ray_intersect_scene(ray_t ray, const scene_t* scene)
hit_result_t ray_intersect_scene(const ray_t* ray, const scene_t* scene)
{
hit_result_t result = {0};
float closest = FLT_MAX;

View File

@@ -1,23 +1,72 @@
#include "Geometry/Mesh.h"
#include "Geometry/Triangle.h"
#include "Common/String.h"
#include "Material/SimpleLit.h"
#include "assimp/cimport.h"
#include "assimp/scene.h"
#include "assimp/postprocess.h"
mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_collection_t* triangles, material_collection_t* materials)
mesh_entity_t mesh_load(const char* filename, scene_t* scene)
{
mesh_entity_t entity = {0};
const C_STRUCT aiScene* scene = aiImportFile(filename, aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_GenSmoothNormals);
if (scene == NULL)
const struct aiScene* mesh_scene = aiImportFile(filename, aiProcessPreset_TargetRealtime_Quality);
if (mesh_scene == NULL)
{
perror(aiGetErrorString());
return entity;
}
for (uint32_t i = 0; i < scene->mNumMeshes; i++)
entity.triangle_id = scene->triangles.count;
entity.material_id = scene->materials.count;
for (uint32_t i = 0; i < mesh_scene->mNumMaterials; i++)
{
struct aiMesh* mesh = scene->mMeshes[i];
const struct aiMaterial* src = mesh_scene->mMaterials[i];
struct aiColor4D base_color;
aiGetMaterialColor(src, AI_MATKEY_COLOR_DIFFUSE, &base_color);
float smoothness = 0.5f;
aiGetMaterialFloat(src, AI_MATKEY_SHININESS, &smoothness);
texture_t* albedo_texture = NULL;
if (aiGetMaterialTextureCount(src, aiTextureType_DIFFUSE) > 0)
{
struct aiString path;
if (AI_SUCCESS == aiGetMaterialTexture(src, aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL))
{
if (!is_absolute_path(path.data))
{
char directory[1024];
get_path_directory(filename, directory, 1024);
char image_path[1024];
memcpy(image_path, path.data, 1024);
string_join(directory, image_path, path.data, 1024);
}
texture_entity_t entity = texture_load(path.data, true, &scene->textures);
if (entity.id != INVALID_TEXTURE_ID)
{
albedo_texture = &scene->textures.buffer[entity.id];
}
}
}
simple_lit_properties_t prop =
{
.albedo = {base_color.r, base_color.g, base_color.b},
.roughness = 1.0f - smoothness,
.metallic = 0.0f,
.albedo_texture = albedo_texture,
};
material_create_simple_lit_default(&prop, &scene->materials);
}
for (uint32_t i = 0; i < mesh_scene->mNumMeshes; i++)
{
const struct aiMesh* mesh = mesh_scene->mMeshes[i];
//TODO: Handle all primitive types, not just triangles
if (mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
@@ -25,9 +74,11 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
continue;
}
bool has_uv = mesh->mTextureCoords[0] != NULL && mesh->mNumUVComponents[0] >= 2;
for (uint32_t j = 0; j < mesh->mNumFaces; j++)
{
struct aiFace* face = &mesh->mFaces[j];
const struct aiFace* face = &mesh->mFaces[j];
if (face->mNumIndices != 3)
{
continue;
@@ -53,6 +104,20 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
mesh->mVertices[index3].z
};
vec2s uv1 = {0.0f, 0.0f};
vec2s uv2 = {0.0f, 0.0f};
vec2s uv3 = {0.0f, 0.0f};
if (has_uv)
{
struct aiVector3D const* tc = mesh->mTextureCoords[0];
uv1.x = tc[index1].x;
uv1.y = tc[index1].y;
uv2.x = tc[index2].x;
uv2.y = tc[index2].y;
uv3.x = tc[index3].x;
uv3.y = tc[index3].y;
}
vec3s normal1 = {
mesh->mNormals[index1].x,
mesh->mNormals[index1].y,
@@ -69,18 +134,15 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
mesh->mNormals[index3].z
};
//TODO: Handle materials, we use OpenPBR standard for parameter naming
vertex_t vertex1 = {.position = point1, .normal = normal1, .uv = uv1};
vertex_t vertex2 = {.position = point2, .normal = normal2, .uv = uv2};
vertex_t vertex3 = {.position = point3, .normal = normal3, .uv = uv3};
// uint32_t properties_count = scene->mMaterials[mesh->mMaterialIndex]->mNumProperties;
// C_STRUCT aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// for (uint32_t p = 0; p < properties_count; p++)
// {
// material->mProperties[p]->mKey;
// }
triangle_create_with_normals(point1, point2, point3, normal1, normal2, normal3, material_id, triangles);
triangle_create(vertex1, vertex2, vertex3, entity.material_id + mesh->mMaterialIndex, &scene->triangles);
entity.triangle_count++;
}
}
aiReleaseImport(mesh_scene);
return entity;
}

View File

@@ -1,6 +1,6 @@
#include "Geometry/Triangle.h"
bool triangle_collection_init(triangle_collection_t* triangles, size_t size)
bool triangle_collection_init(size_t size, triangle_collection_t* triangles)
{
if (size > UINT64_MAX)
{
@@ -45,37 +45,23 @@ void triangle_collection_free(triangle_collection_t* collection)
}
}
void triangle_create_with_normals(vec3s point1, vec3s point2, vec3s point3,
vec3s normal1, vec3s normal2, vec3s normal3,
uint8_t material_id, triangle_collection_t* collection)
void triangle_create(vertex_t v1, vertex_t v2, vertex_t v3, uint8_t material_id, triangle_collection_t* collection)
{
vec3s edge1 = glms_vec3_sub(v2.position, v1.position);
vec3s edge2 = glms_vec3_sub(v3.position, v1.position);
vec3s normal = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
triangle_t triangle = {
.vertices = {v1, v2, v3},
.face_normal = normal,
.material_id = material_id,
};
if (collection->count >= collection->size)
{
triangle_collection_resize(collection, collection->size * 2);
}
triangle_t triangle;
triangle.point_1 = point1;
triangle.point_2 = point2;
triangle.point_3 = point3;
triangle.material_id = material_id;
triangle.normal_1 = normal1;
triangle.normal_2 = normal2;
triangle.normal_3 = normal3;
vec3s edge1 = glms_vec3_sub(point2, point1);
vec3s edge2 = glms_vec3_sub(point3, point1);
triangle.normal_face = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
collection->buffer[collection->count] = triangle;
collection->count++;
}
void triangle_create(vec3s point1, vec3s point2, vec3s point3, uint8_t material_id, triangle_collection_t* collection)
{
vec3s edge1 = glms_vec3_sub(point2, point1);
vec3s edge2 = glms_vec3_sub(point3, point1);
vec3s normal = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
triangle_create_with_normals(point1, point2, point3, normal, normal, normal, material_id, collection);
}

View File

@@ -17,14 +17,11 @@ vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_c
return glms_vec3_zero();
}
ray_t shadow_ray = {
.origin = BIAS_RAY_ORIGION(context->hit_point, context->normal),
.direction = wi,
};
ray_t shadow_ray = ray_create(BIAS_RAY_ORIGION(context->hit_point, context->normal), wi);
float closest = FLT_MAX;
hit_result_t shadow_hit = {1};
ray_intersect_bvh(shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0,
ray_intersect_bvh(&shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0,
&closest, &shadow_hit);
if (shadow_hit.hit)
{
@@ -34,11 +31,12 @@ vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_c
shading_context_t shading_context = {
.normal = context->normal,
.wi = wi,
.wo = glms_vec3_negate(context->wo)
.wo = glms_vec3_negate(context->wo),
.uv = context->uv,
};
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
vec3s light_radiance = glms_vec3_scale(light.color, light.intensity);
vec3s light_contribute = glms_vec3_scale(glms_vec3_mul(throughput, bsdf), fmaxf(0.0f, n_dot_l));
vec3s light_contribute = glms_vec3_scale(glms_vec3_mul(throughput, bsdf), fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for directional light
return glms_vec3_mul(light_radiance, light_contribute);
}

View File

@@ -11,7 +11,6 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
if (context == NULL)
{
return glms_vec3_mul(sky_color, throughput);
// return sky_data.color;
}
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
@@ -20,14 +19,11 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
vec3s wi = random_uniform_cdf_direction(context->normal, sample_index, d1, d2);
float pdf = 1.0f / (4.0f * (float)M_PI);
ray_t shadow_ray = {
.origin = BIAS_RAY_ORIGION(context->hit_point, context->normal),
.direction = wi,
};
ray_t shadow_ray = ray_create(BIAS_RAY_ORIGION(context->hit_point, context->normal), wi);
float closest = FLT_MAX;
hit_result_t shadow_hit = {1};
ray_intersect_bvh(shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0, &closest, &shadow_hit);
ray_intersect_bvh(&shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0, &closest, &shadow_hit);
if (shadow_hit.hit)
{
return glms_vec3_zero();
@@ -36,14 +32,15 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
shading_context_t shading_context = {
.normal = context->normal,
.wi = wi,
.wo = glms_vec3_negate(context->wo)
.wo = glms_vec3_negate(context->wo),
.uv = context->uv,
};
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
bsdf = glms_vec3_mul(bsdf, sky_color);
float cos_theta = fmaxf(glms_vec3_dot(wi, context->normal), 0.0f);
float pdf_bsdf = sample_material_bsdf_pdf(context->material, shading_context.normal, shading_context.wo, shading_context.wi);
float pdf_bsdf = sample_material_bsdf_pdf(context->material, shading_context.normal, shading_context.wo, shading_context.wi, context->uv);
float weight = power_heuristic(pdf, pdf_bsdf);
vec3s env_contrib = glms_vec3_scale(glms_vec3_mul(throughput, bsdf), cos_theta / pdf);

View File

@@ -1,7 +1,9 @@
#include "Material/Material.h"
#include <string.h>
bool material_collection_init(size_t size, material_collection_t* materials)
bool material_collection_init(uint8_t size, material_collection_t* materials)
{
if (size > 254)
{
@@ -41,21 +43,12 @@ void material_collection_free(material_collection_t* materials)
{
if (materials->buffer != NULL)
{
for (uint8_t i = 0; i < materials->count; i++)
{
void* data = materials->buffer[i].data;
if (data != NULL)
{
free(data);
}
}
free(materials->buffer);
materials->buffer = NULL;
}
}
material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, void* data, size_t size_of_data, material_collection_t* collection)
material_entity_t material_create(const void* properties, size_t properties_size, compute_surface_data_f surface_data, sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, material_collection_t* collection)
{
material_t material = {0};
@@ -64,16 +57,11 @@ material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample
material_collection_resize(collection, collection->size * 2);
}
memcpy(material.properties, properties, properties_size);
material.compute_surface_data = surface_data;
material.sample_bsdf = sample;
material.sample_bsdf_pdf = sample_pdf;
material.evaluate_bsdf = evaluate;
material.data = malloc(size_of_data);
if (material.data == NULL)
{
return (material_entity_t){.id = 255};
}
memcpy(material.data, data, size_of_data);
material_entity_t entity = {.id = collection->count};
@@ -83,23 +71,36 @@ material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample
return entity;
}
vec3s sample_material_bsdf(const material_t* material, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out)
vec3s sample_material_bsdf(const material_t* material, vec3s normal, vec3s wo, vec2s uv, uint32_t sample_index, uint32_t bounce, float* pdf_out)
{
vec3s wi = glms_vec3_zero();
if (material->sample_bsdf != NULL)
if (material->compute_surface_data != NULL && material->sample_bsdf != NULL)
{
wi = material->sample_bsdf(material->data, normal, wo, sample_index, bounce, pdf_out);
shading_context_t context =
{
.normal = normal,
.wo = wo,
.uv = uv,
};
wi = material->sample_bsdf(&context, material->properties, material->compute_surface_data, sample_index, bounce, pdf_out);
}
return wi;
}
float sample_material_bsdf_pdf(const material_t* material, vec3s normal, vec3s wo, vec3s wi)
float sample_material_bsdf_pdf(const material_t* material, vec3s normal, vec3s wo, vec3s wi, vec2s uv)
{
float pdf = 0.0f;
if (material->sample_bsdf_pdf != NULL)
if (material->compute_surface_data != NULL && material->sample_bsdf_pdf != NULL)
{
pdf = material->sample_bsdf_pdf(material->data, normal, wo, wi);
shading_context_t context =
{
.normal = normal,
.wo = wo,
.wi = wi,
.uv = uv,
};
pdf = material->sample_bsdf_pdf(&context, material->properties, material->compute_surface_data);
}
return pdf;
@@ -108,9 +109,9 @@ float sample_material_bsdf_pdf(const material_t* material, vec3s normal, vec3s w
vec3s evaluate_material_bsdf(const material_t* material, const shading_context_t* context)
{
vec3s bsdf_color = glms_vec3_zero();
if (material->evaluate_bsdf != NULL)
if (material->compute_surface_data != NULL && material->evaluate_bsdf != NULL)
{
bsdf_color = material->evaluate_bsdf(context, material->data);
bsdf_color = material->evaluate_bsdf(context, material->properties, material->compute_surface_data);
}
return bsdf_color;

View File

@@ -6,14 +6,39 @@
static float DIELECTRIC_REFLECTIVE_F0 = 0.04f; // Standard dielectric reflectivity coef at incident angle (= 4%)
static vec3s DIELECTRIC_REFLECTIVE = {0.04f, 0.04f, 0.04f}; // Standard dielectric reflectivity coef at incident angle (= 4%)
// Simple lit, but keep it unbiased as much as possible
vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out)
void simple_lit_data_default(const shading_context_t* context, const void* properties, void* data_out)
{
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
const simple_lit_properties_t* prop = (simple_lit_properties_t*)properties;
simple_lit_data_t* data = (simple_lit_data_t*)data_out;
data->albedo = prop->albedo;
if (prop->albedo_texture != NULL && prop->albedo_texture->data != NULL)
{
data->albedo = glms_vec3_mul(data->albedo, glms_vec3(texture_sample(prop->albedo_texture, context->uv.x, context->uv.y)));
}
data->roughness = prop->roughness;
if (prop->roughness_texture != NULL && prop->roughness_texture->data != NULL)
{
data->roughness = data->roughness * texture_sample(prop->roughness_texture, context->uv.x, context->uv.y).x;
}
data->metallic = prop->metallic;
if (prop->metallic_texture != NULL && prop->metallic_texture->data != NULL)
{
data->metallic = data->metallic * texture_sample(prop->metallic_texture, context->uv.x, context->uv.y).x;
}
}
// Simple lit, but keep it unbiased as much as possible
vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data, uint32_t sample_index, uint32_t bounce, float* pdf_out)
{
simple_lit_data_t shading_data;
compute_surface_data(context, properties, &shading_data);
//TODO: having a bsdf data struct to avoid recomputing the same thing in both sample and evaluate
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
float cos_theta_0 = fmaxf(glms_vec3_dot(normal, wo), 0.0f);
float cos_theta_0 = fmaxf(glms_vec3_dot(context->normal, context->wo), 0.0f);
vec3s f = fresnel_schlick_vec3(f0, cos_theta_0);
float lum_f = (f.x + f.y + f.z) / 3.0f;
@@ -38,7 +63,7 @@ vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t
if (lob_sample < prob_diffuse) // Diffuse Lobe
{
wi = random_cosine_direction(normal, sample_index, d1, d2);
wi = random_cosine_direction(context->normal, sample_index, d1, d2);
}
else // Specular Lobe
{
@@ -61,11 +86,11 @@ vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t
vec3s tangent_u; // World-space tangent (U)
vec3s bitangent_v; // World-space bitangent (V)
create_orthonormal_basis(normal, &tangent_u, &bitangent_v);
create_orthonormal_basis(context->normal, &tangent_u, &bitangent_v);
vec3s scaled_u = glms_vec3_scale(tangent_u, h_ts.x);
vec3s scaled_v = glms_vec3_scale(bitangent_v, h_ts.y);
vec3s scaled_n = glms_vec3_scale(normal, h_ts.z);
vec3s scaled_n = glms_vec3_scale(context->normal, h_ts.z);
// Transform h from tangent space to world space
vec3s h_ws;
@@ -74,37 +99,37 @@ vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t
h_ws = glms_vec3_normalize(h_ws);
// wi is simple now, just reflect wo around normal
wi = glms_vec3_reflect(glms_vec3_negate(wo), h_ws);
wi = glms_vec3_reflect(glms_vec3_negate(context->wo), h_ws);
}
// Final check to ensure wi is in the correct hemisphere
if (glms_vec3_dot(wi, normal) < 0.0f)
if (glms_vec3_dot(wi, context->normal) < 0.0f)
{
*pdf_out = 0.0f;
return glms_vec3_zero();
}
float pdf_diffuse = pdf_cosine_weighted_hemisphere(normal, wi);
float pdf_specular = pdf_blinn_phong_lobe(normal, wi, wo, shading_data.roughness);
float pdf_diffuse = pdf_cosine_weighted_hemisphere(context->normal, wi);
float pdf_specular = pdf_blinn_phong_lobe(context->normal, wi, context->wo, shading_data.roughness);
*pdf_out = prob_diffuse * pdf_diffuse + prob_specular * pdf_specular;
return wi;
}
//TODO: Most of the calculation here is same as in sample_bsdf_simple_lit, we can optimize this by using a bsdf data struct to avoid recomputing the same thing in both sample and evaluate
float sample_bsdf_pdf_simple_lit(const void* data, vec3s normal, vec3s wo, vec3s wi)
float sample_bsdf_pdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data)
{
// If wi is below the horizon relative to the normal, PDF must be 0
if (glms_vec3_dot(normal, wi) <= 0.0f) // Use <= to be safe
if (glms_vec3_dot(context->normal, context->wi) <= 0.0f) // Use <= to be safe
{
return 0.0f;
}
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
simple_lit_data_t shading_data;
compute_surface_data(context, properties, &shading_data);
// Again, we need bsdf data;
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
float cos_theta_o = fmaxf(glms_vec3_dot(normal, wo), 0.0f); // Use 'o' for outgoing (wo)
float cos_theta_o = fmaxf(glms_vec3_dot(context->normal, context->wo), 0.0f); // Use 'o' for outgoing (wo)
float F = glms_vec3_max(fresnel_schlick_vec3(f0, cos_theta_o));
float prob_specular = glm_lerp(F, 1.0f, shading_data.metallic);
@@ -118,25 +143,25 @@ float sample_bsdf_pdf_simple_lit(const void* data, vec3s normal, vec3s wo, vec3s
prob_diffuse /= total_prob;
prob_specular /= total_prob;
float pdf_diff = pdf_cosine_weighted_hemisphere(normal, wi);
float pdf_diff = pdf_cosine_weighted_hemisphere(context->normal, context->wi);
float diffuse_pdf_component = prob_diffuse * pdf_diff;
float pdf_spec = pdf_blinn_phong_lobe(normal, wo, wi, shading_data.roughness);
float pdf_spec = pdf_blinn_phong_lobe(context->normal, context->wo, context->wi, shading_data.roughness);
float specular_pdf_component = prob_specular * pdf_spec;
return diffuse_pdf_component + specular_pdf_component;
}
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* data)
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data)
{
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
shading_context_t shading_context = *context;
simple_lit_data_t shading_data;
compute_surface_data(context, properties, &shading_data);
vec3s h = glms_vec3_normalize(glms_vec3_add(shading_context.wi, shading_context.wo));
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(shading_context.normal, shading_context.wi));
float n_dot_v = fmaxf(FLT_EPSILON, glms_vec3_dot(shading_context.normal, shading_context.wo));
float n_dot_h = glms_vec3_dot(shading_context.normal, h);
float v_dot_h = glms_vec3_dot(shading_context.wo, h);
vec3s h = glms_vec3_normalize(glms_vec3_add(context->wi, context->wo));
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, context->wi));
float n_dot_v = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, context->wo));
float n_dot_h = glms_vec3_dot(context->normal, h);
float v_dot_h = glms_vec3_dot(context->wo, h);
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);

View File

@@ -12,7 +12,7 @@ static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t nod
const bvh_node_t* node = &bvh_tree.nodes[node_index];
float enter, exit;
if (!ray_intersect_aabb(ray, node->bounds, &enter, &exit))
if (!ray_intersect_aabb(&ray, node->bounds, &enter, &exit))
{
return;
}
@@ -33,11 +33,9 @@ static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t nod
vec4s render_debug(scene_t* scene, ray_t ray, uint16_t sample_index, int flag)
{
vec4s result = glms_vec4_zero();
if (scene == NULL)
{
return result;
return glms_vec4_zero();
}
switch (flag & 0xFF)
@@ -46,20 +44,23 @@ vec4s render_debug(scene_t* scene, ray_t ray, uint16_t sample_index, int flag)
uint32_t count = 0;
ray_intersect_bvh_count(ray, scene->bvh_tree, 0, &count);
vec4s result = glms_vec4_zero();
for (uint32_t i = 0; i < count; i++)
{
result = glms_vec4_add(result, DEBUG_COLOR_BVH);
}
return result;
break;
case DEBUG_SOBOL:
uint16_t i = sample_index ^ (sample_index >> 1);
float sobol_sample_value = sobol_sample(i, 1); // Assuming dimension 0 for simplicity
result = glms_vec4_add(result, (vec4s){sobol_sample_value, sobol_sample_value, sobol_sample_value, 1.0f});
break;
default:
break;
}
return (vec4s){sobol_sample_value, sobol_sample_value, sobol_sample_value, 1.0f};
return result;
case DEBUG_UV:
hit_result_t hit_result = ray_intersect_scene(&ray, scene);
return (vec4s){fmodf(fabsf(hit_result.uv.x), 1.0f), fmodf(fabsf(hit_result.uv.y), 1.0f), 0.0f, 1.0f};
default:
return glms_vec4_zero();
}
}

View File

@@ -76,9 +76,8 @@ unsigned char* render_target_to_char(render_target_t* render_target)
void render_target_free(render_target_t* target)
{
if (target->buffer != NULL)
if (target != NULL && target->buffer != NULL)
{
free(target->buffer);
target->buffer = NULL;
}
}

View File

@@ -31,6 +31,7 @@ static inline uint16_t get_sample_count(uint16_t sample_count, int flag)
{
case DEBUG_BVH:
case DEBUG_SOBOL:
case DEBUG_UV:
return 1;
default:
return sample_count;
@@ -45,6 +46,8 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
uint32_t pixel_id = y * config->width + x;
uint16_t sample_count = get_sample_count(config->sample_count, flag);
float inv_sample = 1.0f / (float)sample_count;
vec2s position_ndc = compute_ndc((float)x, (float)y, config->width, config->height);
vec3s camera_right = quat_get_right(scene->camera.rotation);
vec3s camera_up = quat_get_up(scene->camera.rotation);
@@ -60,10 +63,7 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_right, sensor_offset_x));
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_up, sensor_offset_y));
ray_t ray = {
.origin = scene->camera.position,
.direction = glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position))
};
ray_t ray = ray_create(scene->camera.position, glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position)));
vec4s out_color = glms_vec4_zero();
@@ -81,7 +81,7 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
accumulated_color = glms_vec4_add(accumulated_color, out_color);
}
*pixel_color = glms_vec4_scale(accumulated_color, 1.0f / (float)sample_count);
*pixel_color = glms_vec4_scale(accumulated_color, inv_sample);
}
// TODO: Progressive rendering

View File

@@ -1,14 +1,19 @@
#include "Rendering/Scene.h"
bool scene_init(scene_t* scene, uint64_t triangle_count, uint8_t material_count, uint32_t punctual_light_count)
bool scene_init(scene_t* scene, uint64_t triangle_count, uint16_t texture_count, uint8_t material_count, uint32_t punctual_light_count)
{
scene_t temp = {0};
if (!triangle_collection_init(&temp.triangles, triangle_count))
if (!triangle_collection_init(triangle_count, &temp.triangles))
{
goto triangle_failed;
}
if (!texture_collection_init(texture_count, &temp.textures))
{
goto texture_failed;
}
if (!material_collection_init(material_count, &temp.materials))
{
goto material_failed;
@@ -33,6 +38,8 @@ bool scene_init(scene_t* scene, uint64_t triangle_count, uint8_t material_count,
light_failed:
material_collection_free(&temp.materials);
material_failed:
texture_collection_free(&temp.textures);
texture_failed:
triangle_collection_free(&temp.triangles);
triangle_failed:
return false;
@@ -58,8 +65,14 @@ bool scene_build_bvh(scene_t* scene)
void scene_free(scene_t* scene)
{
if (scene == NULL)
{
return;
}
bvh_tree_free(&scene->bvh_tree);
triangle_collection_free(&scene->triangles);
texture_collection_free(&scene->textures);
material_collection_free(&scene->materials);
light_collection_free(&scene->lights);
}

198
source/Rendering/Texture.c Normal file
View File

@@ -0,0 +1,198 @@
#include "Rendering/Texture.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define GET_CHANNEL_DATA(pixel, channel, channel_count, default) (channel < channel_count ? pixel[channel] : default) / 255.0f
static inline texture_entity_t invalid_texture_entity()
{
return (texture_entity_t){.id = INVALID_TEXTURE_ID};
}
bool texture_collection_init(uint16_t size, texture_collection_t* textures)
{
texture_collection_t temp = {0};
temp.buffer = (texture_t*)malloc(size * sizeof(texture_t));
if (temp.buffer == NULL)
{
return false;
}
temp.size = size;
temp.count = 0;
*textures = temp;
return true;
}
void texture_collection_resize(texture_collection_t* textures, uint16_t size)
{
texture_t* temp = (texture_t*)realloc(textures->buffer, size * sizeof(texture_t));
if (temp != NULL)
{
textures->buffer = temp;
textures->size = size;
}
}
void texture_collection_free(texture_collection_t* textures)
{
if (textures == NULL)
{
return;
}
for (uint16_t i = 0; i < textures->count; i++)
{
texture_free(&textures->buffer[i]);
}
free(textures->buffer);
textures->buffer = NULL;
}
texture_entity_t texture_load(const char* filename, bool srgb, texture_collection_t* textures)
{
int width, height, channels;
uint8_t* data = stbi_load(filename, &width, &height, &channels, 0);
if (data == NULL)
{
return invalid_texture_entity();
}
if (srgb)
{
// Convert to linear space if the texture is in sRGB format
for (int i = 0; i < width * height * channels; i++)
{
data[i] = (uint8_t)(powf(data[i] / 255.0f, 2.2f) * 255.0f);
}
}
texture_t texture = {0};
texture.width = (uint32_t)width;
texture.height = (uint32_t)height;
texture.channel_count = (uint8_t)channels;
texture.data = data;
texture.wrap_mode = REPEAT;
texture.filter_mode = LINEAR;
if (textures->count >= textures->size)
{
texture_collection_resize(textures, textures->size * 2);
}
texture_entity_t entity = {.id = textures->count};
textures->buffer[textures->count] = texture;
textures->count++;
return entity;
}
static inline void warp_uv(wrap_mode_t mode, float* u, float* v)
{
switch (mode)
{
case REPEAT:
*u = fmodf(fabsf(*u), 1.0f);
*v = fmodf(fabsf(*v), 1.0f);
break;
case CLAMP:
*u = fminf(fmaxf(*u, 0.0f), 1.0f);
*v = fminf(fmaxf(*v, 0.0f), 1.0f);
break;
}
}
static vec4s get_pixel_color(const texture_t* texture, uint32_t x, uint32_t y)
{
uint32_t pixel_index = y * texture->width + x;
if (pixel_index >= texture->width * texture->height)
{
return (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
}
uint8_t* pixel = &texture->data[pixel_index * texture->channel_count];
return (vec4s)
{
GET_CHANNEL_DATA(pixel, 0, texture->channel_count, 0),
GET_CHANNEL_DATA(pixel, 1, texture->channel_count, 0),
GET_CHANNEL_DATA(pixel, 2, texture->channel_count, 0),
GET_CHANNEL_DATA(pixel, 3, texture->channel_count, 1)
};
}
static vec4s nearest_filter(const texture_t* texture, float u, float v)
{
uint32_t x = (uint32_t)floorf(u * (texture->width - 1));
uint32_t y = (uint32_t)floorf(v * (texture->height - 1));
x = x < texture->width ? x : texture->width - 1;
y = y < texture->height ? y : texture->height - 1;
return get_pixel_color(texture, x, y);
}
static vec4s linear_filter(const texture_t* texture, float u, float v)
{
float x = u * (texture->width - 1);
float y = v * (texture->height - 1);
uint32_t x0 = (uint32_t)floorf(x);
uint32_t x1 = x0 + 1;
uint32_t y0 = (uint32_t)floorf(y);
uint32_t y1 = y0 + 1;
float sx = x - (float)x0;
float sy = y - (float)y0;
// Clamp to edges
x0 = x0 < texture->width ? x0 : texture->width - 1;
x1 = x1 < texture->width ? x1 : texture->width - 1;
y0 = y0 < texture->height ? y0 : texture->height - 1;
y1 = y1 < texture->height ? y1 : texture->height - 1;
// Sample 4 texels
vec4s c00 = get_pixel_color(texture, x0, y0);
vec4s c10 = get_pixel_color(texture, x1, y0);
vec4s c01 = get_pixel_color(texture, x0, y1);
vec4s c11 = get_pixel_color(texture, x1, y1);
// Interpolate along x
vec4s c0 = glms_vec4_lerp(c00, c10, sx);
vec4s c1 = glms_vec4_lerp(c01, c11, sx);
// Interpolate along y
vec4s result = glms_vec4_lerp(c0, c1, sy);
return result;
}
static inline vec4s filter_texture(const texture_t* texture, float u, float v)
{
switch (texture->filter_mode)
{
case NEAREST:
return nearest_filter(texture, u, v);
case LINEAR:
return linear_filter(texture, u, v);
default:
return (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
}
}
vec4s texture_sample(const texture_t* texture, float u, float v)
{
warp_uv(texture->wrap_mode, &u, &v);
return filter_texture(texture, u, v);
}
void texture_free(texture_t* texture)
{
if (texture != NULL && texture->data != NULL)
{
stbi_image_free(texture->data);
}
}

View File

@@ -3,19 +3,20 @@
#include <svpng.inc>
#include "Algorithm/Sobol.h"
// #include "Geometry/GeometryUtilities.h"
#include "Geometry/Mesh.h"
#include "Lighting/SkyLight.h"
#include "Material/SimpleLit.h"
// #include "Material/SimpleLit.h"
#include "Rendering/PostProcessing.h"
#include "Rendering/Scene.h"
#include "Window.h"
#define TITLE "Path Tracing"
#define SPONZA_PATH "./assets/sponza.obj"
#define SPONZA_PATH "./assets/sponza.fbx"
static bool scene_setup(scene_t* scene)
{
if (!scene_init(scene, 67000, 8, 1))
if (!scene_init(scene, 167000, 3, 8, 1))
{
return false;
}
@@ -26,7 +27,7 @@ static bool scene_setup(scene_t* scene)
// TODO: Standardize light unit
light_entity_t sun = light_create_directional_light(&scene->lights);
directional_light_t* sun_light = &scene->lights.directional_lights[sun.id];
sun_light->direction = glms_vec3_normalize((vec3s){-0.5f, 1.0f, 0.15f});
sun_light->direction = glms_vec3_normalize((vec3s){-0.5f, 1.0f, 0.25f});
sun_light->color = (vec3s){1.0f, 0.93f, 0.87f};
sun_light->intensity = 2.0f;
sun_light->angular_diameter = 0.53f;
@@ -42,16 +43,8 @@ static bool scene_setup(scene_t* scene)
static bool load_assets(scene_t* scene)
{
simple_lit_data_t floor_lit_data =
{
.albedo = (vec3s){1.0f, 1.0f, 1.0f},
.roughness = 0.95f,
.metallic = 0.0f,
};
material_entity_t floor_material = material_create_simple_lit(&floor_lit_data, &scene->materials);
mesh_load(SPONZA_PATH, floor_material.id, &scene->triangles, &scene->materials);
mesh_load(SPONZA_PATH, scene);
// quad_create((vec3s){0.0f, 0.0f, 0.0f}, (vec3s){0.0f, 1.0f, 0.0f}, (vec3s){1.0f, 0.0f, 0.0f}, 10.0f, floor_material.id, &scene->triangles);
return scene_build_bvh(scene);
}
@@ -157,14 +150,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
{
omp_set_num_threads(16);
scene_t scene;
render_target_t img;
scene_t scene = {0};
render_target_t img = {0};
render_job_t* job = NULL;
rendering_config_t config = {
.width = 1920 / 2,
.height = 1080 / 2,
.sample_count = 64 * 4,
.sample_count = 64 * 1,
.max_depth = 4,
.bucket_size = 64,
};