Initial upload

This commit is contained in:
2025-04-15 11:29:46 +09:00
commit b915d56f73
212 changed files with 43262 additions and 0 deletions

147
source/Algorithm/BSDF.c Normal file
View File

@@ -0,0 +1,147 @@
#include "Algorithm/BSDF.h"
#include "Common.h"
#include "cglm/struct/vec3.h"
#include <float.h>
#include <math.h>
static const float DIELECTRIC_REFLECTIVE_F0 = 0.04f; // Standard dielectric reflectivity coef at incident angle (= 4%)
static const vec3s DIELECTRIC_REFLECTIVE = {0.04f, 0.04f, 0.04f}; // Standard dielectric reflectivity coef at incident angle (= 4%)
static inline float roughness_to_blinn_phong_specular_exponent(float roughness)
{
return glm_clamp(2 * 1.0f / (max(roughness * roughness, FLT_EPSILON)) - 2, FLT_EPSILON, 1.0f / FLT_EPSILON);
}
static inline vec3s fresnel_schlick_vec3(vec3s f0, float cos_theta)
{
float factor = powf(1.0f - cos_theta, 5.0f);
vec3s one = {{1.0f, 1.0f, 1.0f}};
vec3s reflected = glms_vec3_scale(glms_vec3_sub(one, f0), factor);
return glms_vec3_add(f0, reflected);
}
vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s wo, float* pdf_out)
{
const simple_lit_data_t shading_data = *(const simple_lit_data_t*)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 F = glms_vec3_max(fresnel_schlick_vec3(f0, cos_theta_0)); // We use the max component of the Fresnel term for simplicity
float prob_specular = glm_lerp(F, 1.0f, shading_data.metallic);
float prob_diffuse = (1.0f - shading_data.metallic) * (1.0f - F); // Diffuse only for non-metals, reduced by reflection
float total_prob = prob_diffuse + prob_specular;
if (total_prob < FLT_EPSILON)
{
*pdf_out = 0.0f;
return glms_vec3_zero();
}
// Normalize probabilities
prob_diffuse /= total_prob;
prob_specular /= total_prob;
vec3s wi = glms_vec3_zero();
float pdf_lobe = 0.0f;
if (random_float() < prob_diffuse) // Diffuse Lobe
{
wi = random_cosine_direction(normal);
pdf_lobe = fmaxf(glms_vec3_dot(wi, normal), 0.0f) / (float)M_PI; // PDF for cosine sampling
if (pdf_lobe < FLT_EPSILON)
{
*pdf_out = 0.0f;
return glms_vec3_zero();
}
// Calculate combined PDF (using probabilities of both methods generating THIS wi) - Simplified: use chosen lobe's PDF
// float pdf_spec = pdf_ggx_specular_lobe(normal, wo, roughness, wi);
// *pdf_out = prob_diffuse * pdf_lobe + prob_specular * pdf_spec; // Power Heuristic / MIS would be better
*pdf_out = pdf_lobe;
}
else // Specular Lobe
{
// For simplification we use blinn-phong lobe distribution, we will implement GGX for standard lit later
// When talking about simplification, wen even can use a simple interpolation bwtween roughness and wi, but it's too biased.
// A common simplification involves sampling spherical coordinates(theta and phi angles) related to normal such that cose(theta) is distributed according to the Blinn-Phong distribution
// We can use a inversion sampling where cos(theta) = powf(random_float(), 1.0f / (specular_exponent + 1.0f)) and phi = 2 * PI * random_float()
float specular_exponent = roughness_to_blinn_phong_specular_exponent(shading_data.roughness);
float theta = acosf(powf(random_float(), 1.0f / (specular_exponent + 1.0f)));
float phi = 2.0f * (float)M_PI * random_float();
vec3s h_ts = (vec3s){
sinf(theta) * cosf(phi),
sinf(theta) * sinf(phi),
cosf(theta)
};
vec3s tangent_u; // World-space tangent (U)
vec3s bitangent_v; // World-space bitangent (V)
create_orthonormal_basis(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);
// Transform h from tangent space to world space
vec3s h_ws;
h_ws = glms_vec3_add(scaled_u, scaled_v);
h_ws = glms_vec3_add(h_ws, scaled_n);
// wi is simple now, just reflect wo around h
wi = glms_vec3_reflect(glms_vec3_negate(wo), h_ws);
// The pdf of sampling wi via this blinn-phong is related to the pdf of sampling h and Jacobian of the transformation from h to wi
// Since the pdf(h) is (exp + 1) / (2 * pi) * pow(dot(n, h), exp). The jacobian is 1 / (4 * dot(wo, h)).
// The pdf(wi) will be pdf(h) * jacobian
// We use inverse CDF here to get the cos from sampling.
float cos_theta_h = powf(random_float(), 1.0f / (specular_exponent + 1.0f));
float pdf_h = (specular_exponent + 1.0f) / (2.0f * (float)M_PI) * powf(cos_theta_h, specular_exponent);
float w_dot_h = fmaxf(FLT_EPSILON, glms_vec3_dot(wo, h_ws));
float jacobian = 1.0f / (4.0f * w_dot_h);
pdf_lobe = pdf_h * jacobian;
if (pdf_lobe < FLT_EPSILON)
{
*pdf_out = 0.0f;
return glms_vec3_zero();
}
*pdf_out = pdf_lobe;
}
// Final check to ensure wi is in the correct hemisphere
if (glms_vec3_dot(wi, normal) < 0.0f)
{
*pdf_out = 0.0f;
return glms_vec3_zero();
}
return wi;
}
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* data)
{
const simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
const shading_context_t shading_context = *context;
vec3s h = glms_vec3_normalize(glms_vec3_add(shading_context.wi, 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 f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
vec3s diffuse_color = glms_vec3_scale(shading_data.albedo, 1.0f - shading_data.metallic);
float specular_exponent = roughness_to_blinn_phong_specular_exponent(shading_data.roughness);
// Normalization factor D (Blinn-Phong distribution)
float D_norm = (specular_exponent + 2.0f) / (2.0f * (float)M_PI); // Common normalization
float D = D_norm * powf(n_dot_h, specular_exponent);
vec3s F = fresnel_schlick_vec3(f0, v_dot_h);
vec3s diffuse_term = glms_vec3_scale(diffuse_color, 1.0f / (float)M_PI);
vec3s specular_term = glms_vec3_scale(F, D);
// return diffuse_term;
return glms_vec3_add(diffuse_term, specular_term);
}

View File

@@ -0,0 +1,132 @@
#include "Algorithm/PathTracing.h"
#include "Common.h"
#include "Material.h"
static hit_result_t ray_intersect(const triangle_t triangle, const ray_t ray)
{
hit_result_t result = {0};
vec3s normal = triangle.normal;
float n_dot_r = glms_vec3_dot(normal, ray.direction);
if (n_dot_r > 0.0f)
{
normal = glms_vec3_scale(normal, -1.0f);
}
// triangle is parallel to the ray
if (fabsf(n_dot_r) < FLT_EPSILON)
{
result.hit = false;
return result;
}
// Get distance from ray origin to triangle plane
float distance = (glms_vec3_dot(normal, triangle.point1) - glms_vec3_dot(normal, ray.origin)) / glms_vec3_dot(normal, ray.direction);
if (distance < FLT_EPSILON)
{
result.hit = false;
return result;
}
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 edge1 = glms_vec3_sub(triangle.point2, triangle.point1);
vec3s edge2 = glms_vec3_sub(triangle.point3, triangle.point2);
vec3s edge3 = glms_vec3_sub(triangle.point1, triangle.point3);
vec3s vp = glms_vec3_sub(intersection_point, triangle.point1);
vec3s vp2 = glms_vec3_sub(intersection_point, triangle.point2);
vec3s vp3 = glms_vec3_sub(intersection_point, triangle.point3);
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, c1) < 0.0f
|| glms_vec3_dot(triangle.normal, c2) < 0.0f
|| glms_vec3_dot(triangle.normal, c3) < 0.0f)
{
result.hit = false;
return result;
}
result.hit = true;
result.point = intersection_point;
result.normal = normal;
result.distance = distance;
return result;
}
// TODO: Implement faster methods like BVH, KD-Tree or uniform grid acceleration
vec3s path_trace(const triangle_collection_t* triangles, const material_collection_t* materials, ray_t ray, int max_depth)
{
vec3s accumulated_color = glms_vec3_zero();
vec3s throughput = glms_vec3_one();
int depth = 0;
while (depth < max_depth)
{
uint8_t material_id = 255;
hit_result_t closest_hit = {0};
closest_hit.distance = 1145141919.810f;
for (uint64_t i = 0; i < triangles->count; i++)
{
hit_result_t hit_result = ray_intersect(triangles->buffer[i], ray);
if (hit_result.hit && hit_result.distance < closest_hit.distance)
{
closest_hit = hit_result;
material_id = triangles->buffer[i].material_id;
}
}
if (!closest_hit.hit)
{
// accumulated_color = glms_vec3_add(accumulated_color, throughput); // TODO: Skybox
break;
}
material_t* hit_material = &materials->buffer[material_id];
vec3s emission = hit_material->emission;
accumulated_color = glms_vec3_add(accumulated_color, glms_vec3_mul(throughput, emission));
float pdf;
vec3s wo = glms_vec3_negate(ray.direction); // We need to negate the direction of the incoming ray
vec3s wi = sample_material_bsdf(hit_material, closest_hit.normal, wo, &pdf);
//vec3s wi = random_cosine_direction(closest_hit.normal);
//float cos_theta_s = fmaxf(0.0f, glms_vec3_dot(wi, closest_hit.normal));
//float pdf = cos_theta_s / (float)M_PI;
if (pdf < 0.0f)
{
break;
}
shading_context_t shading_context = {
.normal = closest_hit.normal,
.wi = wi,
.wo = wo
};
vec3s bsdf = evaluate_material_bsdf(hit_material, &shading_context);
float cos_theta = fmaxf(0.0f, glms_vec3_dot(wi, closest_hit.normal));
throughput = glms_vec3_mul(throughput, glms_vec3_scale(bsdf, cos_theta / pdf));
// We do Russian roulette to decide whether to continue tracing or terminate the path
if (depth > 2)
{
float q = fminf(glms_vec3_max(throughput), 0.95f);
if (random_float() > q)
{
break; // Terminate the path
}
}
ray.origin = glms_vec3_add(closest_hit.point, glms_vec3_scale(closest_hit.normal, FLT_EPSILON));
ray.direction = wi;
depth++;
}
return accumulated_color;
}

