Enhance build system and project documentation

Added LICENSE file with MIT License and copyright notice.
Added preview.png binary file for project assets.
Changed CMakeLists.txt to include asset copying command.
Changed mesh loading in Mesh.c to support smooth normals.
Changed ray origin biasing in PathTracing.c and shadow rays.
Changed ray-triangle intersection logic in RayIntersection.c.
Changed ray_intersect_bvh_count function in Debug.c to static.
Changed rendering functions in Scene.c with TODO for optimization.
Updated README.md with project description and build instructions.
Updated window dimensions in main.c for testing purposes.
This commit is contained in:
2025-04-21 21:03:08 +09:00
parent 6800810369
commit 59941241c3
13 changed files with 194 additions and 184 deletions

View File

@@ -84,7 +84,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
throughput = glms_vec3_mul(throughput, glms_vec3_scale(bsdf, cos_theta / pdf_bsdf));
// We do Russian roulette to decide whether to continue tracing or terminate the path
if (depth > 2)
if (depth > 1)
{
float q = fminf(glms_vec3_max(throughput), 0.95f);
float rr_sample = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
@@ -97,7 +97,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 = glms_vec3_add(closest_hit.point, glms_vec3_scale(closest_hit.normal, 1.192092896e-04F));
active_ray.origin = BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal);
active_ray.direction = wi;
prev_normal = closest_hit.normal;

View File

