Enhance graphics library functionality and structure
Added new function signatures in `assimp-vc143-mt.lib` for improved logging, parsing, and vector operations. Added new metadata and configuration information in `assimp-vc143-mt.dll` for versioning and licensing compliance. Added Sobol sequence generation in `Sobol.c` for quasi-random sampling. Added window message handling in `Window.c` for rendering graphics. Added ray-triangle intersection tests in `RayIntersection.c` for collision detection. Added functions for loading mesh data in `Mesh.c` to support 3D model import. Added functions for managing triangle collections in `Triangle.c` to enhance geometric data handling. Added light evaluation functions in `LightEvaluation.c` and `SkyLight.c` for realistic rendering. Added sampling and evaluation functions for simple lit materials in `SimpleLit.c`. Changed various header files to include copyright and licensing information. Changed existing functions in multiple files to improve performance and clarity. Removed unused code in several files to streamline the library.
This commit is contained in:
@@ -1,104 +1,73 @@
|
||||
#include "Algorithm/PathTracing.h"
|
||||
#include "Common.h"
|
||||
#include "Material.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/Sobol.h"
|
||||
#include "Lighting/LightEvaluation.h"
|
||||
|
||||
static hit_result_t ray_intersect(const triangle_t triangle, const ray_t ray)
|
||||
// TODO: Implement a faster methods like BVH, KD-Tree or uniform grid acceleration
|
||||
// TODO: Split the diffuse and specular into different Monte Carlo, so we can decide the sample count for each one
|
||||
vec3s path_trace(const scene_t* scene, const ray_t ray, const uint32_t sample_index, const int max_depth)
|
||||
{
|
||||
hit_result_t result = {0};
|
||||
const triangle_collection_t* triangles = &scene->triangles;
|
||||
const material_collection_t* materials = &scene->materials;
|
||||
const light_collection_t* lights = &scene->lights;
|
||||
|
||||
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();
|
||||
|
||||
ray_t active_ray = ray;
|
||||
vec3s prev_normal = glms_vec3_zero();
|
||||
float pdf_bsdf = 1.0f; // Even though pdf_bsdf should be avaliable after the first bounce. For seafty, we set it to 1.0f for the first iteration.
|
||||
sobol_state_t sobol_state = {sample_index, 0};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
hit_result_t closest_hit = ray_intersect_closest(triangles, active_ray);
|
||||
|
||||
if (!closest_hit.hit)
|
||||
{
|
||||
// accumulated_color = glms_vec3_add(accumulated_color, throughput); // TODO: Skybox
|
||||
vec3s sky_light = evaluate_bsdf_sky(scene, NULL, &sobol_state);
|
||||
if (depth > 0)
|
||||
{
|
||||
// Have to multiply the weight since we evaluate the sky at each bounce
|
||||
float pdf_nee = pdf_cosine_weighted_hemisphere(prev_normal, active_ray.direction);
|
||||
float weight = power_heuristic(pdf_bsdf, pdf_nee);
|
||||
sky_light = glms_vec3_scale(sky_light, weight);
|
||||
}
|
||||
accumulated_color = glms_vec3_add(accumulated_color, sky_light); // TODO: Skybox
|
||||
break;
|
||||
}
|
||||
|
||||
material_t* hit_material = &materials->buffer[material_id];
|
||||
// Add the emission of the hit material to the accumulated color
|
||||
material_t* hit_material = &materials->buffer[triangles->buffer[closest_hit.triangle_id].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)
|
||||
light_shading_context_t light_context = {
|
||||
.normal = closest_hit.normal,
|
||||
.hit_point = closest_hit.point,
|
||||
.wo = active_ray.direction,
|
||||
.throughput = throughput,
|
||||
.triangles = triangles,
|
||||
.material = hit_material
|
||||
};
|
||||
|
||||
// Running the light loop.
|
||||
// TODO: Implementing other light types.
|
||||
for (uint32_t i = 0; i < lights->directional_light_count; i++)
|
||||
{
|
||||
vec3s l = evaluate_bsdf_directional(lights->directional_lights[i], &light_context, &sobol_state);
|
||||
accumulated_color = glms_vec3_add(accumulated_color, l);
|
||||
}
|
||||
|
||||
vec3s sky_light = evaluate_bsdf_sky(scene, &light_context, &sobol_state);
|
||||
accumulated_color = glms_vec3_add(accumulated_color, sky_light);
|
||||
|
||||
// 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, &sobol_state, &pdf_bsdf);
|
||||
if (pdf_bsdf <= 0.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -110,20 +79,24 @@ vec3s path_trace(const triangle_collection_t* triangles, const material_collecti
|
||||
};
|
||||
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
|
||||
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)
|
||||
{
|
||||
float q = fminf(glms_vec3_max(throughput), 0.95f);
|
||||
if (random_float() > q)
|
||||
{
|
||||
break; // Terminate the path
|
||||
break; // Terminate the path
|
||||
}
|
||||
// Keep the energy of the path by scaling the throughput
|
||||
throughput = glms_vec3_scale(throughput, 1.0f / q);
|
||||
}
|
||||
|
||||
ray.origin = glms_vec3_add(closest_hit.point, glms_vec3_scale(closest_hit.normal, FLT_EPSILON));
|
||||
ray.direction = wi;
|
||||
active_ray.origin = glms_vec3_add(closest_hit.point, glms_vec3_scale(closest_hit.normal, FLT_EPSILON));
|
||||
active_ray.direction = wi;
|
||||
prev_normal = closest_hit.normal;
|
||||
|
||||
depth++;
|
||||
}
|
||||
|
||||
114
source/Algorithm/RayIntersection.c
Normal file
114
source/Algorithm/RayIntersection.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "algorithm/RayIntersection.h"
|
||||
|
||||
hit_result_t ray_intersect(const triangle_t triangle, const ray_t ray)
|
||||
{
|
||||
hit_result_t result = {0};
|
||||
|
||||
vec3s normal_face = triangle.normal_face;
|
||||
float n_dot_r = glms_vec3_dot(normal_face, ray.direction);
|
||||
if (n_dot_r > 0.0f)
|
||||
{
|
||||
normal_face = glms_vec3_negate(normal_face);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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.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 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 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
result.hit = true;
|
||||
result.point = intersection_point;
|
||||
result.normal = normal_interp;
|
||||
result.distance = distance;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
hit_result_t ray_intersect_closest(const triangle_collection_t* triangles, const ray_t ray)
|
||||
{
|
||||
hit_result_t closest_hit = {0};
|
||||
closest_hit.distance = FLT_MAX;
|
||||
|
||||
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;
|
||||
closest_hit.triangle_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
return closest_hit;
|
||||
}
|
||||
|
||||
hit_result_t ray_intersect_any(const triangle_collection_t* triangles, const ray_t ray)
|
||||
{
|
||||
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.triangle_id = i;
|
||||
return hit_result;
|
||||
}
|
||||
}
|
||||
|
||||
return (hit_result_t){0};
|
||||
}
|
||||
89
source/Algorithm/Sobol.c
Normal file
89
source/Algorithm/Sobol.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "Algorithm/Sobol.h"
|
||||
|
||||
//NOTE: Maybe precompute the vector table?
|
||||
void sobol_init()
|
||||
{
|
||||
// First, set dimension 0 manually (Van der Corput)
|
||||
for (int i = 0; i < SOBOL_BITS; i++)
|
||||
{
|
||||
sobol_direction_vectors[0][i] = 1U << (31 - i);
|
||||
}
|
||||
|
||||
// Dimensions 2 to 6
|
||||
for (int d = 1; d < SOBOL_DIMENSIONS; d++)
|
||||
{
|
||||
int s = s_vals[d - 1];
|
||||
int a = a_vals[d - 1];
|
||||
const int* m = m_vals[d - 1];
|
||||
|
||||
// Set initial direction numbers
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
sobol_direction_vectors[d][i] = (uint32_t)m[i] << (31 - i);
|
||||
}
|
||||
|
||||
// Compute remaining direction numbers
|
||||
for (int i = s; i < SOBOL_BITS; i++)
|
||||
{
|
||||
uint32_t v = sobol_direction_vectors[d][i - s];
|
||||
v ^= v >> s;
|
||||
|
||||
for (int k = 1; k < s; k++)
|
||||
{
|
||||
if ((a >> (s - 1 - k)) & 1)
|
||||
v ^= sobol_direction_vectors[d][i - k];
|
||||
}
|
||||
|
||||
sobol_direction_vectors[d][i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t sobol_get_bit(uint32_t index, uint32_t bit)
|
||||
{
|
||||
return (index >> bit) & 1;
|
||||
}
|
||||
|
||||
static inline float sobol_uint_to_float(uint32_t x)
|
||||
{
|
||||
return x * 2.3283064365386963e-10f; // 1/2^32
|
||||
}
|
||||
|
||||
float sobol_sample(uint32_t index, uint32_t dimension)
|
||||
{
|
||||
if (dimension >= SOBOL_DIMENSIONS)
|
||||
{
|
||||
return 0.0f; // Invalid dimension
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < SOBOL_BITS; i++)
|
||||
{
|
||||
if (sobol_get_bit(index, i))
|
||||
{
|
||||
result ^= sobol_direction_vectors[dimension][i];
|
||||
}
|
||||
}
|
||||
|
||||
return sobol_uint_to_float(result);
|
||||
}
|
||||
|
||||
float sobol_next(sobol_state_t* state)
|
||||
{
|
||||
uint32_t index = state->index;
|
||||
uint32_t dimension = state->dimension;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < SOBOL_BITS; i++)
|
||||
{
|
||||
if (sobol_get_bit(index, i))
|
||||
{
|
||||
result ^= sobol_direction_vectors[dimension][i];
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the index for the next call
|
||||
state->dimension = (state->dimension + 1) % SOBOL_DIMENSIONS;
|
||||
|
||||
return sobol_uint_to_float(result);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
85
source/Geometry/Mesh.c
Normal file
85
source/Geometry/Mesh.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "Geometry/Mesh.h"
|
||||
#include "Geometry/Triangle.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 entity = {0};
|
||||
|
||||
const C_STRUCT aiScene* scene = NULL;
|
||||
scene = aiImportFile(filename,aiProcessPreset_TargetRealtime_MaxQuality);
|
||||
if (scene == NULL)
|
||||
{
|
||||
// fprintf(stderr, "Error loading mesh: %s\n", aiGetErrorString());
|
||||
return entity;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < scene->mNumMeshes; i++)
|
||||
{
|
||||
const struct aiMesh* mesh = scene->mMeshes[i];
|
||||
|
||||
//TODO: Handle all mesh types, not just triangles
|
||||
if (mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
|
||||
{
|
||||
// fprintf(stderr, "Mesh %llu is not a triangle mesh\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < mesh->mNumFaces; j++)
|
||||
{
|
||||
const 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;
|
||||
}
|
||||
|
||||
vec3s point1 = {
|
||||
mesh->mVertices[face->mIndices[0]].x,
|
||||
mesh->mVertices[face->mIndices[0]].y,
|
||||
mesh->mVertices[face->mIndices[0]].z
|
||||
};
|
||||
vec3s point2 = {
|
||||
mesh->mVertices[face->mIndices[1]].x,
|
||||
mesh->mVertices[face->mIndices[1]].y,
|
||||
mesh->mVertices[face->mIndices[1]].z
|
||||
};
|
||||
vec3s point3 = {
|
||||
mesh->mVertices[face->mIndices[2]].x,
|
||||
mesh->mVertices[face->mIndices[2]].y,
|
||||
mesh->mVertices[face->mIndices[2]].z
|
||||
};
|
||||
|
||||
vec3s normal1 = {
|
||||
mesh->mNormals[face->mIndices[0]].x,
|
||||
mesh->mNormals[face->mIndices[0]].y,
|
||||
mesh->mNormals[face->mIndices[0]].z
|
||||
};
|
||||
vec3s normal2 = {
|
||||
mesh->mNormals[face->mIndices[1]].x,
|
||||
mesh->mNormals[face->mIndices[1]].y,
|
||||
mesh->mNormals[face->mIndices[1]].z
|
||||
};
|
||||
vec3s normal3 = {
|
||||
mesh->mNormals[face->mIndices[2]].x,
|
||||
mesh->mNormals[face->mIndices[2]].y,
|
||||
mesh->mNormals[face->mIndices[2]].z
|
||||
};
|
||||
|
||||
//TODO: Handle materials, we use OpenPBR standard for parameter naming
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Triangle.h"
|
||||
#include "Geometry/Triangle.h"
|
||||
|
||||
triangle_collection_t triangle_collection_create(size_t size)
|
||||
{
|
||||
@@ -36,26 +36,38 @@ void triangle_collection_free(triangle_collection_t* collection)
|
||||
collection->buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void triangle_create(vec3s point1, vec3s point2, vec3s point3, uint8_t material_id, 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)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
new_triangle.normal = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
|
||||
triangle.normal_face = glms_vec3_normalize(glms_vec3_cross(edge1, edge2));
|
||||
|
||||
collection->buffer[collection->count] = new_triangle;
|
||||
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);
|
||||
}
|
||||
|
||||
40
source/Lighting/LightEvaluation.c
Normal file
40
source/Lighting/LightEvaluation.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "Lighting/LightEvaluation.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
#include "Algorithm/Sobol.h"
|
||||
|
||||
vec3s evaluate_bsdf_directional(const directional_light_t light, const light_shading_context_t* context, sobol_state_t* sobol_state)
|
||||
{
|
||||
float angular_radius = glm_rad(light.angular_diameter / 2.0f);
|
||||
vec3s wi = random_cosine_direction_angular(light.direction, angular_radius, sobol_state);
|
||||
|
||||
ray_t shadow_ray = {
|
||||
.origin = glms_vec3_add(context->hit_point, glms_vec3_scale(context->normal, 0.001f)),
|
||||
.direction = wi,
|
||||
};
|
||||
|
||||
hit_result_t shadow_hit = ray_intersect_any(context->triangles, shadow_ray);
|
||||
if (shadow_hit.hit)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
}
|
||||
|
||||
float n_dot_l = glms_vec3_dot(context->normal, light.direction);
|
||||
if (n_dot_l <= 0.0f)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
}
|
||||
|
||||
shading_context_t shading_context = {
|
||||
.normal = context->normal,
|
||||
.wi = wi,
|
||||
.wo = glms_vec3_negate(context->wo)
|
||||
};
|
||||
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
|
||||
|
||||
vec3s light_irradiance = glms_vec3_scale(light.color, light.intensity);
|
||||
float cos_theta = fmaxf(n_dot_l, 0.0f);
|
||||
vec3s irradiance_contrib = glms_vec3_scale(light_irradiance, cos_theta);
|
||||
|
||||
vec3s light_contrib = glms_vec3_scale(glms_vec3_mul(context->throughput, bsdf), cos_theta); // glms_vec3_scale(glms_vec3_mul(throughput, bsdf), cos_theta / 1.0f); pdf for directional light is 1.0f, we can ignore it.
|
||||
return glms_vec3_mul(irradiance_contrib, light_contrib);
|
||||
}
|
||||
44
source/Lighting/SkyLight.c
Normal file
44
source/Lighting/SkyLight.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "Lighting/SkyLight.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
#include "Material/Material.h"
|
||||
|
||||
vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* context, sobol_state_t* sobol_state)
|
||||
{
|
||||
const constant_sky_data_t sky_data = *(const constant_sky_data_t*)data;
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
return sky_data.color; // No context, return the sky color directly.
|
||||
}
|
||||
|
||||
vec3s wi = random_cosine_direction(context->normal, sobol_state);
|
||||
float pdf = pdf_cosine_weighted_hemisphere(context->normal, wi);
|
||||
|
||||
ray_t shadow_ray = {
|
||||
.origin = glms_vec3_add(context->hit_point, glms_vec3_scale(context->normal, 0.001f)),
|
||||
.direction = wi,
|
||||
};
|
||||
|
||||
hit_result_t shadow_hit = ray_intersect_any(context->triangles, shadow_ray);
|
||||
if (shadow_hit.hit)
|
||||
{
|
||||
return glms_vec3_zero(); // Skip if the ray hits something
|
||||
}
|
||||
|
||||
shading_context_t shading_context = {
|
||||
.normal = context->normal,
|
||||
.wi = wi,
|
||||
.wo = glms_vec3_negate(context->wo)
|
||||
};
|
||||
|
||||
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
|
||||
bsdf = glms_vec3_mul(bsdf, sky_data.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 weight = power_heuristic(pdf, pdf_bsdf);
|
||||
|
||||
vec3s env_contrib = glms_vec3_scale(glms_vec3_mul(context->throughput, bsdf), cos_theta / pdf);
|
||||
return glms_vec3_scale(env_contrib, weight);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,26 +1,12 @@
|
||||
#include "Material/SimpleLit.h"
|
||||
#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)
|
||||
// Simple lit, but keep it unbiased as much as possible
|
||||
vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s wo, sobol_state_t* sobol_state, float* pdf_out)
|
||||
{
|
||||
const simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
||||
|
||||
@@ -39,26 +25,15 @@ vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s w
|
||||
}
|
||||
|
||||
// Normalize probabilities
|
||||
// total_prob should be 1.0f, worth it? Maybe still need to avoid floating point errors
|
||||
prob_diffuse /= total_prob;
|
||||
prob_specular /= total_prob;
|
||||
|
||||
vec3s wi = glms_vec3_zero();
|
||||
float pdf_lobe = 0.0f;
|
||||
vec3s wi;
|
||||
|
||||
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;
|
||||
wi = random_cosine_direction(normal, sobol_state);
|
||||
}
|
||||
else // Specular Lobe
|
||||
{
|
||||
@@ -67,8 +42,8 @@ vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s w
|
||||
// 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();
|
||||
float theta = acosf(powf(sobol_next(sobol_state), 1.0f / (specular_exponent + 1.0f)));
|
||||
float phi = 2.0f * (float)M_PI * sobol_next(sobol_state);
|
||||
vec3s h_ts = (vec3s){
|
||||
sinf(theta) * cosf(phi),
|
||||
sinf(theta) * sinf(phi),
|
||||
@@ -87,27 +62,10 @@ vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s w
|
||||
vec3s h_ws;
|
||||
h_ws = glms_vec3_add(scaled_u, scaled_v);
|
||||
h_ws = glms_vec3_add(h_ws, scaled_n);
|
||||
h_ws = glms_vec3_normalize(h_ws); // Normalize the half-vector
|
||||
|
||||
// wi is simple now, just reflect wo around h
|
||||
// wi is simple now, just reflect wo around normal
|
||||
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
|
||||
@@ -117,21 +75,64 @@ vec3s sample_bsdf_simple_lit(const void* data, const vec3s normal, const vec3s w
|
||||
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);
|
||||
*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, const vec3s normal, const vec3s wo, const vec3s wi)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const simple_lit_data_t shading_data = *(const simple_lit_data_t*)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 F = glms_vec3_max(fresnel_schlick_vec3(f0, cos_theta_o));
|
||||
|
||||
float prob_specular = glm_lerp(F, 1.0f, shading_data.metallic);
|
||||
float prob_diffuse = (1.0f - shading_data.metallic) * (1.0f - F);
|
||||
float total_prob = prob_diffuse + prob_specular;
|
||||
|
||||
if (total_prob < FLT_EPSILON)
|
||||
{
|
||||
return 0.0f; // No probability of scattering
|
||||
}
|
||||
prob_diffuse /= total_prob;
|
||||
prob_specular /= total_prob;
|
||||
|
||||
float pdf_diff = pdf_cosine_weighted_hemisphere(normal, wi);
|
||||
float diffuse_pdf_component = prob_diffuse * pdf_diff;
|
||||
|
||||
float pdf_spec = pdf_blinn_phong_lobe(normal, wo, 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)
|
||||
{
|
||||
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_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 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);
|
||||
vec3s diffuse_term = glms_vec3_scale(diffuse_color, (float)M_1_PI);
|
||||
|
||||
float specular_exponent = roughness_to_blinn_phong_specular_exponent(shading_data.roughness);
|
||||
// Normalization factor D (Blinn-Phong distribution)
|
||||
@@ -139,9 +140,13 @@ vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* dat
|
||||
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);
|
||||
float denominator = 4.0f * n_dot_l * n_dot_v;
|
||||
if (denominator < FLT_EPSILON)
|
||||
{
|
||||
return diffuse_term;
|
||||
}
|
||||
|
||||
vec3s specular_term = glms_vec3_scale(F, D / denominator); // Specular term (Blinn-Phong), we assume that G = 1.0f for simplicity
|
||||
|
||||
// return diffuse_term;
|
||||
return glms_vec3_add(diffuse_term, specular_term);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Rendering/RenderTarget.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
render_target_t render_target_create(uint32_t width, uint32_t height)
|
||||
{
|
||||
@@ -8,9 +9,17 @@ render_target_t render_target_create(uint32_t width, uint32_t height)
|
||||
target.width = width;
|
||||
target.height = height;
|
||||
|
||||
size_t buffer_size = (size_t)width * height * sizeof(vec4s);
|
||||
size_t size_of_pixel = sizeof(vec4s);
|
||||
size_t buffer_size = (size_t)width * height * size_of_pixel;
|
||||
target.buffer = (vec4s*)malloc(buffer_size);
|
||||
|
||||
memset(target.buffer, 0, buffer_size);
|
||||
|
||||
for (size_t i = 0; i < buffer_size / size_of_pixel; i++)
|
||||
{
|
||||
target.buffer[i].w = 1.0;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -34,6 +43,28 @@ void render_target_set_pixel(render_target_t* render_target, uint32_t x, uint32_
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* render_target_to_char(render_target_t* render_target)
|
||||
{
|
||||
size_t buffer_size = (size_t)render_target->width * render_target->height * 4; // 4 bytes for RGBA
|
||||
unsigned char* char_buffer = (unsigned char*)malloc(buffer_size);
|
||||
|
||||
for (uint32_t y = 0; y < render_target->height; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < render_target->width; x++)
|
||||
{
|
||||
vec4s pixel = render_target_get_pixel(render_target, x, y);
|
||||
size_t index = ((size_t)y * render_target->width + x) * 4;
|
||||
|
||||
char_buffer[index + 0] = COLOR_CLAMP(pixel.x * 255.0f);
|
||||
char_buffer[index + 1] = COLOR_CLAMP(pixel.y * 255.0f);
|
||||
char_buffer[index + 2] = COLOR_CLAMP(pixel.z * 255.0f);
|
||||
char_buffer[index + 3] = COLOR_CLAMP(pixel.w * 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return char_buffer;
|
||||
}
|
||||
|
||||
void render_target_free(render_target_t* target)
|
||||
{
|
||||
if (target->buffer != NULL)
|
||||
|
||||
@@ -3,122 +3,187 @@
|
||||
#include "Rendering/Scene.h"
|
||||
#include "Algorithm/PathTracing.h"
|
||||
|
||||
scene_t scene_create(uint64_t triangle_count, uint8_t material_count)
|
||||
scene_t scene_create(const uint64_t triangle_count, const uint8_t material_count, const uint32_t punctual_light_count)
|
||||
{
|
||||
scene_t scene = {0};
|
||||
|
||||
scene.triangles = triangle_collection_create(triangle_count);
|
||||
scene.materials = material_collection_create(material_count);
|
||||
scene.lights = light_collection_create(punctual_light_count, 16); // NOTE: We just fixed the max directional light count to 16.
|
||||
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
|
||||
16.0f / 9.0f
|
||||
);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
static inline vec2s compute_ndc(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
|
||||
static inline void ensure_camera_aspect_ratio(camera_t* camera, const rendering_config_t config)
|
||||
{
|
||||
if (fabsf((float)config.width / config.height - camera->aspect_ratio) > FLT_EPSILON)
|
||||
{
|
||||
*camera = camera_create(
|
||||
camera->position,
|
||||
camera->forward,
|
||||
camera->up,
|
||||
camera->focal_length,
|
||||
camera->size_x,
|
||||
(float)config.width / (float)config.height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static inline vec2s compute_ndc(float x, float y, uint32_t width, uint32_t height)
|
||||
{
|
||||
return (vec2s){
|
||||
.x = (float)x / (float)width,
|
||||
.x = x / (float)width,
|
||||
#ifdef FLIP_Y
|
||||
.y = 1.0f - (float)y / (float)height
|
||||
.y = 1.0f - y / (float)height
|
||||
#else
|
||||
.y = (float)y / (float)height
|
||||
.y = y / (float)height
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
render_target_t scene_render(scene_t* scene, rendering_config_t config)
|
||||
static void screne_render_pixel(scene_t* scene, const rendering_config_t config, const vec3s coord, const uint32_t x, const uint32_t y, vec4s* pixel_color)
|
||||
{
|
||||
if (fabsf((float)config.width / config.height - scene->camera.aspect_ratio) > FLT_EPSILON)
|
||||
vec4s accumulated_color = glms_vec4_zero();
|
||||
*pixel_color = accumulated_color;
|
||||
uint32_t pixel_id = y * config.width + x;
|
||||
|
||||
for (uint16_t k = 0; k < config.sample_count; k++)
|
||||
{
|
||||
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
|
||||
);
|
||||
vec2s position_ndc = compute_ndc((float)x, (float)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))
|
||||
};
|
||||
|
||||
// TODO: Hash it
|
||||
uint32_t sobol_idx = pixel_id * config.sample_count + (k + 1);
|
||||
vec3s out_color = path_trace(scene, ray, sobol_idx, config.max_depth);
|
||||
|
||||
// TODO: Handle alpha
|
||||
vec4s color = {.x = out_color.x, .y = out_color.y, .z = out_color.z, .w = 1.0f};
|
||||
accumulated_color = glms_vec4_add(accumulated_color, color);
|
||||
}
|
||||
|
||||
// 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);
|
||||
*pixel_color = glms_vec4_scale(accumulated_color, 1.0f / (float)config.sample_count);
|
||||
}
|
||||
|
||||
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;
|
||||
bool scene_render_tile(scene_t* scene, rendering_context_t* ctx, render_target_t* render_target,
|
||||
const rendering_config_t config, const uint32_t tile_index, tile_t* tile_out)
|
||||
{
|
||||
if (ctx->is_done)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ctx->is_init)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&scene->camera, config);
|
||||
ctx->tile_count_x = (config.width + config.bucket_size - 1) / config.bucket_size;
|
||||
ctx->tile_count_y = (config.height + config.bucket_size - 1) / config.bucket_size;
|
||||
ctx->coord = glms_vec3_add(scene->camera.position, glms_vec3_scale(scene->camera.forward, scene->camera.focal_length));
|
||||
ctx->is_init = true;
|
||||
}
|
||||
|
||||
if (tile_index >= ctx->tile_count_x * ctx->tile_count_y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t tile_x_0 = tile_index % ctx->tile_count_x * config.bucket_size;
|
||||
uint32_t tile_y_0 = tile_index / ctx->tile_count_x * config.bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + config.bucket_size, config.width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + config.bucket_size, config.height);
|
||||
|
||||
tile_out->x = tile_x_0;
|
||||
tile_out->y = tile_y_0;
|
||||
tile_out->width = tile_x_1 - tile_x_0;
|
||||
tile_out->height = tile_y_1 - tile_y_0;
|
||||
|
||||
int64_t x, y; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers. To avoid overflow, we need to use int64_t
|
||||
rendering_config_t config_copy = config; // Have to copy it, otherwise OpenMP will cause it become invalid. Not sure if this is a bug or not.
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_x_0, tile_x_1, tile_y_0, tile_y_1, config_copy) \
|
||||
private(x, y)
|
||||
for (y = tile_y_0; y < tile_y_1; y++)
|
||||
{
|
||||
for (x = tile_x_0; x < tile_x_1; x++)
|
||||
{
|
||||
vec4s pixel_color;
|
||||
screne_render_pixel(scene, config_copy, ctx->coord, (uint32_t)x, (uint32_t)y, &pixel_color);
|
||||
render_target_set_pixel(render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
||||
}
|
||||
}
|
||||
|
||||
if (tile_index == ctx->tile_count_x * ctx->tile_count_y - 1)
|
||||
{
|
||||
ctx->is_done = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
render_target_t scene_render(scene_t* scene, const rendering_config_t config)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&scene->camera, config);
|
||||
|
||||
// The actual float buffer inside the render target is on the heap, copy return shoudl be fine.
|
||||
render_target_t render_target = render_target_create(config.width, config.height);
|
||||
|
||||
uint32_t tile_count_x = (config.width + config.bucket_size - 1) / config.bucket_size;
|
||||
uint32_t tile_count_y = (config.height + config.bucket_size - 1) / config.bucket_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);
|
||||
float inv_sample = 1.0f / config.sample_count;
|
||||
vec3s coord = glms_vec3_add(scene->camera.position, glms_vec3_scale(scene->camera.forward, scene->camera.focal_length));
|
||||
|
||||
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
|
||||
int64_t x, y, tile_index; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers. To avoid overflow, we need to use int64_t
|
||||
rendering_config_t config_copy = config;
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_count_x, tile_count_y, tile_count, config_copy, coord, inv_sample, render_target) \
|
||||
private(x, y, tile_index)
|
||||
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));
|
||||
uint32_t tile_x_0 = (uint32_t)tile_index % tile_count_x * config.bucket_size;
|
||||
uint32_t tile_y_0 = (uint32_t)tile_index / tile_count_x * config.bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + config.bucket_size, config.width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + config.bucket_size, config.height);
|
||||
|
||||
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));
|
||||
}
|
||||
vec4s pixel_color;
|
||||
screne_render_pixel(scene, config_copy, coord, (uint32_t)x, (uint32_t)y, &pixel_color);
|
||||
render_target_set_pixel(&render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
return render_target;
|
||||
}
|
||||
|
||||
void scene_free(scene_t* scene)
|
||||
{
|
||||
triangle_collection_free(&scene->triangles);
|
||||
material_collection_free(&scene->materials);
|
||||
light_collection_free(&scene->lights);
|
||||
}
|
||||
|
||||
105
source/Window.c
Normal file
105
source/Window.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "Window.h"
|
||||
|
||||
static LRESULT CALLBACK wndow_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
HDC hdc = GetDC(hwnd);
|
||||
bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bitmap_info.bmiHeader.biWidth = window_width;
|
||||
bitmap_info.bmiHeader.biHeight = -window_height; // Negative to flip the image
|
||||
bitmap_info.bmiHeader.biPlanes = 1;
|
||||
bitmap_info.bmiHeader.biBitCount = 32; // 32 bits per pixel (RGBA)
|
||||
bitmap_info.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
bitmap = CreateDIBSection(hdc, &bitmap_info, DIB_RGB_COLORS,
|
||||
(void**)&pixel_buffer, NULL, 0);
|
||||
hdc_mem = CreateCompatibleDC(hdc);
|
||||
SelectObject(hdc_mem, bitmap);
|
||||
|
||||
ReleaseDC(hwnd, hdc);
|
||||
return 0;
|
||||
}
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hwnd, &ps);
|
||||
HDC hdc_mem = CreateCompatibleDC(hdc);
|
||||
HGDIOBJ old_bitmap = SelectObject(hdc_mem, bitmap);
|
||||
|
||||
BitBlt(hdc, 0, 0, window_width, window_height, hdc_mem, 0, 0, SRCCOPY);
|
||||
|
||||
SelectObject(hdc_mem, old_bitmap);
|
||||
DeleteDC(hdc_mem);
|
||||
EndPaint(hwnd, &ps);
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
{
|
||||
DeleteDC(hdc_mem);
|
||||
DeleteObject(bitmap);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
|
||||
int window_create(const char* title, HINSTANCE hInst, const int width, const int height)
|
||||
{
|
||||
RECT rect = {0, 0, width, height};
|
||||
AdjustWindowRect(&rect, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);
|
||||
|
||||
window_width = rect.right - rect.left;
|
||||
window_height = rect.bottom - rect.top;
|
||||
|
||||
WNDCLASS wc = {0};
|
||||
wc.lpfnWndProc = wndow_proc;
|
||||
wc.hInstance = hInst;
|
||||
wc.lpszClassName = title;
|
||||
wc.hCursor = LoadCursor(hInst, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
|
||||
RegisterClass(&wc);
|
||||
|
||||
int screen_width = GetSystemMetrics(SM_CXSCREEN);
|
||||
int screen_height = GetSystemMetrics(SM_CYSCREEN);
|
||||
int pos_x = (screen_width - window_width) / 2;
|
||||
int pos_y = (screen_height - window_height) / 2;
|
||||
|
||||
hwnd = CreateWindowEx(0, title, title, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
|
||||
pos_x, pos_y, window_width, window_height,
|
||||
NULL, NULL, wc.hInstance, NULL);
|
||||
|
||||
if (!hwnd)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ShowWindow(hwnd, SW_SHOW);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void window_update_pixels(const vec4s color, const int pixel_x, const int pixel_y)
|
||||
{
|
||||
int pixel_index = (pixel_y * window_width + pixel_x) * 4;
|
||||
|
||||
// NOTE: The pixel buffer is in BGRA format, so we need to swap the channels
|
||||
pixel_buffer[pixel_index] = COLOR_CLAMP(color.z * 255.0f);
|
||||
pixel_buffer[pixel_index + 1] = COLOR_CLAMP(color.y * 255.0f);
|
||||
pixel_buffer[pixel_index + 2] = COLOR_CLAMP(color.x * 255.0f);
|
||||
pixel_buffer[pixel_index + 3] = COLOR_CLAMP(color.w * 255.0f);
|
||||
}
|
||||
|
||||
void window_refresh_region(const int pixel_x, const int pixel_y, const int region_width, const int region_height)
|
||||
{
|
||||
HDC hdc = GetDC(hwnd);
|
||||
BitBlt(hdc, pixel_x, pixel_y, region_width, region_height, hdc_mem, pixel_x, pixel_y, SRCCOPY);
|
||||
|
||||
ReleaseDC(hwnd, hdc);
|
||||
}
|
||||
187
source/main.c
187
source/main.c
@@ -1,100 +1,119 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <svpng.inc>
|
||||
#include <omp.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "MaterialExtra.h"
|
||||
#include "Algorithm/Sobol.h"
|
||||
#include "Lighting/SkyLight.h"
|
||||
#include "Material/SimpleLit.h"
|
||||
#include "Rendering/Scene.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Material.h"
|
||||
#include "Triangle.h"
|
||||
#include "Geometry.h"
|
||||
#include "Geometry/GeometryUtilities.h"
|
||||
#include "Window.h"
|
||||
|
||||
|
||||
static inline void save_img(render_target_t* source, const uint32_t width, const uint32_t height, const char* filename)
|
||||
static 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)
|
||||
FILE* file_stream;
|
||||
fopen_s(&file_stream, filename, "wb");
|
||||
if (file_stream == 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;
|
||||
}
|
||||
unsigned char* img_buffer = render_target_to_char(source);
|
||||
svpng(file_stream, width, height, img_buffer, 1);
|
||||
|
||||
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);
|
||||
fclose(file_stream);
|
||||
free(img_buffer);
|
||||
}
|
||||
|
||||
int main()
|
||||
// int main()
|
||||
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR pCmdLine, _In_ int nCmdShow)
|
||||
{
|
||||
omp_set_num_threads(24);
|
||||
rendering_config_t config = {
|
||||
.width = 1280 / 2,
|
||||
.height = 720 / 2,
|
||||
.sample_count = 64,
|
||||
.max_depth = 4,
|
||||
.bucket_size = 64,
|
||||
};
|
||||
|
||||
const int SAMPLE_COUNT = 256;
|
||||
int result = window_create("Path Tracing", hInstance, config.width, config.height);
|
||||
if (result != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to create window\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint32_t WIDTH = 640;
|
||||
const uint32_t HEIGHT = 360;
|
||||
#pragma region SceneSetup
|
||||
omp_set_num_threads(16);
|
||||
sobol_init();
|
||||
|
||||
scene_t scene = scene_create(64, 4);
|
||||
scene_t scene = scene_create(64, 8, 4);
|
||||
|
||||
scene.camera.position = (vec3s){0.0f, 0.0f, 5.0f};
|
||||
|
||||
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.75f, 0.75f, 1.0f});
|
||||
sun_light->color = (vec3s){1.0f, 0.93f, 0.87f};
|
||||
sun_light->intensity = 3.0f;
|
||||
sun_light->angular_diameter = 0.53f;
|
||||
|
||||
scene.lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t){
|
||||
.color = (vec3s){0.73f, 0.82f, 1.0f}
|
||||
});
|
||||
|
||||
simple_lit_data_t gray_lit_data = {
|
||||
.albedo = (vec3s){0.73f, 0.73f, 0.73f},
|
||||
.roughness = 1.0f,
|
||||
.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 = 1.0f,
|
||||
.metallic = 0.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);
|
||||
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.75f,
|
||||
.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 floor_material = material_create_simple_lit(&floor_lit_data, &scene.materials);
|
||||
|
||||
scene.materials.buffer[top_light_material.id].emission = (vec3s){4.0f, 4.0f, 4.0f};
|
||||
// scene.materials.buffer[gray_light_material.id].emission = (vec3s){10.0f, 10.0f, 10.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
|
||||
1.0f, gray_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
|
||||
);
|
||||
// sphere_create(
|
||||
// (vec3s){0.0f, 0.0f, 0.0f},
|
||||
// 1.0f, gray_light_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
|
||||
4.0f, floor_material.id, &scene.triangles
|
||||
);
|
||||
quad_create(
|
||||
(vec3s){0.0f, 2.0f, 0.0f},
|
||||
@@ -106,33 +125,63 @@ int main()
|
||||
(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
|
||||
4.0f, green_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
|
||||
4.0f, blue_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
|
||||
4.0f, red_material.id, &scene.triangles
|
||||
);
|
||||
|
||||
rendering_config_t config = {
|
||||
.width = WIDTH,
|
||||
.height = HEIGHT,
|
||||
.sample_count = SAMPLE_COUNT,
|
||||
.max_depth = 4,
|
||||
.tile_size = 16,
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
render_target_t img = scene_render(&scene, config);
|
||||
render_target_t img = render_target_create(config.width, config.height);
|
||||
// render_target_t img = scene_render(&scene, config);
|
||||
// save_img(&img, config.width, config.height, "output.png");
|
||||
|
||||
save_img(&img, WIDTH, HEIGHT, "output.png");
|
||||
MSG msg;
|
||||
uint16_t tile_index = 0;
|
||||
tile_t current_tile = {0};
|
||||
rendering_context_t ctx = {0};
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (msg.message == WM_QUIT)
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
if (scene_render_tile(&scene, &ctx, &img, config, tile_index, ¤t_tile))
|
||||
{
|
||||
for (uint32_t y = current_tile.y; y < current_tile.y + current_tile.height; y++)
|
||||
{
|
||||
for (uint32_t x = current_tile.x; x < current_tile.x + current_tile.width; x++)
|
||||
{
|
||||
vec4s pixel = render_target_get_pixel(&img, x, y);
|
||||
// pixel = aces_tone_map(pixel);
|
||||
window_update_pixels(pixel, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
window_refresh_region(current_tile.x, current_tile.y, current_tile.width, current_tile.height);
|
||||
tile_index++;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
render_target_free(&img);
|
||||
scene_free(&scene);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user