21
source/Geometry.c Normal file
View File

@@ -0,0 +1,21 @@
#include "Geometry.h"
void quad_create(vec3s center, vec3s forward, vec3s up, float size, uint8_t material_id, triangle_collection_t* collection)
{
float half_size = size / 2.0f;
vec3s right = glms_vec3_cross(forward, up);
vec3s scaled_right = glms_vec3_scale(right, half_size);
vec3s scaled_up = glms_vec3_scale(up, half_size);
vec3s temp_sub = glms_vec3_sub(center, scaled_right);
vec3s temp_add = glms_vec3_add(center, scaled_right);
vec3s top_left = glms_vec3_add(temp_sub, scaled_up);
vec3s top_right = glms_vec3_add(temp_add, scaled_up);
vec3s bottom_right = glms_vec3_sub(temp_add, scaled_up);
vec3s bottom_left = glms_vec3_sub(temp_sub, scaled_up);
triangle_create(top_left, bottom_left, top_right, material_id, collection);
triangle_create(top_right, bottom_right, bottom_left, material_id, collection);
}

80
source/Material.c Normal file
View File

@@ -0,0 +1,80 @@
#include "Material.h"
material_collection_t material_collection_create(size_t size)
{
if (size > 254)
{
size = 254; // Limit the count to 254 to fit in a uint8_t
}
material_collection_t collection = {0};
collection.buffer = (material_t*)malloc(size * sizeof(material_t));
collection.size = (uint8_t)size;
return collection;
}
void material_collection_resize(material_collection_t* materials, size_t size)
{
if (size > 254)
{
size = 254; // Limit the count to 254 to fit in a uint8_t
}
material_t* temp = realloc(materials->buffer, size * sizeof(material_t));
if (temp != NULL)
{
materials->buffer = temp;
materials->size = (uint8_t)size;
}
}
void material_collection_free(material_collection_t* materials)
{
if (materials->buffer != NULL)
{
free(materials->buffer);
materials->buffer = NULL;
}
}
material_t material_create(const sample_bsdf_f sample, const evaluate_bsdf_f evaluate, void* data, material_collection_t* collection)
{
material_t material = {0};
if (collection->count >= collection->size)
{
material_collection_resize(collection, collection->size * 2);
}
material.sample_bsdf = sample;
material.evaluate_bsdf = evaluate;
material.data = data;
material.id = collection->count;
collection->buffer[collection->count] = material;
collection->count++;
return material;
}
vec3s sample_material_bsdf(material_t* material, const vec3s normal, const vec3s wo, float* pdf_out)
{
vec3s wi = glms_vec3_zero();
if (material->sample_bsdf != NULL)
{
wi = material->sample_bsdf(material->data, normal, wo, pdf_out);
}
return wi;
}
vec3s evaluate_material_bsdf(material_t* material, const shading_context_t* context)
{
vec3s bsdf_color = glms_vec3_zero();
if (material->evaluate_bsdf != NULL)
{
bsdf_color = material->evaluate_bsdf(context, material->data);
}
return bsdf_color;
}

