Add HDR sky lighting support and improve rendering
Added support for HDR sky lighting, including sampling environment maps with a hierarchical CDF. Added new utility functions for handling HDR sky data, including memory management and sampling functions. Added functions to improve texture handling, including pixel data retrieval and texture coordinate management. Changed the path tracing algorithm to enhance light evaluation from HDR skies and adjust light contribution calculations. Changed BSDF sampling functions to utilize constants from Common.h for better readability. Changed the main application logic to load HDR textures and configure the scene with improved lighting settings. Refactored ray intersection logic to enhance accuracy and performance in triangle intersections. Adjusted the sample count in the rendering configuration to optimize performance. Updated the README.md to document new features and example renders.
This commit is contained in:
@@ -57,7 +57,7 @@ vec3s normal_ts_to_ws(vec3s normal, vec3s tangent)
|
||||
|
||||
float pdf_cosine_weighted_hemisphere(vec3s normal, vec3s wi)
|
||||
{
|
||||
return fmaxf(glms_vec3_dot(wi, normal), 0.0f) / (float)M_PI;
|
||||
return fmaxf(glms_vec3_dot(wi, normal), 0.0f) / PI;
|
||||
}
|
||||
|
||||
float pdf_blinn_phong_lobe(vec3s normal, vec3s wi, vec3s wo, float roughness)
|
||||
@@ -85,7 +85,7 @@ float pdf_blinn_phong_lobe(vec3s normal, vec3s wi, vec3s wo, float roughness)
|
||||
// PDF of sampling h (Blinn-Phong distribution)
|
||||
// D(h) = (specular_exponent + 1) / (2 * PI) * pow(max(0, dot(n, h)), specular_exponent)
|
||||
float n_dot_h = fmaxf(0.0f, glms_vec3_dot(normal, h));
|
||||
float pdf_h = (specular_exponent + 1.0f) / (2.0f * (float)M_PI) * powf(n_dot_h, specular_exponent);
|
||||
float pdf_h = (specular_exponent + 1.0f) / (2.0f * PI) * powf(n_dot_h, specular_exponent);
|
||||
|
||||
// Jacobian of the transformation from h to wi
|
||||
// jacobian = 1 / (4 * dot(wo, h))
|
||||
@@ -103,7 +103,7 @@ vec3s sample_cosine_weighted_hemisphere_z_angular(float angular, uint32_t index,
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
|
||||
float phi = 2.0f * (float)M_PI * r1;
|
||||
float phi = 2.0f * PI * r1;
|
||||
|
||||
float cos_angular = cosf(angular);
|
||||
// Correctly sample cos(theta) for cosine weighting within the cone [cos_angular, 1]
|
||||
@@ -128,7 +128,7 @@ vec3s sample_cosine_weighted_hemisphere_z(uint32_t index, uint32_t d1, uint32_t
|
||||
float r2 = sobol_sample(index, d2);
|
||||
|
||||
float r = sqrtf(r1);
|
||||
float phi = 2.0f * (float)M_PI * r2;
|
||||
float phi = 2.0f * PI * r2;
|
||||
|
||||
float disk_x = r * cosf(phi);
|
||||
float disk_y = r * sinf(phi);
|
||||
@@ -198,7 +198,7 @@ vec3s random_uniform_cdf_direction(vec3s direction, uint32_t index, uint32_t d1,
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
|
||||
float phi = 2.0f * (float)M_PI * r1;
|
||||
float phi = 2.0f * PI * r1;
|
||||
float cos_theta = 1.0f - r2 * 2.0f;
|
||||
float sin_theta = sqrtf(fmaxf(0.0f, 1.0f - cos_theta * cos_theta));
|
||||
|
||||
@@ -226,7 +226,7 @@ vec3s random_uniform_cdf_direction_angular(vec3s direction, uint32_t index, floa
|
||||
float cos_theta = 1.0f - r1 * (1.0f - cos_alpha);
|
||||
float sin_theta = sqrtf(fmaxf(0.0f, 1.0f - cos_theta * cos_theta));
|
||||
|
||||
float phi = 2.0f * (float)M_PI * r2;
|
||||
float phi = 2.0f * PI * r2;
|
||||
|
||||
float x = sin_theta * cosf(phi);
|
||||
float y = sin_theta * sinf(phi);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "Algorithm/PathTracing.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Lighting/LightEvaluation.h"
|
||||
|
||||
// TODO: Split the diffuse and specular into different Monte Carlo, so we can decide the sample count for each one
|
||||
@@ -10,7 +9,6 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
vec3s throughput = glms_vec3_one();
|
||||
|
||||
ray_t active_ray = ray;
|
||||
vec3s prev_normal = glms_vec3_zero();
|
||||
float pdf_bsdf = 1.0f;
|
||||
|
||||
uint16_t depth = 0;
|
||||
@@ -27,12 +25,6 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
.textures = &scene->textures,
|
||||
};
|
||||
path_output sky_output = evaluate_bsdf_sky(&scene->lights, &light_context, throughput, sample_index);
|
||||
if (depth > 0)
|
||||
{
|
||||
// Have to multiply the weight since we evaluate the sky at each bounce
|
||||
float weight = power_heuristic(pdf_bsdf, sky_output.pdf);
|
||||
sky_output.direct_lighting = glms_vec3_scale(sky_output.direct_lighting, weight);
|
||||
}
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_output.direct_lighting, 0.0f));
|
||||
break;
|
||||
}
|
||||
@@ -58,21 +50,11 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
|
||||
path_output material_output = render_material(hit_material, &shading_context);
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(material_output.direct_lighting, 0.0f));
|
||||
pdf_bsdf = material_output.pdf;
|
||||
|
||||
float cos_theta = fmaxf(0.0f, glms_vec3_dot(material_output.wi, closest_hit.normal));
|
||||
throughput = glms_vec3_mul(throughput, material_output.bsdf);
|
||||
|
||||
switch (material_output.state)
|
||||
{
|
||||
case TERMINATE:
|
||||
goto end_path_trace;
|
||||
case PATH_THROUGH:
|
||||
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, glms_vec3_negate(closest_hit.normal)), active_ray.direction);
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// We do Russian roulette to decide whether to continue tracing or terminate the path
|
||||
if (depth > 1)
|
||||
{
|
||||
@@ -86,11 +68,18 @@ 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 = ray_create(BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal), material_output.wi);
|
||||
prev_normal = closest_hit.normal;
|
||||
pdf_bsdf = material_output.pdf;
|
||||
|
||||
depth++;
|
||||
switch (material_output.state)
|
||||
{
|
||||
case TERMINATE:
|
||||
goto end_path_trace;
|
||||
//case PATH_THROUGH:
|
||||
// active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, glms_vec3_negate(closest_hit.normal)), active_ray.direction);
|
||||
// continue;
|
||||
default:
|
||||
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal), material_output.wi);
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end_path_trace:
|
||||
|
||||
@@ -63,100 +63,8 @@ vec3s offset_ray_origin(vec3s point, vec3s normal, vec3s wo)
|
||||
}
|
||||
|
||||
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(const ray_t* ray, const triangle_t* triangle)
|
||||
{
|
||||
hit_result_t result = {0};
|
||||
|
||||
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 = triangle->face_normal;
|
||||
float n_dot_r = glms_vec3_dot(normal, ray->direction);
|
||||
if (n_dot_r > 0.0f)
|
||||
{
|
||||
normal = glms_vec3_negate(normal);
|
||||
}
|
||||
|
||||
// triangle is parallel to the ray
|
||||
if (fabsf(n_dot_r) < FLT_EPSILON)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get distance from ray origin to triangle plane
|
||||
float distance = (glms_vec3_dot(normal, triangle->vertices[0].position) - glms_vec3_dot(normal, ray->origin)) / glms_vec3_dot(normal, ray->direction);
|
||||
|
||||
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));
|
||||
|
||||
// Check if the intersection point is inside the triangle using barycentric coordinates
|
||||
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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
#else
|
||||
hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle)
|
||||
{
|
||||
hit_result_t result = {0};
|
||||
@@ -210,6 +118,7 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
||||
result.distance = t;
|
||||
result.point = glms_vec3_add(origin, glms_vec3_scale(direction, t));
|
||||
|
||||
// Should we output u, v, w instead of normal, tangent, and uv?
|
||||
vec3s normal = glms_vec3_scale(triangle->vertices[0].normal, w);
|
||||
normal = glms_vec3_add(normal, glms_vec3_scale(triangle->vertices[1].normal, u));
|
||||
normal = glms_vec3_add(normal, glms_vec3_scale(triangle->vertices[2].normal, v));
|
||||
@@ -230,7 +139,6 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ray_intersect_aabb(const ray_t* ray, aabb_t aabb, float* enter_out, float* exit_out)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user