@@ -1,5 +1,6 @@
#include "algorithm/RayIntersection.h"
#include "Geometry/Triangle.h"
#include "cglm/struct/vec3.h"
hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
{
@@ -15,16 +16,14 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
// 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_face, triangle.point_1) - glms_vec3_dot(normal_face, ray.origin)) / glms_vec3_dot(normal_face, ray.direction);
if (distance < FLT_EPSILON)
if (distance < RAY_EPSILON)
{
result.hit = false;
return result;
}
@@ -43,99 +42,25 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
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, c2) < 0.0f
|| glms_vec3_dot(triangle.normal_face, c3) < 0.0f)
{
result.hit = false;
return result;
}
// Normal interpolation
float d00 = glms_vec3_dot(edge1, edge1);
float d01 = glms_vec3_dot(edge1, edge2);
float d11 = glms_vec3_dot(edge2, edge2);
float d20 = glms_vec3_dot(vp, edge1);
float d21 = glms_vec3_dot(vp, edge2);
float denom = d00 * d11 - d01 * d01;
float v = (d11 * d20 - d01 * d21) / denom;
float w = (d00 * d21 - d01 * d20) / denom;
float u = 1.0f - v - w;
vec3s normal_interp = glms_vec3_addadd(
glms_vec3_scale(triangle.normal_1, u),
glms_vec3_scale(triangle.normal_2, v),
glms_vec3_scale(triangle.normal_3, w));
normal_interp = glms_vec3_normalize(normal_interp);
if (n_dot_r > 0.0f)
{
normal_interp = glms_vec3_negate(normal_interp);
}
// TODO: Normal interpolation
result.hit = true;
result.point = intersection_point;
result.normal = normal_interp;
result.normal = normal_face;
result.distance = distance;
return result;
}
static hit_result_t intersect_triangle(ray_t ray, triangle_t tri)
{
hit_result_t result = {0};
vec3s edge1 = glms_vec3_sub(tri.point_2, tri.point_1);
vec3s edge2 = glms_vec3_sub(tri.point_3, tri.point_1);
vec3s h = glms_vec3_cross(ray.direction, edge2);
float a = glms_vec3_dot(edge1, h);
if (a > -FLT_EPSILON && a < FLT_EPSILON)
{
return result;
}
float f = 1.0f / a;
vec3s s = glms_vec3_sub(ray.origin, tri.point_1);
float u = f * glms_vec3_dot(s, h);
if (u < 0.0f || u > 1.0f)
{
return result; // Hit outside the triangle's u range.
}
vec3s q = glms_vec3_cross(s, edge1);
float v = f * glms_vec3_dot(ray.direction, q);
if (v < 0.0f || u + v > 1.0f)
{
return result; // Hit outside the triangle's v range.
}
// At this stage we can compute t to find out where the intersection point is on the line.
float t = f * glms_vec3_dot(edge2, q);
float esp = fmaxf(glms_vec3_max(glms_vec3_abs(edge1)), glms_vec3_max(glms_vec3_abs(edge2))) * 1.192092896e-04F;
if (t > 1.192092896e-04F)
{
result.hit = true;
result.distance = t;
result.point = glms_vec3_add(ray.origin, glms_vec3_scale(ray.direction, t));
result.normal = glms_vec3_cross(edge1, edge2);
if (glms_vec3_dot(result.normal, ray.direction) > 0)
{
result.normal = glms_vec3_scale(result.normal, -1.0f);
}
result.normal = glms_vec3_normalize(result.normal);
return result;
}
return result;
}
bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_out)
{
//vec3s inv_dir = glms_vec3_div(glms_vec3_one(), ray.direction);
// vec3s inv_dir = glms_vec3_div(glms_vec3_one(), ray.direction);
vec3s inv_dir;
inv_dir.x = ray.direction.x != 0.0f ? 1.0f / ray.direction.x : FLT_MAX;
inv_dir.y = ray.direction.y != 0.0f ? 1.0f / ray.direction.y : FLT_MAX;
@@ -150,7 +75,7 @@ bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_ou
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
t_far = fminf(t_far, tmax);
}
// Y axis
{
@@ -159,7 +84,7 @@ bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_ou
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
t_far = fminf(t_far, tmax);
}
// Z axis
{
@@ -168,7 +93,7 @@ bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_ou
float tmin = fminf(t1, t2);
float tmax = fmaxf(t1, t2);
t_near = fmaxf(t_near, tmin);
t_far = fminf(t_far, tmax);
t_far = fminf(t_far, tmax);
}
if (t_near > t_far || t_far < 0.0f)
@@ -176,8 +101,10 @@ bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_ou
return false;
}
if (enter_out) *enter_out = t_near;
if (exit_out) *exit_out = t_far;
if (enter_out)
*enter_out = t_near;
if (exit_out)
*exit_out = t_far;
return true;
}
@@ -191,18 +118,19 @@ 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, hit_result_t* best_hit_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];
float enter, exit;
if (!ray_intersect_aabb(ray, node->bounds, &enter, &exit)) {
if (!ray_intersect_aabb(ray, node->bounds, &enter, &exit))
{
return;
}
if (enter > *closest_out) {
if (enter > *closest_out)
{
return;
}
@@ -212,8 +140,8 @@ void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes,
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 = intersect_triangle(ray, all_triangles->buffer[triangle_index]);
if (hit_result.hit && hit_result.distance < *closest_out )
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;
*best_hit_out = hit_result;
@@ -233,7 +161,6 @@ void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes,
bool hit_left_aabb = ray_intersect_aabb(ray, left_child->bounds, &left_enter, &left_exit);
bool hit_right_aabb = ray_intersect_aabb(ray, right_child->bounds, &right_enter, &right_exit);
// Traverse children based on closest AABB and current best hit distance (*closest_t)
if (hit_left_aabb && hit_right_aabb)
{
@@ -270,8 +197,7 @@ hit_result_t ray_intersect_scene(ray_t ray, const scene_t* scene)
hit_result_t result = {0};
float closest = FLT_MAX;
if (scene == NULL || scene->bvh_tree.nodes == NULL
|| scene->triangles.count == 0 || scene->bvh_tree.node_count == 0 || scene->bvh_tree.primitive_count == 0)
if (scene == NULL || scene->bvh_tree.nodes == NULL || scene->triangles.count == 0 || scene->bvh_tree.node_count == 0 || scene->bvh_tree.primitive_count == 0)
{
return result;
}

View File

@@ -1,7 +1,7 @@
#include "Debug.h"
#include "Algorithm/RayIntersection.h"
void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t node_index, uint32_t* count_out)
static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t node_index, uint32_t* count_out)
{
const float _MAX_DIST = 1e6f;
if (bvh_tree.nodes == NULL || bvh_tree.primitive_indices == NULL || count_out == NULL)

View File

@@ -8,10 +8,10 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
{
mesh_entity_t entity = {0};
const C_STRUCT aiScene* scene = aiImportFile(filename,aiProcessPreset_TargetRealtime_MaxQuality);
const C_STRUCT aiScene* scene = aiImportFile(filename, aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_GenSmoothNormals);
if (scene == NULL)
{
// fprintf(stderr, "Error loading mesh: %s\n", aiGetErrorString());
perror(aiGetErrorString());
return entity;
}
@@ -19,10 +19,9 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
{
struct aiMesh* mesh = scene->mMeshes[i];
//TODO: Handle all mesh types, not just triangles
//TODO: Handle all primitive types, not just triangles
if (mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
{
// fprintf(stderr, "Mesh %llu is not a triangle mesh\n", i);
continue;
}
@@ -31,40 +30,43 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
struct aiFace* face = &mesh->mFaces[j];
if (face->mNumIndices != 3)
{
// fprintf(stderr, "Face %llu in mesh %llu does not have 3 indices\n", j, i);
continue;
}
uint32_t index1 = face->mIndices[0];
uint32_t index2 = face->mIndices[1];
uint32_t index3 = face->mIndices[2];
vec3s point1 = {
mesh->mVertices[face->mIndices[0]].x,
mesh->mVertices[face->mIndices[0]].y,
mesh->mVertices[face->mIndices[0]].z
mesh->mVertices[index1].x,
mesh->mVertices[index1].y,
mesh->mVertices[index1].z
};
vec3s point2 = {
mesh->mVertices[face->mIndices[1]].x,
mesh->mVertices[face->mIndices[1]].y,
mesh->mVertices[face->mIndices[1]].z
mesh->mVertices[index2].x,
mesh->mVertices[index2].y,
mesh->mVertices[index2].z
};
vec3s point3 = {
mesh->mVertices[face->mIndices[2]].x,
mesh->mVertices[face->mIndices[2]].y,
mesh->mVertices[face->mIndices[2]].z
mesh->mVertices[index3].x,
mesh->mVertices[index3].y,
mesh->mVertices[index3].z
};
vec3s normal1 = {
mesh->mNormals[face->mIndices[0]].x,
mesh->mNormals[face->mIndices[0]].y,
mesh->mNormals[face->mIndices[0]].z
mesh->mNormals[index1].x,
mesh->mNormals[index1].y,
mesh->mNormals[index1].z
};
vec3s normal2 = {
mesh->mNormals[face->mIndices[1]].x,
mesh->mNormals[face->mIndices[1]].y,
mesh->mNormals[face->mIndices[1]].z
mesh->mNormals[index2].x,
mesh->mNormals[index2].y,
mesh->mNormals[index2].z
};
vec3s normal3 = {
mesh->mNormals[face->mIndices[2]].x,
mesh->mNormals[face->mIndices[2]].y,
mesh->mNormals[face->mIndices[2]].z
mesh->mNormals[index3].x,
mesh->mNormals[index3].y,
mesh->mNormals[index3].z
};
//TODO: Handle materials, we use OpenPBR standard for parameter naming

View File

@@ -18,7 +18,7 @@ vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_c
}
ray_t shadow_ray = {
.origin = glms_vec3_add(context->hit_point, glms_vec3_scale(context->normal, 1.192092896e-04F)),
.origin = BIAS_RAY_ORIGION(context->hit_point, context->normal),
.direction = wi,
};

View File

@@ -21,7 +21,7 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
float pdf = 1.0f / (4.0f * (float)M_PI);
ray_t shadow_ray = {
.origin = glms_vec3_add(context->hit_point, glms_vec3_scale(context->normal, 1.192092896e-04F)),
.origin = BIAS_RAY_ORIGION(context->hit_point, context->normal),
.direction = wi,
};

View File

@@ -154,7 +154,7 @@ static void screen_render_pixel(scene_t* scene, rendering_config_t config, vec3s
*pixel_color = glms_vec4_scale(accumulated_color, 1.0f / (float)sample_count);
}
// TODO: This should be moved to renderer
bool scene_render_tile(scene_t* scene, rendering_context_t* ctx, rendering_config_t config, uint32_t tile_index, int rendering_flag, render_target_t* render_target, tile_t* tile_out)
{
if (ctx->is_done)
@@ -210,6 +210,7 @@ bool scene_render_tile(scene_t* scene, rendering_context_t* ctx, rendering_confi
return true;
}
// TODO: This should be moved to renderer
bool scene_render(scene_t* scene, rendering_config_t config, int rendering_flag, render_target_t* render_target)
{
ensure_camera_aspect_ratio(&scene->camera, config);

View File

@@ -1,18 +1,18 @@
#include <svpng.inc>
#include <omp.h>
#include <stdint.h>
#include <svpng.inc>
#include "Algorithm/Sobol.h"
#include "Debug.h"
#include "Geometry/GeometryUtilities.h"
// #include "Geometry/GeometryUtilities.h"
#include "Geometry/Mesh.h"
#include "Lighting/SkyLight.h"
#include "Material/SimpleLit.h"
#include "Rendering/Scene.h"
#include "Rendering/PostProcessing.h"
#include "Rendering/Scene.h"
#include "Window.h"
static void save_img(render_target_t* source, uint32_t width, uint32_t height, const char* filename)
static void save_img(render_target_t* source, uint32_t width, uint32_t height, const char* filename)
{
FILE* file_stream;
fopen_s(&file_stream, filename, "wb");
@@ -33,8 +33,8 @@ static void save_img(render_target_t* source, uint32_t width, uint32_t height,
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR pCmdLine, _In_ int nCmdShow)
{
rendering_config_t config = {
.width = 1920 / 1,
.height = 1080 / 1,
.width = 1920 / 4,
.height = 1080 / 4,
.sample_count = 64,
.max_depth = 4,
.bucket_size = 64,
@@ -67,52 +67,50 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
sun_light->intensity = 2.0f;
sun_light->angular_diameter = 0.53f;
scene.lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t)
{
scene.lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t){
.color = (vec3s){0.73f, 0.82f, 1.0f},
.intensity = 1.0f,
});
simple_lit_data_t gray_lit_data =
{
.albedo = (vec3s){0.73f, 0.73f, 0.73f},
.roughness = 0.5f,
.metallic = 0.0f,
};
simple_lit_data_t blue_lit_data =
{
.albedo = (vec3s){0.0f, 0.0f, 1.0f},
.roughness = 0.5f,
.metallic = 0.0f,
};
simple_lit_data_t red_lit_data =
{
.albedo = (vec3s){1.0f, 0.0f, 0.0f},
.roughness = 0.5f,
.metallic = 0.0f,
};
simple_lit_data_t green_lit_data =
{
.albedo = (vec3s){0.0f, 1.0f, 0.0f},
.roughness = 0.5f,
.metallic = 0.0f,
};
simple_lit_data_t floor_lit_data =
{
// simple_lit_data_t gray_lit_data =
// {
// .albedo = (vec3s){0.73f, 0.73f, 0.73f},
// .roughness = 0.5f,
// .metallic = 0.0f,
// };
// simple_lit_data_t blue_lit_data =
// {
// .albedo = (vec3s){0.0f, 0.0f, 1.0f},
// .roughness = 0.5f,
// .metallic = 0.0f,
// };
// simple_lit_data_t red_lit_data =
// {
// .albedo = (vec3s){1.0f, 0.0f, 0.0f},
// .roughness = 0.5f,
// .metallic = 0.0f,
// };
// simple_lit_data_t green_lit_data =
// {
// .albedo = (vec3s){0.0f, 1.0f, 0.0f},
// .roughness = 0.5f,
// .metallic = 0.0f,
// };
simple_lit_data_t floor_lit_data = {
.albedo = (vec3s){1.0f, 1.0f, 1.0f},
.roughness = 0.95f,
.metallic = 0.0f,
};
material_entity_t gray_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
material_entity_t gray_light_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
material_entity_t blue_material = material_create_simple_lit(&blue_lit_data, &scene.materials);
material_entity_t red_material = material_create_simple_lit(&red_lit_data, &scene.materials);
material_entity_t green_material = material_create_simple_lit(&green_lit_data, &scene.materials);
// material_entity_t gray_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
// material_entity_t gray_light_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
// material_entity_t blue_material = material_create_simple_lit(&blue_lit_data, &scene.materials);
// material_entity_t red_material = material_create_simple_lit(&red_lit_data, &scene.materials);
// material_entity_t green_material = material_create_simple_lit(&green_lit_data, &scene.materials);
material_entity_t floor_material = material_create_simple_lit(&floor_lit_data, &scene.materials);
//scene.materials.buffer[gray_light_material.id].emission = (vec3s){10.0f, 10.0f, 10.0f};
// scene.materials.buffer[gray_light_material.id].emission = (vec3s){10.0f, 10.0f, 10.0f};
mesh_load("F:/c/SimpleRayTracer/assets/sponza.obj", floor_material.id, &scene.triangles, &scene.materials);
mesh_load("./assets/sponza.obj", floor_material.id, &scene.triangles, &scene.materials);
// quad_create(
// (vec3s){0.0f, 3.95f, 0.0f},
@@ -162,16 +160,6 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
goto exit;
}
// BVH Debug
for (uint32_t i = 0; i < scene.bvh_tree.node_count; i++)
{
bvh_node_t* node = &scene.bvh_tree.nodes[i];
aabb_t bounds = node->bounds;
vec3s min = bounds.min;
vec3s max = bounds.max;
printf("Node %u: Min: (%f, %f, %f), Max: (%f, %f, %f)\n", i, min.x, min.y, min.z, max.x, max.y, max.z);
}
MSG msg;
uint16_t tile_index = 0;
tile_t current_tile = {0};
@@ -190,7 +178,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
DispatchMessage(&msg);
}
if (scene_render_tile(&scene, &ctx, config, tile_index, DEBUG_BVH, &img, &current_tile))
// TODO: This hurt performance a lot, need to be optimized
if (scene_render_tile(&scene, &ctx, config, tile_index, DEBUG_NONE, &img, &current_tile))
{
for (uint32_t y = current_tile.y; y < current_tile.y + current_tile.height; y++)
{