19
source/Rendering/Camera.c Normal file
View File

@@ -0,0 +1,19 @@
#include "Rendering/Camera.h"
camera_t camera_create(vec3s position, vec3s forward, vec3s up, float focal_length, float size_x, float aspect_ratio)
{
camera_t camera =
{
.position = position,
.forward = glms_vec3_normalize(forward),
.up = glms_vec3_normalize(up),
.right = glms_vec3_cross(forward, up), // Assuming forward and up are not parallel
.focal_length = focal_length,
.size_x = size_x,
.size_y = size_x / aspect_ratio,
.fov_x = 2.0f * (float)atan(size_x / (2.0f * focal_length)),
.fov_y = 2.0f * (float)atan(size_x / (2.0f * focal_length * aspect_ratio)),
};
return camera;
}

View File

@@ -0,0 +1,44 @@
#include "Rendering/RenderTarget.h"
#include <stdlib.h>
#include <string.h>
render_target_t render_target_create(uint32_t width, uint32_t height)
{
render_target_t target;
target.width = width;
target.height = height;
size_t buffer_size = (size_t)width * height * sizeof(vec4s);
target.buffer = (vec4s*)malloc(buffer_size);
memset(target.buffer, 0, buffer_size);
return target;
}
vec4s render_target_get_pixel(render_target_t* render_target, uint32_t x, uint32_t y)
{
if (x < render_target->width && y < render_target->height)
{
size_t index = (size_t)y * render_target->width + x;
return render_target->buffer[index];
}
return (vec4s){0.0f, 0.0f, 0.0f, 0.0f}; // Return black if out of bounds
}
void render_target_set_pixel(render_target_t* render_target, uint32_t x, uint32_t y, vec4s color)
{
if (x < render_target->width && y < render_target->height)
{
size_t index = (size_t)y * render_target->width + x;
render_target->buffer[index] = color;
}
}
void render_target_free(render_target_t* target)
{
if (target->buffer != NULL)
{
free(target->buffer);
target->buffer = NULL;
}
}

