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:
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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öller–Trumbore
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
@@ -36,7 +87,7 @@ mesh_entity_t mesh_load(const char* filename, uint8_t material_id, triangle_coll
|
||||
uint32_t index1 = face->mIndices[0];
|
||||
uint32_t index2 = face->mIndices[1];
|
||||
uint32_t index3 = face->mIndices[2];
|
||||
|
||||
|
||||
vec3s point1 = {
|
||||
mesh->mVertices[index1].x,
|
||||
mesh->mVertices[index1].y,
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -14,4 +14,4 @@ camera_t camera_create(vec3s position, versors rotation, float focal_length, flo
|
||||
};
|
||||
|
||||
return camera;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
198
source/Rendering/Texture.c
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user