Update rendering, material handling, and shading logic
Changed README.md to update rendering settings and build instructions. Changed BSDF.h to add functions for normal unpacking and tangent transformation. Changed RayIntersection.h to include tangent vector in hit_result_t. Changed Common.h to include vec2.h for 2D vector handling. Changed String.h to add string_copy function and improve is_absolute_path. Changed GeometryUtilities.h to enhance quad creation with tangent calculations. Changed Mesh.h to include tangents in the vertex structure. Changed Triangle.h to add tangents in the vertex structure for better normal mapping. Changed Light.h to include tangents in the light shading context. Changed SkyLight.h to introduce a new structure for sky lights. Changed Material.h to include tangents in the shading context. Changed SimpleLit.h to add normal and tangent textures for detailed shading. Changed Texture.h to introduce a new structure for texture assets. Changed BSDF.c to add functions for unpacking normals and transforming tangents. Changed PathTracing.c to include tangents in the shading context. Changed RayIntersection.c to calculate normals and tangents in ray-triangle intersections. Changed Mesh.c to improve material texture loading and handle tangents. Changed Material.c to enhance material collection initialization and resizing. Changed SimpleLit.c to incorporate normal mapping with normal textures. Changed Texture.c to improve management of texture assets and resources.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#include "ALgorithm/BSDF.h"
|
||||
#include "cglm/util.h"
|
||||
#include "cglm/struct/mat3.h"
|
||||
|
||||
float power_heuristic(float pdf_a, float pdf_b)
|
||||
{
|
||||
@@ -24,6 +26,33 @@ vec3s fresnel_schlick_vec3(vec3s f0, float cos_theta)
|
||||
return glms_vec3_adds(glms_vec3_scale(f0, (1.0f - x5)), x5);
|
||||
}
|
||||
|
||||
vec3s normal_unpack(vec3s normal)
|
||||
{
|
||||
vec3s unpacked_normal = glms_vec3_scale(normal, 2.0f);
|
||||
unpacked_normal = glms_vec3_sub(unpacked_normal, glms_vec3_one());
|
||||
|
||||
float dot_xy = glm_clamp_zo(unpacked_normal.x * unpacked_normal.x + unpacked_normal.y * unpacked_normal.y);
|
||||
unpacked_normal.z = fmaxf(FLT_MIN, sqrtf(1.0f - dot_xy));
|
||||
|
||||
return unpacked_normal;
|
||||
}
|
||||
|
||||
vec3s normal_ts_to_ws(vec3s normal, vec3s tangent)
|
||||
{
|
||||
vec3s t = glms_vec3_normalize(tangent);
|
||||
vec3s b = glms_vec3_cross(normal, t);
|
||||
|
||||
// Matrix in cglm is column-major, not row-major
|
||||
mat3s tbn =
|
||||
{
|
||||
t.x, b.x, normal.x,
|
||||
t.y, b.y, normal.y,
|
||||
t.z, b.z, normal.z
|
||||
};
|
||||
|
||||
return glms_vec3_normalize(glms_mat3_mulv(tbn, normal));
|
||||
}
|
||||
|
||||
// BSDF sampling functions
|
||||
|
||||
float pdf_cosine_weighted_hemisphere(vec3s normal, vec3s wi)
|
||||
|
||||
@@ -39,10 +39,11 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(glms_vec3_mul(throughput, emission), 0.0f));
|
||||
|
||||
light_shading_context_t light_context = {
|
||||
.normal = closest_hit.normal,
|
||||
.hit_point = closest_hit.point,
|
||||
.wo = active_ray.direction,
|
||||
.normal = closest_hit.normal,
|
||||
.tangent = closest_hit.tangent,
|
||||
.uv = closest_hit.uv,
|
||||
.wo = active_ray.direction,
|
||||
|
||||
.bounce_depth = depth,
|
||||
|
||||
@@ -64,10 +65,11 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
|
||||
// Bounce and prepare for the next iteration
|
||||
shading_context_t shading_context = {
|
||||
.normal = closest_hit.normal,
|
||||
.position = closest_hit.point,
|
||||
.wo = glms_vec3_negate(active_ray.direction),
|
||||
.normal = closest_hit.normal,
|
||||
.tangent = closest_hit.tangent,
|
||||
.uv = closest_hit.uv,
|
||||
.wo = glms_vec3_negate(active_ray.direction),
|
||||
|
||||
.textures = &scene->textures,
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ ray_t ray_create(vec3s origin, vec3s direction)
|
||||
((direction.x < 0.0f) ? 1 : 0) |
|
||||
((direction.y < 0.0f) ? 2 : 0) |
|
||||
((direction.z < 0.0f) ? 4 : 0)
|
||||
,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -168,12 +167,12 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
||||
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);
|
||||
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);
|
||||
vec3s P = glms_vec3_cross(direction, e2);
|
||||
float det = glms_vec3_dot(e1, P);
|
||||
|
||||
if (fabsf(det) < FLT_EPSILON)
|
||||
{
|
||||
@@ -191,7 +190,7 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
||||
}
|
||||
|
||||
// Calculate barycentric v
|
||||
vec3s Q = glms_vec3_cross(T, E1);
|
||||
vec3s Q = glms_vec3_cross(T, e1);
|
||||
float v = glms_vec3_dot(direction, Q) * invDet;
|
||||
if (v < 0.0f || u + v > 1.0f)
|
||||
{
|
||||
@@ -199,22 +198,34 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
||||
}
|
||||
|
||||
// Distance along the ray
|
||||
float t = glms_vec3_dot(E2, Q) * invDet;
|
||||
float t = glms_vec3_dot(e2, Q) * invDet;
|
||||
if (t < RAY_EPSILON)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
float w = 1.0f - u - v;
|
||||
|
||||
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);
|
||||
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));
|
||||
if (glms_vec3_dot(normal, direction) > 0.0f)
|
||||
{
|
||||
normal = glms_vec3_negate(normal);
|
||||
}
|
||||
|
||||
result.normal = glms_vec3_normalize(normal);
|
||||
|
||||
vec3s tangent = glms_vec3_scale(triangle->vertices[0].tangent, w);
|
||||
tangent = glms_vec3_add(tangent, glms_vec3_scale(triangle->vertices[1].tangent, u));
|
||||
tangent = glms_vec3_add(tangent, glms_vec3_scale(triangle->vertices[2].tangent, v));
|
||||
|
||||
result.tangent = glms_vec3_normalize(tangent);
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
#include "Geometry/Mesh.h"
|
||||
#include "ALgorithm/BSDF.h"
|
||||
#include "Common/String.h"
|
||||
#include "Material/SimpleLit.h"
|
||||
#include "assimp/cimport.h"
|
||||
#include "assimp/scene.h"
|
||||
#include "assimp/postprocess.h"
|
||||
|
||||
static texture_entity_t load_material_texture(const struct aiMaterial* material, enum aiTextureType type, const char* filename, scene_t* scene)
|
||||
{
|
||||
struct aiString path;
|
||||
if (AI_SUCCESS == aiGetMaterialTexture(material, type, 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);
|
||||
}
|
||||
|
||||
return texture_load(path.data, true, &scene->textures);
|
||||
}
|
||||
return invalid_texture_entity();
|
||||
}
|
||||
|
||||
mesh_entity_t mesh_load(const char* filename, scene_t* scene)
|
||||
{
|
||||
mesh_entity_t entity = {0};
|
||||
@@ -24,43 +42,30 @@ mesh_entity_t mesh_load(const char* filename, scene_t* scene)
|
||||
{
|
||||
const struct aiMaterial* src = mesh_scene->mMaterials[i];
|
||||
|
||||
struct aiColor4D base_color;
|
||||
struct aiColor4D base_color = {0.73f};
|
||||
aiGetMaterialColor(src, AI_MATKEY_COLOR_DIFFUSE, &base_color);
|
||||
|
||||
float roughness = 0.5f;
|
||||
aiGetMaterialFloat(src, AI_MATKEY_SHININESS, &roughness);
|
||||
roughness = blinn_phong_specular_exponent_to_roughness(roughness);
|
||||
float roughness = 0.75f;
|
||||
aiGetMaterialFloat(src, AI_MATKEY_ROUGHNESS_FACTOR, &roughness);
|
||||
|
||||
texture_entity_t albedo_entity = invalid_texture_entity();
|
||||
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);
|
||||
}
|
||||
float metallic = 0.0f;
|
||||
aiGetMaterialFloat(src, AI_MATKEY_METALLIC_FACTOR, &metallic);
|
||||
|
||||
texture_entity_t entity = texture_load(path.data, true, &scene->textures);
|
||||
if (entity.id != INVALID_TEXTURE_ID)
|
||||
{
|
||||
albedo_entity = entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
texture_entity_t albedo_entity = load_material_texture(src, aiTextureType_DIFFUSE, filename, scene);
|
||||
texture_entity_t normal_entity = load_material_texture(src, aiTextureType_NORMALS, filename, scene);
|
||||
texture_entity_t roughness_entity = load_material_texture(src, aiTextureType_DIFFUSE_ROUGHNESS, filename, scene);
|
||||
texture_entity_t metallic_entity = load_material_texture(src, aiTextureType_METALNESS, filename, scene);
|
||||
|
||||
simple_lit_properties_t prop =
|
||||
{
|
||||
.albedo = {base_color.r, base_color.g, base_color.b},
|
||||
.roughness = roughness,
|
||||
.metallic = 0.0f,
|
||||
.metallic = metallic,
|
||||
|
||||
.albedo_texture = albedo_entity,
|
||||
.normal_texture = normal_entity,
|
||||
.roughness_texture = roughness_entity,
|
||||
.metallic_texture = metallic_entity,
|
||||
};
|
||||
|
||||
material_create_simple_lit_default(&prop, &scene->materials);
|
||||
@@ -87,61 +92,23 @@ mesh_entity_t mesh_load(const char* filename, scene_t* scene)
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t index1 = face->mIndices[0];
|
||||
uint32_t index2 = face->mIndices[1];
|
||||
uint32_t index3 = face->mIndices[2];
|
||||
uint32_t index0 = face->mIndices[0];
|
||||
uint32_t index1 = face->mIndices[1];
|
||||
uint32_t index2 = face->mIndices[2];
|
||||
|
||||
vec3s point1 = {
|
||||
mesh->mVertices[index1].x,
|
||||
mesh->mVertices[index1].y,
|
||||
mesh->mVertices[index1].z
|
||||
};
|
||||
vec3s point2 = {
|
||||
mesh->mVertices[index2].x,
|
||||
mesh->mVertices[index2].y,
|
||||
mesh->mVertices[index2].z
|
||||
};
|
||||
vec3s point3 = {
|
||||
mesh->mVertices[index3].x,
|
||||
mesh->mVertices[index3].y,
|
||||
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)
|
||||
vertex_t vertices[3] = {0};
|
||||
for (uint32_t k = 0; k < 3; k++)
|
||||
{
|
||||
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;
|
||||
vertices[k].position = (vec3s){mesh->mVertices[face->mIndices[k]].x, mesh->mVertices[face->mIndices[k]].y, mesh->mVertices[face->mIndices[k]].z};
|
||||
vertices[k].normal = (vec3s){mesh->mNormals[face->mIndices[k]].x, mesh->mNormals[face->mIndices[k]].y, mesh->mNormals[face->mIndices[k]].z};
|
||||
vertices[k].tangent = (vec3s){mesh->mTangents[face->mIndices[k]].x, mesh->mTangents[face->mIndices[k]].y, mesh->mTangents[face->mIndices[k]].z};
|
||||
if (has_uv)
|
||||
{
|
||||
vertices[k].uv = (vec2s){mesh->mTextureCoords[0][face->mIndices[k]].x, mesh->mTextureCoords[0][face->mIndices[k]].y};
|
||||
}
|
||||
}
|
||||
|
||||
vec3s normal1 = {
|
||||
mesh->mNormals[index1].x,
|
||||
mesh->mNormals[index1].y,
|
||||
mesh->mNormals[index1].z
|
||||
};
|
||||
vec3s normal2 = {
|
||||
mesh->mNormals[index2].x,
|
||||
mesh->mNormals[index2].y,
|
||||
mesh->mNormals[index2].z
|
||||
};
|
||||
vec3s normal3 = {
|
||||
mesh->mNormals[index3].x,
|
||||
mesh->mNormals[index3].y,
|
||||
mesh->mNormals[index3].z
|
||||
};
|
||||
|
||||
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};
|
||||
|
||||
triangle_create(vertex1, vertex2, vertex3, entity.material_id + mesh->mMaterialIndex, &scene->triangles);
|
||||
triangle_create(vertices[0], vertices[1], vertices[2], entity.material_id + mesh->mMaterialIndex, &scene->triangles);
|
||||
entity.triangle_count++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "Material/Material.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
bool material_collection_init(uint8_t size, material_collection_t* materials)
|
||||
{
|
||||
if (size > 254)
|
||||
@@ -26,9 +24,14 @@ bool material_collection_init(uint8_t size, material_collection_t* materials)
|
||||
|
||||
void material_collection_resize(material_collection_t* materials, size_t size)
|
||||
{
|
||||
if (size > 254)
|
||||
if (size == INVALID_MATERIAL_ID)
|
||||
{
|
||||
size = 254; // Limit the count to 254 to fit in a uint8_t
|
||||
size = INVALID_MATERIAL_ID - 1;
|
||||
}
|
||||
|
||||
if (size == materials->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
material_t* temp = (material_t*)realloc(materials->buffer, size * sizeof(material_t));
|
||||
|
||||
@@ -11,16 +11,27 @@ void simple_lit_data_default(const shading_context_t* context, const void* prope
|
||||
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;
|
||||
|
||||
data->albedo = prop->albedo;
|
||||
const texture_t* albedo_texture = get_texture(context->textures, prop->albedo_texture);
|
||||
if (albedo_texture != NULL && albedo_texture->data != NULL)
|
||||
{
|
||||
data->albedo = glms_vec3_mul(data->albedo, glms_vec3(texture_sample(albedo_texture, context->uv.x, context->uv.y)));
|
||||
}
|
||||
|
||||
data->roughness = prop->roughness;
|
||||
const texture_t* normal_texture = get_texture(context->textures, prop->normal_texture);
|
||||
if (normal_texture != NULL && normal_texture->data != NULL)
|
||||
{
|
||||
vec3s normal_sample = glms_vec3(texture_sample(normal_texture, context->uv.x, context->uv.y));
|
||||
normal_sample = normal_unpack(normal_sample);
|
||||
data->normal = normal_ts_to_ws(normal_sample, context->tangent);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->normal = context->normal;
|
||||
}
|
||||
|
||||
data->roughness = prop->roughness;
|
||||
const texture_t* roughness_texture = get_texture(context->textures, prop->roughness_texture);
|
||||
if (roughness_texture != NULL && roughness_texture->data != NULL)
|
||||
{
|
||||
@@ -28,7 +39,6 @@ void simple_lit_data_default(const shading_context_t* context, const void* prope
|
||||
}
|
||||
|
||||
data->metallic = prop->metallic;
|
||||
|
||||
const texture_t* metallic_texture = get_texture(context->textures, prop->metallic_texture);
|
||||
if (metallic_texture != NULL && metallic_texture->data != NULL)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Rendering/Texture.h"
|
||||
#include "Common/String.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -8,7 +9,7 @@
|
||||
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));
|
||||
temp.buffer = (texture_asset_t*)malloc(size * sizeof(texture_asset_t));
|
||||
if (temp.buffer == NULL)
|
||||
{
|
||||
return false;
|
||||
@@ -23,7 +24,17 @@ bool texture_collection_init(uint16_t size, texture_collection_t* textures)
|
||||
|
||||
void texture_collection_resize(texture_collection_t* textures, uint16_t size)
|
||||
{
|
||||
texture_t* temp = (texture_t*)realloc(textures->buffer, size * sizeof(texture_t));
|
||||
if (size == INVALID_TEXTURE_ID)
|
||||
{
|
||||
size = INVALID_TEXTURE_ID - 1;
|
||||
}
|
||||
|
||||
if (size == textures->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
texture_asset_t* temp = (texture_asset_t*)realloc(textures->buffer, size * sizeof(texture_asset_t));
|
||||
if (temp != NULL)
|
||||
{
|
||||
textures->buffer = temp;
|
||||
@@ -40,7 +51,13 @@ void texture_collection_free(texture_collection_t* textures)
|
||||
|
||||
for (uint16_t i = 0; i < textures->count; i++)
|
||||
{
|
||||
texture_free(&textures->buffer[i]);
|
||||
texture_free(&textures->buffer[i].texture);
|
||||
|
||||
char* full_name = textures->buffer[i].full_name;
|
||||
if (full_name != NULL)
|
||||
{
|
||||
free(full_name);
|
||||
}
|
||||
}
|
||||
|
||||
free(textures->buffer);
|
||||
@@ -49,6 +66,16 @@ void texture_collection_free(texture_collection_t* textures)
|
||||
|
||||
texture_entity_t texture_load(const char* filename, bool srgb, texture_collection_t* textures)
|
||||
{
|
||||
// TODO: This hurts performance, consider using a hash map or similar structure for faster lookups
|
||||
|
||||
// for (uint16_t i = 0; i < textures->count; i++)
|
||||
// {
|
||||
// if (strcmp(textures->buffer[i].full_name, filename) == 0)
|
||||
// {
|
||||
// return (texture_entity_t){.id = i};
|
||||
// }
|
||||
// }
|
||||
|
||||
int width, height, channels;
|
||||
uint8_t* data = stbi_load(filename, &width, &height, &channels, 0);
|
||||
if (data == NULL)
|
||||
@@ -80,7 +107,7 @@ texture_entity_t texture_load(const char* filename, bool srgb, texture_collectio
|
||||
|
||||
texture_entity_t entity = {.id = textures->count};
|
||||
|
||||
textures->buffer[textures->count] = texture;
|
||||
textures->buffer[textures->count] = (texture_asset_t){.full_name = string_copy(filename), .texture = texture};
|
||||
textures->count++;
|
||||
|
||||
return entity;
|
||||
|
||||
Reference in New Issue
Block a user