124
source/Rendering/Scene.c Normal file
View File

@@ -0,0 +1,124 @@
#define FLIP_Y
#include "Rendering/Scene.h"
#include "Algorithm/PathTracing.h"
scene_t scene_create(uint64_t triangle_count, uint8_t material_count)
{
scene_t scene = {0};
scene.triangles = triangle_collection_create(triangle_count);
scene.materials = material_collection_create(material_count);
scene.camera = camera_create(
(vec3s){0.0f, 0.0f, 5.0f},
(vec3s){0.0f, 0.0f, -1.0f},
(vec3s){0.0f, 1.0f, 0.0f},
0.025f,
0.036f,
1.777777f
);
return scene;
}
static inline vec2s compute_ndc(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
{
return (vec2s){
.x = (float)x / (float)width,
#ifdef FLIP_Y
.y = 1.0f - (float)y / (float)height
#else
.y = (float)y / (float)height
#endif
};
}
render_target_t scene_render(scene_t* scene, rendering_config_t config)
{
if (fabsf((float)config.width / config.height - scene->camera.aspect_ratio) > FLT_EPSILON)
{
scene->camera = camera_create(
scene->camera.position,
scene->camera.forward,
scene->camera.up,
scene->camera.focal_length,
scene->camera.size_x,
(float)config.width / (float)config.height
);
}
// The actual float buffer inside the render target is on the heap, copy return shoudl be fine.
render_target_t img = render_target_create(config.width, config.height);
uint32_t tile_count_x = (config.width + config.tile_size - 1) / config.tile_size;
uint32_t tile_count_y = (config.height + config.tile_size - 1) / config.tile_size;
uint32_t tile_count = tile_count_x * tile_count_y;
float brightness_per_sample = ((float)M_PI * 2.0f) * (1.0f / config.sample_count);
int64_t x, y, tile_index; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers for OpenMP to work correctly with the loop bounds. To avoid overflow, we need to use int64_t
#ifndef DEBUG
#pragma omp parallel for schedule(dynamic, 1) default(none) \
shared(tile_count_x, tile_count_y, tile_count) \
private(x, y, tile_index)
#endif
for (tile_index = 0; tile_index < tile_count; tile_index++)
{
uint32_t tile_x_0 = tile_index % tile_count_x * config.tile_size;
uint32_t tile_y_0 = tile_index / tile_count_x * config.tile_size;
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + config.tile_size, config.width);
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + config.tile_size, config.height);
vec3s coord = glms_vec3_add(scene->camera.position, glms_vec3_scale(scene->camera.forward, scene->camera.focal_length));
for (y = tile_y_0; y < tile_y_1; y++)
{
for (x = tile_x_0; x < tile_x_1; x++)
{
for (uint16_t k = 0; k < config.sample_count; k++)
{
vec2s position_ndc = compute_ndc(x, y, config.width, config.height);
float screen_x = position_ndc.x * 2.0f - 1.0f;
float screen_y = position_ndc.y * 2.0f - 1.0f;
float sensor_offset_x = screen_x * scene->camera.size_x / 2.0f;
float sensor_offset_y = screen_y * scene->camera.size_y / 2.0f;
vec3s image_plane_point = coord;
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(scene->camera.right, sensor_offset_x));
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(scene->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))
};
vec3s out_color = path_trace(&scene->triangles, &scene->materials, ray, 4);
out_color = glms_vec3_scale(out_color, brightness_per_sample);
if (glms_vec3_eq(out_color, 0.0f))
{
continue;
}
//TODO: Handle alpha
vec4s color = {
.x = out_color.x,
.y = out_color.y,
.z = out_color.z,
.w = 1.0f
};
vec4s old_color = render_target_get_pixel(&img, x, y);
render_target_set_pixel(&img, x, y, glms_vec4_add(old_color, color));
}
}
}
}
return img;
}
void scene_free(scene_t* scene)
{
triangle_collection_free(&scene->triangles);
material_collection_free(&scene->materials);
}

61
source/Triangle.c Normal file
View File

@@ -0,0 +1,61 @@
#include "Triangle.h"
triangle_collection_t triangle_collection_create(size_t size)
{
if (size > UINT64_MAX)
{
size = UINT64_MAX;
}
triangle_collection_t collection = {0};
collection.buffer = (triangle_t*)malloc(size * sizeof(triangle_t));
collection.size = (uint64_t)size;
return collection;
}
void triangle_collection_resize(triangle_collection_t* collection, size_t size)
{
if (size > UINT64_MAX)
{
size = UINT64_MAX;
}
triangle_t* temp = realloc(collection->buffer, size * sizeof(triangle_t));
if (temp != NULL)
{
collection->buffer = temp;
collection->size = (uint64_t)size;
}
}
void triangle_collection_free(triangle_collection_t* collection)
{
if (collection->buffer != NULL)
{
free(collection->buffer);
collection->buffer = NULL;
}
}
void triangle_create(vec3s point1, vec3s point2, vec3s point3, uint8_t material_id, triangle_collection_t* collection)
{
if (collection->count >= collection->size)
{
triangle_collection_resize(collection, collection->size * 2);
}
triangle_t new_triangle;
new_triangle.point1 = point1;
new_triangle.point2 = point2;
new_triangle.point3 = point3;
new_triangle.material_id = material_id;
vec3s edge1 = glms_vec3_sub(point2, point1);
vec3s edge2 = glms_vec3_sub(point3, point1);
new_triangle.normal = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
collection->buffer[collection->count] = new_triangle;
collection->count++;
}

140
source/main.c Normal file
View File

@@ -0,0 +1,140 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <svpng.inc>
#include <omp.h>
#include <math.h>
#include "MaterialExtra.h"
#include "Rendering/Scene.h"
#include "Algorithm/BSDF.h"
#include "Material.h"
#include "Triangle.h"
#include "Geometry.h"
static inline void save_img(render_target_t* source, const uint32_t width, const uint32_t height, const char* filename)
{
FILE* fileStream;
fopen_s(&fileStream, filename, "wb");
if (fileStream == NULL)
{
fprintf(stderr, "Failed to open file for writing: %s\n", filename);
return;
}
unsigned char* img = (unsigned char*)malloc((size_t)width * height * 4);
if (img == NULL)
{
fprintf(stderr, "Failed to allocate memory for image\n");
return;
}
for (uint32_t y = 0; y < height; y++)
{
for (uint32_t x = 0; x < width; x++)
{
size_t index = ((size_t)y * width + x) * (size_t)4.0;
vec4s color = render_target_get_pixel(source, x, y);
img[index] = (unsigned char)fminf(color.x * 255.0f, 255.0f);
img[index + 1] = (unsigned char)fminf(color.y * 255.0f, 255.0f);
img[index + 2] = (unsigned char)fminf(color.z * 255.0f, 255.0f);
img[index + 3] = (unsigned char)fminf(color.w * 255.0f, 255.0f);
}
}
svpng(fileStream, width, height, img, 1);
fclose(fileStream);
free(img);
}
int main()
{
omp_set_num_threads(24);
const int SAMPLE_COUNT = 256;
const uint32_t WIDTH = 640;
const uint32_t HEIGHT = 360;
scene_t scene = scene_create(64, 4);
simple_lit_data_t gray_lit_data = {
.albedo = (vec3s){0.73f, 0.73f, 0.73f},
.roughness = 1.0f,
.metallic = 0.0f,
};
simple_lit_data_t blue_lit_data = {
.albedo = (vec3s){0.0f, 0.0f, 1.0f},
.roughness = 0.5f,
.metallic = 1.0f,
};
material_t top_light_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
material_t gray_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
material_t blue_material = material_create_simple_lit(&blue_lit_data, &scene.materials);
scene.materials.buffer[top_light_material.id].emission = (vec3s){4.0f, 4.0f, 4.0f};
quad_create(
(vec3s){0.0f, 1.95f, 0.0f},
(vec3s){0.0f, -1.0f, 0.0f},
(vec3s){0.0f, 0.0f, 1.0f},
1.0f, top_light_material.id, &scene.triangles
);
triangle_create(
(vec3s){-1.0f, -1.0f, 0.0f},
(vec3s){1.0f, -1.0f, 0.0f},
(vec3s){0.0f, 1.0f, 0.0f},
gray_material.id, &scene.triangles
);
quad_create(
(vec3s){0.0f, -2.0f, 0.0f},
(vec3s){0.0f, 1.0f, 0.0f},
(vec3s){0.0f, 0.0f, 1.0f},
4.0f, blue_material.id, &scene.triangles
);
quad_create(
(vec3s){0.0f, 2.0f, 0.0f},
(vec3s){0.0f, -1.0f, 0.0f},
(vec3s){0.0f, 0.0f, 1.0f},
4.0f, gray_material.id, &scene.triangles
);
quad_create(
(vec3s){0.0f, 0.0f, -2.0f},
(vec3s){0.0f, 0.0f, 1.0f},
(vec3s){0.0f, 1.0f, 0.0f},
4.0f, gray_material.id, &scene.triangles
);
quad_create(
(vec3s){-2.0f, 0.0f, 0.0f},
(vec3s){1.0f, 0.0f, 0.0f},
(vec3s){0.0f, 1.0f, 0.0f},
4.0f, gray_material.id, &scene.triangles
);
quad_create(
(vec3s){2.0f, 0.0f, 0.0f},
(vec3s){-1.0f, 0.0f, 0.0f},
(vec3s){0.0f, 1.0f, 0.0f},
4.0f, gray_material.id, &scene.triangles
);
rendering_config_t config = {
.width = WIDTH,
.height = HEIGHT,
.sample_count = SAMPLE_COUNT,
.max_depth = 4,
.tile_size = 16,
};
render_target_t img = scene_render(&scene, config);
save_img(&img, WIDTH, HEIGHT, "output.png");
render_target_free(&img);
scene_free(&scene);
return 0;
}