fix: 修复法线贴图计算问题并修复G-Buffer和非Primary Ray的tangent有关bug
- G-Buffer添加tangent上传 - BVH部分附加tangent到Triangle数据 - 删除原tangent近似计算代码并在代码中使用传入的tangentmaster
parent
c88e786deb
commit
330fdcf43d
Binary file not shown.
|
|
@ -226,18 +226,19 @@ void setup_cornell_box() {
|
||||||
// 5: Textured material with normal map (for short box)
|
// 5: Textured material with normal map (for short box)
|
||||||
auto textured_material = std::make_shared<Material>();
|
auto textured_material = std::make_shared<Material>();
|
||||||
textured_material->set_albedo(Vec3(1.0f, 1.0f, 1.0f));
|
textured_material->set_albedo(Vec3(1.0f, 1.0f, 1.0f));
|
||||||
textured_material->set_metallic(0.0f);
|
textured_material->set_metallic(1.0f);
|
||||||
textured_material->set_roughness(0.8f);
|
textured_material->set_roughness(1.0f);
|
||||||
textured_material->set_type(MaterialType::DIFFUSE);
|
// textured_material->set_type(MaterialType::DIFFUSE);
|
||||||
|
textured_material->set_type(MaterialType::METAL);
|
||||||
|
|
||||||
// Load textures
|
// Load textures
|
||||||
auto albedo_tex = std::make_shared<are::Texture>();
|
// auto albedo_tex = std::make_shared<are::Texture>();
|
||||||
if (albedo_tex->load_from_file("examples/assets/normal_map_cornell_box/albedo.png")) {
|
// if (albedo_tex->load_from_file("examples/assets/normal_map_cornell_box/albedo.png")) {
|
||||||
textured_material->set_albedo_texture(albedo_tex);
|
// textured_material->set_albedo_texture(albedo_tex);
|
||||||
ARE_LOG_INFO("Loaded albedo texture");
|
// ARE_LOG_INFO("Loaded albedo texture");
|
||||||
} else {
|
// } else {
|
||||||
ARE_LOG_WARN("Failed to load albedo texture");
|
// ARE_LOG_WARN("Failed to load albedo texture");
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto normal_tex = std::make_shared<are::Texture>();
|
auto normal_tex = std::make_shared<are::Texture>();
|
||||||
if (normal_tex->load_from_file("examples/assets/normal_map_cornell_box/normal.png")) {
|
if (normal_tex->load_from_file("examples/assets/normal_map_cornell_box/normal.png")) {
|
||||||
|
|
@ -296,7 +297,8 @@ void setup_cornell_box() {
|
||||||
Vec3(room_size, room_size, -room_size),
|
Vec3(room_size, room_size, -room_size),
|
||||||
Vec3(room_size, -room_size, -room_size),
|
Vec3(room_size, -room_size, -room_size),
|
||||||
Vec3(0.0f, 0.0f, 1.0f),
|
Vec3(0.0f, 0.0f, 1.0f),
|
||||||
white_id);
|
metal_id);
|
||||||
|
// white_id);
|
||||||
back_wall->upload_to_gpu();
|
back_wall->upload_to_gpu();
|
||||||
g_scene->add_mesh(back_wall);
|
g_scene->add_mesh(back_wall);
|
||||||
|
|
||||||
|
|
@ -361,7 +363,7 @@ void setup_cornell_box() {
|
||||||
g_camera->set_position(g_cameraPos);
|
g_camera->set_position(g_cameraPos);
|
||||||
g_camera->set_target(g_cameraTarget);
|
g_camera->set_target(g_cameraTarget);
|
||||||
g_camera->set_up(g_cameraUp);
|
g_camera->set_up(g_cameraUp);
|
||||||
g_camera->set_perspective(45.0f, static_cast<float>(WINDOW_WIDTH) / WINDOW_HEIGHT, 0.1f, 100.0f);
|
g_camera->set_perspective(90.0f, static_cast<float>(WINDOW_WIDTH) / WINDOW_HEIGHT, 0.1f, 100.0f);
|
||||||
g_scene->set_camera(g_camera);
|
g_scene->set_camera(g_camera);
|
||||||
|
|
||||||
// Add point light
|
// Add point light
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ constexpr int GBUFFER_NORMAL = 1;
|
||||||
constexpr int GBUFFER_ALBEDO = 2;
|
constexpr int GBUFFER_ALBEDO = 2;
|
||||||
constexpr int GBUFFER_MATERIAL = 3;
|
constexpr int GBUFFER_MATERIAL = 3;
|
||||||
constexpr int GBUFFER_MATERIAL_ID = 4;
|
constexpr int GBUFFER_MATERIAL_ID = 4;
|
||||||
constexpr int GBUFFER_COUNT = 5;
|
constexpr int GBUFFER_TEXCOORD = 5;
|
||||||
|
constexpr int GBUFFER_TANGENT = 6;
|
||||||
|
constexpr int GBUFFER_COUNT = 7;
|
||||||
|
|
||||||
// Compute shader work group size
|
// Compute shader work group size
|
||||||
constexpr int COMPUTE_GROUP_SIZE_X = 16;
|
constexpr int COMPUTE_GROUP_SIZE_X = 16;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ struct Triangle {
|
||||||
Vec3 v0_, v1_, v2_;
|
Vec3 v0_, v1_, v2_;
|
||||||
Vec3 n0_, n1_, n2_;
|
Vec3 n0_, n1_, n2_;
|
||||||
Vec2 uv0_, uv1_, uv2_;
|
Vec2 uv0_, uv1_, uv2_;
|
||||||
|
Vec3 t0_, t1_, t2_; // Tangents for each vertex
|
||||||
uint material_id_;
|
uint material_id_;
|
||||||
|
|
||||||
// Get bounding box of triangle
|
// Get bounding box of triangle
|
||||||
|
|
@ -75,6 +76,8 @@ struct TriangleGpu {
|
||||||
Vec4 n2_; ///< xyz = n2, w = reserved
|
Vec4 n2_; ///< xyz = n2, w = reserved
|
||||||
Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1
|
Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1
|
||||||
Vec4 uv2_; ///< xy = uv2, zw = reserved
|
Vec4 uv2_; ///< xy = uv2, zw = reserved
|
||||||
|
Vec4 t0_; ///< xyz = t0 (tangent at v0), w = reserved
|
||||||
|
Vec4 t1_; ///< xyz = t1 (tangent at v1), w = reserved
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bounding Volume Hierarchy for ray tracing acceleration
|
// Bounding Volume Hierarchy for ray tracing acceleration
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,24 @@ public:
|
||||||
return textures_[static_cast<int>(slot)] != nullptr;
|
return textures_[static_cast<int>(slot)] != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set texture array index for a slot (used by RayTracer)
|
||||||
|
* @param slot Texture slot
|
||||||
|
* @param index Index into the texture array
|
||||||
|
*/
|
||||||
|
void set_texture_index(TextureSlot slot, uint32_t index) {
|
||||||
|
texture_indices_[static_cast<int>(slot)] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get texture array index for a slot
|
||||||
|
* @param slot Texture slot
|
||||||
|
* @return Index into the texture array (0 = no texture)
|
||||||
|
*/
|
||||||
|
uint32_t get_texture_index(TextureSlot slot) const {
|
||||||
|
return texture_indices_[static_cast<int>(slot)];
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vec3 albedo_;
|
Vec3 albedo_;
|
||||||
Vec3 emission_;
|
Vec3 emission_;
|
||||||
|
|
@ -238,6 +256,7 @@ private:
|
||||||
MaterialType type_;
|
MaterialType type_;
|
||||||
|
|
||||||
std::array<std::shared_ptr<Texture>, static_cast<int>(TextureSlot::COUNT)> textures_;
|
std::array<std::shared_ptr<Texture>, static_cast<int>(TextureSlot::COUNT)> textures_;
|
||||||
|
std::array<uint32_t, static_cast<int>(TextureSlot::COUNT)> texture_indices_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ layout(location = 1) out vec4 g_normal;
|
||||||
layout(location = 2) out vec4 g_albedo;
|
layout(location = 2) out vec4 g_albedo;
|
||||||
layout(location = 3) out vec4 g_material;
|
layout(location = 3) out vec4 g_material;
|
||||||
layout(location = 4) out uint g_material_id;
|
layout(location = 4) out uint g_material_id;
|
||||||
|
layout(location = 5) out vec4 g_texcoord;
|
||||||
|
layout(location = 6) out vec4 g_tangent;
|
||||||
|
|
||||||
uniform vec3 u_albedo;
|
uniform vec3 u_albedo;
|
||||||
uniform float u_metallic;
|
uniform float u_metallic;
|
||||||
|
|
@ -38,4 +40,10 @@ void main() {
|
||||||
|
|
||||||
g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type));
|
g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type));
|
||||||
g_material_id = u_material_id;
|
g_material_id = u_material_id;
|
||||||
|
|
||||||
|
// Store texcoord
|
||||||
|
g_texcoord = vec4(fs_in.texcoord, 0.0, 0.0);
|
||||||
|
|
||||||
|
// Store tangent (xyz = tangent, w = unused)
|
||||||
|
g_tangent = vec4(fs_in.tangent, 0.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
|
||||||
layout(binding = 5, rgba32f) uniform readonly image2D g_material;
|
layout(binding = 5, rgba32f) uniform readonly image2D g_material;
|
||||||
layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
|
layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
|
||||||
|
|
||||||
|
// Texcoord from G-Buffer
|
||||||
|
layout(binding = 7, rgba32f) uniform readonly image2D g_texcoord;
|
||||||
|
|
||||||
|
// Tangent from G-Buffer
|
||||||
|
layout(binding = 8, rgba32f) uniform readonly image2D g_tangent;
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
layout(binding = 3, rgba32f) uniform image2D output_image;
|
layout(binding = 3, rgba32f) uniform image2D output_image;
|
||||||
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||||
|
|
@ -64,6 +70,7 @@ struct HitInfo {
|
||||||
vec3 position;
|
vec3 position;
|
||||||
vec3 normal;
|
vec3 normal;
|
||||||
vec2 texcoord;
|
vec2 texcoord;
|
||||||
|
vec3 tangent;
|
||||||
uint material_id;
|
uint material_id;
|
||||||
int material_type; // material type from G-Buffer
|
int material_type; // material type from G-Buffer
|
||||||
};
|
};
|
||||||
|
|
@ -88,6 +95,8 @@ struct TriangleGpu {
|
||||||
vec4 n2;
|
vec4 n2;
|
||||||
vec4 uv0_uv1; // xy uv0, zw uv1
|
vec4 uv0_uv1; // xy uv0, zw uv1
|
||||||
vec4 uv2; // xy uv2
|
vec4 uv2; // xy uv2
|
||||||
|
vec4 t0; // tangent at v0
|
||||||
|
vec4 t1; // tangent at v1
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std430, binding = 0) readonly buffer MaterialBuffer { Material materials[]; };
|
layout(std430, binding = 0) readonly buffer MaterialBuffer { Material materials[]; };
|
||||||
|
|
@ -268,11 +277,21 @@ bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) {
|
||||||
vec2 uv1 = tri.uv0_uv1.zw;
|
vec2 uv1 = tri.uv0_uv1.zw;
|
||||||
vec2 uv2 = tri.uv2.xy;
|
vec2 uv2 = tri.uv2.xy;
|
||||||
|
|
||||||
|
// Interpolate tangents
|
||||||
|
vec3 t0 = tri.t0.xyz;
|
||||||
|
vec3 t1 = tri.t1.xyz;
|
||||||
|
// Compute t2 from normal and t0 (t2 = cross(n, t0))
|
||||||
|
vec3 t2 = normalize(cross(n0, t0)); // approximate third tangent
|
||||||
|
|
||||||
hit.hit = true;
|
hit.hit = true;
|
||||||
hit.t = t;
|
hit.t = t;
|
||||||
hit.position = ray.origin + t * ray.direction;
|
hit.position = ray.origin + t * ray.direction;
|
||||||
hit.normal = normalize(n0 * w + n1 * u + n2 * v);
|
hit.normal = normalize(n0 * w + n1 * u + n2 * v);
|
||||||
hit.texcoord = uv0 * w + uv1 * u + uv2 * v;
|
hit.texcoord = uv0 * w + uv1 * u + uv2 * v;
|
||||||
|
|
||||||
|
// Interpolate tangent using barycentric coordinates
|
||||||
|
hit.tangent = normalize(t0 * w + t1 * u + t2 * v);
|
||||||
|
|
||||||
hit.material_id = as_uint(tri.v0_material.w);
|
hit.material_id = as_uint(tri.v0_material.w);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -378,6 +397,7 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||||
hit.position = vec3(0.0);
|
hit.position = vec3(0.0);
|
||||||
hit.normal = vec3(0.0, 1.0, 0.0);
|
hit.normal = vec3(0.0, 1.0, 0.0);
|
||||||
hit.texcoord = vec2(0.0);
|
hit.texcoord = vec2(0.0);
|
||||||
|
hit.tangent = vec3(0.0);
|
||||||
hit.material_id = 0u;
|
hit.material_id = 0u;
|
||||||
hit.material_type = 0;
|
hit.material_type = 0;
|
||||||
|
|
||||||
|
|
@ -396,9 +416,19 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||||
vec4 mat = imageLoad(g_material, pixel_coords);
|
vec4 mat = imageLoad(g_material, pixel_coords);
|
||||||
int mtype = int(mat.w);
|
int mtype = int(mat.w);
|
||||||
|
|
||||||
|
// Read texcoord from G-Buffer
|
||||||
|
vec4 texcoord_tangent = imageLoad(g_texcoord, pixel_coords);
|
||||||
|
vec2 texcoord = texcoord_tangent.xy;
|
||||||
|
|
||||||
|
// Read tangent from G-Buffer
|
||||||
|
vec4 tangent_data = imageLoad(g_tangent, pixel_coords);
|
||||||
|
vec3 tangent = tangent_data.xyz;
|
||||||
|
|
||||||
hit.hit = true;
|
hit.hit = true;
|
||||||
hit.position = p;
|
hit.position = p;
|
||||||
hit.normal = n;
|
hit.normal = n;
|
||||||
|
hit.texcoord = texcoord;
|
||||||
|
hit.tangent = tangent;
|
||||||
hit.material_id = mid;
|
hit.material_id = mid;
|
||||||
hit.material_type = mtype;
|
hit.material_type = mtype;
|
||||||
|
|
||||||
|
|
@ -425,7 +455,7 @@ vec3 apply_normal_map(vec3 normal, vec2 texcoord, vec3 tangent, uint normal_hand
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply material textures to get final PBR values
|
// Apply material textures to get final PBR values
|
||||||
void apply_material_textures(inout Material mat, vec2 texcoord, vec3 normal, vec3 tangent) {
|
void apply_material_textures(inout Material mat, inout vec3 normal, vec2 texcoord, vec3 tangent) {
|
||||||
if (!u_enable_textures) return;
|
if (!u_enable_textures) return;
|
||||||
|
|
||||||
// Albedo texture
|
// Albedo texture
|
||||||
|
|
@ -556,7 +586,7 @@ ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed
|
||||||
// Direct lighting (with shadow ray)
|
// Direct lighting (with shadow ray)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) {
|
vec3 eval_direct_lighting(inout HitInfo hit, Material mat, inout uint seed) {
|
||||||
if (u_light_count == 0u) return vec3(0.0);
|
if (u_light_count == 0u) return vec3(0.0);
|
||||||
|
|
||||||
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
|
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
|
||||||
|
|
@ -652,10 +682,8 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
||||||
mat0.type = hit0.material_type;
|
mat0.type = hit0.material_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply PBR textures
|
// Apply PBR textures (use tangent from G-Buffer if available)
|
||||||
vec3 tangent0 = normalize(cross(hit0.normal, vec3(0.0, 1.0, 0.0)));
|
apply_material_textures(mat0, hit0.normal, hit0.texcoord, hit0.tangent);
|
||||||
if (length(tangent0) < 0.001) tangent0 = normalize(cross(hit0.normal, vec3(1.0, 0.0, 0.0)));
|
|
||||||
apply_material_textures(mat0, hit0.texcoord, hit0.normal, tangent0);
|
|
||||||
|
|
||||||
radiance += throughput * mat0.emission;
|
radiance += throughput * mat0.emission;
|
||||||
if (mat0.type == MATERIAL_DIFFUSE) {
|
if (mat0.type == MATERIAL_DIFFUSE) {
|
||||||
|
|
@ -679,10 +707,8 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
||||||
|
|
||||||
Material mat = fetch_material(hit.material_id);
|
Material mat = fetch_material(hit.material_id);
|
||||||
|
|
||||||
// Apply PBR textures
|
// Apply PBR textures (use tangent from intersection)
|
||||||
vec3 tangent = normalize(cross(hit.normal, vec3(0.0, 1.0, 0.0)));
|
apply_material_textures(mat, hit.normal, hit.texcoord, hit.tangent);
|
||||||
if (length(tangent) < 0.001) tangent = normalize(cross(hit.normal, vec3(1.0, 0.0, 0.0)));
|
|
||||||
apply_material_textures(mat, hit.texcoord, hit.normal, tangent);
|
|
||||||
|
|
||||||
radiance += throughput * mat.emission;
|
radiance += throughput * mat.emission;
|
||||||
if (mat.type == MATERIAL_DIFFUSE) {
|
if (mat.type == MATERIAL_DIFFUSE) {
|
||||||
|
|
|
||||||
459
src/core/bvh.cpp
459
src/core/bvh.cpp
|
|
@ -1,41 +1,41 @@
|
||||||
#include "core/bvh.h"
|
#include "core/bvh.h"
|
||||||
#include "utils/logger.h"
|
|
||||||
#include "basic/constants.h"
|
#include "basic/constants.h"
|
||||||
|
#include "utils/logger.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
||||||
// AABB implementation
|
// AABB implementation
|
||||||
void AABB::expand(const Vec3& point) {
|
void AABB::expand(const Vec3 &point) {
|
||||||
min_ = glm::min(min_, point);
|
min_ = glm::min(min_, point);
|
||||||
max_ = glm::max(max_, point);
|
max_ = glm::max(max_, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AABB::expand(const AABB& other) {
|
void AABB::expand(const AABB &other) {
|
||||||
min_ = glm::min(min_, other.min_);
|
min_ = glm::min(min_, other.min_);
|
||||||
max_ = glm::max(max_, other.max_);
|
max_ = glm::max(max_, other.max_);
|
||||||
}
|
}
|
||||||
|
|
||||||
float AABB::surface_area() const {
|
float AABB::surface_area() const {
|
||||||
Vec3 extent = max_ - min_;
|
Vec3 extent = max_ - min_;
|
||||||
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
|
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AABB::is_valid() const {
|
bool AABB::is_valid() const {
|
||||||
return min_.x <= max_.x && min_.y <= max_.y && min_.z <= max_.z;
|
return min_.x <= max_.x && min_.y <= max_.y && min_.z <= max_.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triangle implementation
|
// Triangle implementation
|
||||||
AABB Triangle::get_bounds() const {
|
AABB Triangle::get_bounds() const {
|
||||||
AABB bounds(v0_, v0_);
|
AABB bounds(v0_, v0_);
|
||||||
bounds.expand(v1_);
|
bounds.expand(v1_);
|
||||||
bounds.expand(v2_);
|
bounds.expand(v2_);
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 Triangle::get_centroid() const {
|
Vec3 Triangle::get_centroid() const {
|
||||||
return (v0_ + v1_ + v2_) / 3.0f;
|
return (v0_ + v1_ + v2_) / 3.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// BVH implementation
|
// BVH implementation
|
||||||
|
|
@ -43,290 +43,299 @@ BVH::BVH() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BVH::~BVH() {
|
BVH::~BVH() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BVH::build(const std::vector<std::shared_ptr<Mesh>>& meshes) {
|
bool BVH::build(const std::vector<std::shared_ptr<Mesh>> &meshes) {
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
ARE_LOG_INFO("Building BVH...");
|
ARE_LOG_INFO("Building BVH...");
|
||||||
|
|
||||||
// Extract all triangles from meshes
|
// Extract all triangles from meshes
|
||||||
for (const auto& mesh : meshes) {
|
for (const auto &mesh : meshes) {
|
||||||
const auto& vertices = mesh->get_vertices();
|
const auto &vertices = mesh->get_vertices();
|
||||||
const auto& indices = mesh->get_indices();
|
const auto &indices = mesh->get_indices();
|
||||||
uint material_id = mesh->get_material();
|
uint material_id = mesh->get_material();
|
||||||
Mat4 transform = mesh->get_transform();
|
Mat4 transform = mesh->get_transform();
|
||||||
|
|
||||||
for (size_t i = 0; i < indices.size(); i += 3) {
|
for (size_t i = 0; i < indices.size(); i += 3) {
|
||||||
Triangle tri;
|
Triangle tri;
|
||||||
|
|
||||||
// Transform vertices
|
// Transform vertices
|
||||||
Vec4 v0 = transform * Vec4(vertices[indices[i]].position_, 1.0f);
|
Vec4 v0 = transform * Vec4(vertices[indices[i]].position_, 1.0f);
|
||||||
Vec4 v1 = transform * Vec4(vertices[indices[i + 1]].position_, 1.0f);
|
Vec4 v1 = transform * Vec4(vertices[indices[i + 1]].position_, 1.0f);
|
||||||
Vec4 v2 = transform * Vec4(vertices[indices[i + 2]].position_, 1.0f);
|
Vec4 v2 = transform * Vec4(vertices[indices[i + 2]].position_, 1.0f);
|
||||||
|
|
||||||
tri.v0_ = Vec3(v0) / v0.w;
|
tri.v0_ = Vec3(v0) / v0.w;
|
||||||
tri.v1_ = Vec3(v1) / v1.w;
|
tri.v1_ = Vec3(v1) / v1.w;
|
||||||
tri.v2_ = Vec3(v2) / v2.w;
|
tri.v2_ = Vec3(v2) / v2.w;
|
||||||
|
|
||||||
// Transform normals
|
// Transform normals
|
||||||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
|
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
|
||||||
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
|
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
|
||||||
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
|
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
|
||||||
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
|
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
|
||||||
|
|
||||||
// Copy UVs
|
// Transform tangents
|
||||||
tri.uv0_ = vertices[indices[i]].texcoord_;
|
tri.t0_ = glm::normalize(normal_matrix * vertices[indices[i]].tangent_);
|
||||||
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
|
tri.t1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].tangent_);
|
||||||
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
|
tri.t2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].tangent_);
|
||||||
|
|
||||||
tri.material_id_ = material_id;
|
// Copy UVs
|
||||||
|
tri.uv0_ = vertices[indices[i]].texcoord_;
|
||||||
|
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
|
||||||
|
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
|
||||||
|
|
||||||
triangles_.push_back(tri);
|
tri.material_id_ = material_id;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triangles_.empty()) {
|
triangles_.push_back(tri);
|
||||||
ARE_LOG_WARN("No triangles to build BVH");
|
}
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize triangle indices
|
if (triangles_.empty()) {
|
||||||
triangle_indices_.resize(triangles_.size());
|
ARE_LOG_WARN("No triangles to build BVH");
|
||||||
for (size_t i = 0; i < triangles_.size(); ++i) {
|
return false;
|
||||||
triangle_indices_[i] = static_cast<uint>(i);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve space for nodes (estimate)
|
// Initialize triangle indices
|
||||||
nodes_.reserve(triangles_.size() * 2);
|
triangle_indices_.resize(triangles_.size());
|
||||||
|
for (size_t i = 0; i < triangles_.size(); ++i) {
|
||||||
|
triangle_indices_[i] = static_cast<uint>(i);
|
||||||
|
}
|
||||||
|
|
||||||
// Create root node
|
// Reserve space for nodes (estimate)
|
||||||
nodes_.emplace_back();
|
nodes_.reserve(triangles_.size() * 2);
|
||||||
|
|
||||||
// Build BVH recursively
|
// Create root node
|
||||||
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
|
nodes_.emplace_back();
|
||||||
|
|
||||||
ARE_LOG_INFO("BVH built: " + std::to_string(nodes_.size()) + " nodes, " +
|
// Build BVH recursively
|
||||||
std::to_string(triangles_.size()) + " triangles");
|
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
|
||||||
|
|
||||||
return true;
|
ARE_LOG_INFO("BVH built: " + std::to_string(nodes_.size()) + " nodes, " + std::to_string(triangles_.size()) + " triangles");
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
|
void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
|
||||||
BVHNode& node = nodes_[node_idx];
|
BVHNode &node = nodes_[node_idx];
|
||||||
|
|
||||||
// Calculate bounds
|
// Calculate bounds
|
||||||
AABB bounds = calculate_bounds_(first_prim, prim_count);
|
AABB bounds = calculate_bounds_(first_prim, prim_count);
|
||||||
node.aabb_min_ = bounds.min_;
|
node.aabb_min_ = bounds.min_;
|
||||||
node.aabb_max_ = bounds.max_;
|
node.aabb_max_ = bounds.max_;
|
||||||
|
|
||||||
// Leaf node threshold
|
// Leaf node threshold
|
||||||
const uint LEAF_SIZE = 4;
|
const uint LEAF_SIZE = 4;
|
||||||
|
|
||||||
if (prim_count <= LEAF_SIZE) {
|
if (prim_count <= LEAF_SIZE) {
|
||||||
// Create leaf node
|
// Create leaf node
|
||||||
node.left_first_ = first_prim;
|
|
||||||
node.count_ = prim_count;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find best split
|
|
||||||
int axis;
|
|
||||||
float split_pos;
|
|
||||||
float split_cost = find_best_split_(first_prim, prim_count, axis, split_pos);
|
|
||||||
if(split_cost == std::numeric_limits<float>::max()) {
|
|
||||||
node.left_first_ = first_prim;
|
node.left_first_ = first_prim;
|
||||||
node.count_ = prim_count;
|
node.count_ = prim_count;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if split is beneficial
|
// Find best split
|
||||||
float no_split_cost = prim_count * bounds.surface_area();
|
int axis;
|
||||||
if (split_cost >= no_split_cost) {
|
float split_pos;
|
||||||
// Create leaf node
|
float split_cost = find_best_split_(first_prim, prim_count, axis, split_pos);
|
||||||
node.left_first_ = first_prim;
|
if (split_cost == std::numeric_limits<float>::max()) {
|
||||||
node.count_ = prim_count;
|
node.left_first_ = first_prim;
|
||||||
return;
|
node.count_ = prim_count;
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Partition primitives
|
// Check if split is beneficial
|
||||||
uint mid = first_prim;
|
float no_split_cost = prim_count * bounds.surface_area();
|
||||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
if (split_cost >= no_split_cost) {
|
||||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
// Create leaf node
|
||||||
float centroid = tri.get_centroid()[axis];
|
node.left_first_ = first_prim;
|
||||||
|
node.count_ = prim_count;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (centroid < split_pos) {
|
// Partition primitives
|
||||||
std::swap(triangle_indices_[i], triangle_indices_[mid]);
|
uint mid = first_prim;
|
||||||
mid++;
|
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||||
}
|
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||||
}
|
float centroid = tri.get_centroid()[axis];
|
||||||
|
|
||||||
// Ensure we have primitives on both sides
|
if (centroid < split_pos) {
|
||||||
if (mid == first_prim || mid == first_prim + prim_count) {
|
std::swap(triangle_indices_[i], triangle_indices_[mid]);
|
||||||
mid = first_prim + prim_count / 2;
|
mid++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create interior node
|
// Ensure we have primitives on both sides
|
||||||
uint left_count = mid - first_prim;
|
if (mid == first_prim || mid == first_prim + prim_count) {
|
||||||
uint right_count = prim_count - left_count;
|
mid = first_prim + prim_count / 2;
|
||||||
|
}
|
||||||
|
|
||||||
node.left_first_ = static_cast<uint>(nodes_.size());
|
// Create interior node
|
||||||
node.count_ = 0;
|
uint left_count = mid - first_prim;
|
||||||
|
uint right_count = prim_count - left_count;
|
||||||
|
|
||||||
// Create child nodes
|
node.left_first_ = static_cast<uint>(nodes_.size());
|
||||||
nodes_.emplace_back();
|
node.count_ = 0;
|
||||||
nodes_.emplace_back();
|
|
||||||
|
|
||||||
// Recursively build children
|
// Create child nodes
|
||||||
build_recursive_(node.left_first_, first_prim, left_count);
|
nodes_.emplace_back();
|
||||||
build_recursive_(node.left_first_ + 1, mid, right_count);
|
nodes_.emplace_back();
|
||||||
|
|
||||||
|
// Recursively build children
|
||||||
|
build_recursive_(node.left_first_, first_prim, left_count);
|
||||||
|
build_recursive_(node.left_first_ + 1, mid, right_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
float BVH::find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos) {
|
float BVH::find_best_split_(uint first_prim, uint prim_count, int &axis, float &split_pos) {
|
||||||
float best_cost = std::numeric_limits<float>::max();
|
float best_cost = std::numeric_limits<float>::max();
|
||||||
axis = 0, split_pos = 0.0f;
|
axis = 0, split_pos = 0.0f;
|
||||||
|
|
||||||
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
|
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
|
||||||
|
|
||||||
// Try each axis
|
// Try each axis
|
||||||
for (int a = 0; a < 3; ++a) {
|
for (int a = 0; a < 3; ++a) {
|
||||||
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
|
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
|
||||||
if (extent < EPSILON) continue;
|
if (extent < EPSILON)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Try multiple split positions
|
// Try multiple split positions
|
||||||
const int NUM_BINS = 16;
|
const int NUM_BINS = 16;
|
||||||
for (int i = 1; i < NUM_BINS; ++i) {
|
for (int i = 1; i < NUM_BINS; ++i) {
|
||||||
float t = static_cast<float>(i) / NUM_BINS;
|
float t = static_cast<float>(i) / NUM_BINS;
|
||||||
float pos = centroid_bounds.min_[a] + t * extent;
|
float pos = centroid_bounds.min_[a] + t * extent;
|
||||||
|
|
||||||
// Count primitives and calculate bounds for each side
|
// Count primitives and calculate bounds for each side
|
||||||
AABB left_bounds, right_bounds;
|
AABB left_bounds, right_bounds;
|
||||||
uint left_count = 0, right_count = 0;
|
uint left_count = 0, right_count = 0;
|
||||||
|
|
||||||
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
|
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
|
||||||
Triangle& tri = triangles_[triangle_indices_[j]];
|
Triangle &tri = triangles_[triangle_indices_[j]];
|
||||||
float centroid = tri.get_centroid()[a];
|
float centroid = tri.get_centroid()[a];
|
||||||
|
|
||||||
if (centroid < pos) {
|
if (centroid < pos) {
|
||||||
left_bounds.expand(tri.get_bounds());
|
left_bounds.expand(tri.get_bounds());
|
||||||
left_count++;
|
left_count++;
|
||||||
} else {
|
} else {
|
||||||
right_bounds.expand(tri.get_bounds());
|
right_bounds.expand(tri.get_bounds());
|
||||||
right_count++;
|
right_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate SAH cost
|
// Calculate SAH cost
|
||||||
if (left_count == 0 || right_count == 0) continue;
|
if (left_count == 0 || right_count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
float cost = left_count * left_bounds.surface_area() +
|
float cost = left_count * left_bounds.surface_area() + right_count * right_bounds.surface_area();
|
||||||
right_count * right_bounds.surface_area();
|
|
||||||
|
|
||||||
if (cost < best_cost) {
|
if (cost < best_cost) {
|
||||||
best_cost = cost;
|
best_cost = cost;
|
||||||
axis = a;
|
axis = a;
|
||||||
split_pos = pos;
|
split_pos = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return best_cost;
|
return best_cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
AABB BVH::calculate_bounds_(uint first_prim, uint prim_count) {
|
AABB BVH::calculate_bounds_(uint first_prim, uint prim_count) {
|
||||||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
AABB bounds { Vec3(std::numeric_limits<float>::max()),
|
||||||
Vec3(std::numeric_limits<float>::lowest())};
|
Vec3(std::numeric_limits<float>::lowest()) };
|
||||||
|
|
||||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||||
bounds.expand(tri.get_bounds());
|
bounds.expand(tri.get_bounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
AABB BVH::calculate_centroid_bounds_(uint first_prim, uint prim_count) {
|
AABB BVH::calculate_centroid_bounds_(uint first_prim, uint prim_count) {
|
||||||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
AABB bounds { Vec3(std::numeric_limits<float>::max()),
|
||||||
Vec3(std::numeric_limits<float>::lowest())};
|
Vec3(std::numeric_limits<float>::lowest()) };
|
||||||
|
|
||||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||||
bounds.expand(tri.get_centroid());
|
bounds.expand(tri.get_centroid());
|
||||||
}
|
}
|
||||||
|
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) {
|
bool BVH::upload_to_gpu(Buffer &node_buffer, Buffer &triangle_buffer) {
|
||||||
if (nodes_.empty() || triangles_.empty()) {
|
if (nodes_.empty() || triangles_.empty()) {
|
||||||
ARE_LOG_ERROR("Cannot upload empty BVH to GPU");
|
ARE_LOG_ERROR("Cannot upload empty BVH to GPU");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reorder triangles according to BVH layout
|
// Reorder triangles according to BVH layout
|
||||||
std::vector<Triangle> ordered_triangles;
|
std::vector<Triangle> ordered_triangles;
|
||||||
ordered_triangles.reserve(triangles_.size());
|
ordered_triangles.reserve(triangles_.size());
|
||||||
for (uint idx : triangle_indices_) {
|
for (uint idx : triangle_indices_) {
|
||||||
ordered_triangles.push_back(triangles_[idx]);
|
ordered_triangles.push_back(triangles_[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack nodes to GPU layout
|
// Pack nodes to GPU layout
|
||||||
std::vector<BVHNodeGpu> node_gpu;
|
std::vector<BVHNodeGpu> node_gpu;
|
||||||
node_gpu.resize(nodes_.size());
|
node_gpu.resize(nodes_.size());
|
||||||
for (size_t i = 0; i < nodes_.size(); ++i) {
|
for (size_t i = 0; i < nodes_.size(); ++i) {
|
||||||
const BVHNode& n = nodes_[i];
|
const BVHNode &n = nodes_[i];
|
||||||
BVHNodeGpu g;
|
BVHNodeGpu g;
|
||||||
g.aabb_min_left_first_ = Vec4(n.aabb_min_, glm::uintBitsToFloat(n.left_first_));
|
g.aabb_min_left_first_ = Vec4(n.aabb_min_, glm::uintBitsToFloat(n.left_first_));
|
||||||
g.aabb_max_count_ = Vec4(n.aabb_max_, glm::uintBitsToFloat(n.count_));
|
g.aabb_max_count_ = Vec4(n.aabb_max_, glm::uintBitsToFloat(n.count_));
|
||||||
node_gpu[i] = g;
|
node_gpu[i] = g;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack triangles to GPU layout
|
// Pack triangles to GPU layout
|
||||||
std::vector<TriangleGpu> tri_gpu;
|
std::vector<TriangleGpu> tri_gpu;
|
||||||
tri_gpu.resize(ordered_triangles.size());
|
tri_gpu.resize(ordered_triangles.size());
|
||||||
for (size_t i = 0; i < ordered_triangles.size(); ++i) {
|
for (size_t i = 0; i < ordered_triangles.size(); ++i) {
|
||||||
const Triangle& t = ordered_triangles[i];
|
const Triangle &t = ordered_triangles[i];
|
||||||
|
|
||||||
TriangleGpu g{};
|
TriangleGpu g {};
|
||||||
g.v0_material_ = Vec4(t.v0_, glm::uintBitsToFloat(t.material_id_));
|
g.v0_material_ = Vec4(t.v0_, glm::uintBitsToFloat(t.material_id_));
|
||||||
g.v1_ = Vec4(t.v1_, 0.0f);
|
g.v1_ = Vec4(t.v1_, 0.0f);
|
||||||
g.v2_ = Vec4(t.v2_, 0.0f);
|
g.v2_ = Vec4(t.v2_, 0.0f);
|
||||||
|
|
||||||
g.n0_ = Vec4(t.n0_, 0.0f);
|
g.n0_ = Vec4(t.n0_, 0.0f);
|
||||||
g.n1_ = Vec4(t.n1_, 0.0f);
|
g.n1_ = Vec4(t.n1_, 0.0f);
|
||||||
g.n2_ = Vec4(t.n2_, 0.0f);
|
g.n2_ = Vec4(t.n2_, 0.0f);
|
||||||
|
|
||||||
g.uv0_uv1_ = Vec4(t.uv0_.x, t.uv0_.y, t.uv1_.x, t.uv1_.y);
|
g.uv0_uv1_ = Vec4(t.uv0_.x, t.uv0_.y, t.uv1_.x, t.uv1_.y);
|
||||||
g.uv2_ = Vec4(t.uv2_.x, t.uv2_.y, 0.0f, 0.0f);
|
g.uv2_ = Vec4(t.uv2_.x, t.uv2_.y, 0.0f, 0.0f);
|
||||||
|
|
||||||
tri_gpu[i] = g;
|
// Pack tangents
|
||||||
}
|
g.t0_ = Vec4(t.t0_, 0.0f);
|
||||||
|
g.t1_ = Vec4(t.t1_, 0.0f);
|
||||||
|
|
||||||
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
tri_gpu[i] = g;
|
||||||
node_gpu.size() * sizeof(BVHNodeGpu),
|
}
|
||||||
node_gpu.data(),
|
|
||||||
BufferUsage::STATIC_DRAW)) {
|
|
||||||
ARE_LOG_ERROR("Failed to upload BVH nodes to GPU");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||||
tri_gpu.size() * sizeof(TriangleGpu),
|
node_gpu.size() * sizeof(BVHNodeGpu),
|
||||||
tri_gpu.data(),
|
node_gpu.data(),
|
||||||
BufferUsage::STATIC_DRAW)) {
|
BufferUsage::STATIC_DRAW)) {
|
||||||
ARE_LOG_ERROR("Failed to upload BVH triangles to GPU");
|
ARE_LOG_ERROR("Failed to upload BVH nodes to GPU");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARE_LOG_INFO("BVH uploaded to GPU successfully");
|
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||||
return true;
|
tri_gpu.size() * sizeof(TriangleGpu),
|
||||||
|
tri_gpu.data(),
|
||||||
|
BufferUsage::STATIC_DRAW)) {
|
||||||
|
ARE_LOG_ERROR("Failed to upload BVH triangles to GPU");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARE_LOG_INFO("BVH uploaded to GPU successfully");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BVH::clear() {
|
void BVH::clear() {
|
||||||
nodes_.clear();
|
nodes_.clear();
|
||||||
triangles_.clear();
|
triangles_.clear();
|
||||||
triangle_indices_.clear();
|
triangle_indices_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
|
|
@ -5,226 +5,240 @@
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
||||||
GBuffer::GBuffer(uint width, uint height)
|
GBuffer::GBuffer(uint width, uint height)
|
||||||
: width_(width)
|
: width_(width)
|
||||||
, height_(height)
|
, height_(height)
|
||||||
, fbo_(INVALID_HANDLE)
|
, fbo_(INVALID_HANDLE)
|
||||||
, depth_texture_(INVALID_HANDLE)
|
, depth_texture_(INVALID_HANDLE)
|
||||||
, initialized_(false) {
|
, initialized_(false) {
|
||||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||||
textures_[i] = INVALID_HANDLE;
|
textures_[i] = INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GBuffer::~GBuffer() {
|
GBuffer::~GBuffer() {
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBuffer::initialize() {
|
bool GBuffer::initialize() {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
ARE_LOG_WARN("GBuffer already initialized");
|
ARE_LOG_WARN("GBuffer already initialized");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo_);
|
glGenFramebuffers(1, &fbo_);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||||
|
|
||||||
textures_[GBUFFER_POSITION] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
textures_[GBUFFER_POSITION] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||||
textures_[GBUFFER_NORMAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
textures_[GBUFFER_NORMAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||||
textures_[GBUFFER_ALBEDO] = create_texture_(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
textures_[GBUFFER_ALBEDO] = create_texture_(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||||
|
|
||||||
// New: material params (metallic, roughness, ior, type)
|
// New: material params (metallic, roughness, ior, type)
|
||||||
textures_[GBUFFER_MATERIAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
textures_[GBUFFER_MATERIAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||||
|
|
||||||
// New: material id (integer)
|
// New: material id (integer)
|
||||||
textures_[GBUFFER_MATERIAL_ID] = create_texture_(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT);
|
textures_[GBUFFER_MATERIAL_ID] = create_texture_(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT);
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
// New: texcoord + tangent (xy = texcoord, zw = tangent)
|
||||||
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
textures_[GBUFFER_TEXCOORD] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
|
||||||
GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
|
||||||
GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
|
||||||
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL], 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID,
|
|
||||||
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL_ID], 0);
|
|
||||||
|
|
||||||
glGenTextures(1, &depth_texture_);
|
// New: tangent (xyz = tangent, w = unused)
|
||||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
textures_[GBUFFER_TANGENT] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width_, height_, 0,
|
|
||||||
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
|
||||||
GL_TEXTURE_2D, depth_texture_, 0);
|
|
||||||
|
|
||||||
GLenum draw_buffers[GBUFFER_COUNT] = {
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||||
GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
||||||
GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||||||
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0);
|
||||||
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
||||||
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID
|
GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0);
|
||||||
};
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
||||||
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
|
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL], 0);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID,
|
||||||
|
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL_ID], 0);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_TEXCOORD,
|
||||||
|
GL_TEXTURE_2D, textures_[GBUFFER_TEXCOORD], 0);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_TANGENT,
|
||||||
|
GL_TEXTURE_2D, textures_[GBUFFER_TANGENT], 0);
|
||||||
|
|
||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
glGenTextures(1, &depth_texture_);
|
||||||
ARE_LOG_ERROR("GBuffer framebuffer is not complete");
|
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width_, height_, 0,
|
||||||
return false;
|
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
|
||||||
}
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
|
GL_TEXTURE_2D, depth_texture_, 0);
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
GLenum draw_buffers[GBUFFER_COUNT] = {
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_TEXCOORD,
|
||||||
|
GL_COLOR_ATTACHMENT0 + GBUFFER_TANGENT
|
||||||
|
};
|
||||||
|
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
|
||||||
|
|
||||||
initialized_ = true;
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
ARE_LOG_INFO("GBuffer initialized successfully");
|
ARE_LOG_ERROR("GBuffer framebuffer is not complete");
|
||||||
return true;
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
ARE_LOG_INFO("GBuffer initialized successfully");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBuffer::release() {
|
void GBuffer::release() {
|
||||||
if (!initialized_) return;
|
if (!initialized_)
|
||||||
|
return;
|
||||||
|
|
||||||
if (fbo_ != INVALID_HANDLE) {
|
if (fbo_ != INVALID_HANDLE) {
|
||||||
glDeleteFramebuffers(1, &fbo_);
|
glDeleteFramebuffers(1, &fbo_);
|
||||||
fbo_ = INVALID_HANDLE;
|
fbo_ = INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||||
if (textures_[i] != INVALID_HANDLE) {
|
if (textures_[i] != INVALID_HANDLE) {
|
||||||
glDeleteTextures(1, &textures_[i]);
|
glDeleteTextures(1, &textures_[i]);
|
||||||
textures_[i] = INVALID_HANDLE;
|
textures_[i] = INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth_texture_ != INVALID_HANDLE) {
|
if (depth_texture_ != INVALID_HANDLE) {
|
||||||
glDeleteTextures(1, &depth_texture_);
|
glDeleteTextures(1, &depth_texture_);
|
||||||
depth_texture_ = INVALID_HANDLE;
|
depth_texture_ = INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
ARE_LOG_INFO("GBuffer released");
|
ARE_LOG_INFO("GBuffer released");
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
||||||
TextureHandle texture;
|
TextureHandle texture;
|
||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
|
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBuffer::render(const Scene& scene, const Shader& shader) {
|
void GBuffer::render(const Scene &scene, const Shader &shader) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
ARE_LOG_ERROR("GBuffer not initialized");
|
ARE_LOG_ERROR("GBuffer not initialized");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind framebuffer
|
// Bind framebuffer
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||||
glViewport(0, 0, width_, height_);
|
glViewport(0, 0, width_, height_);
|
||||||
|
|
||||||
// Clear buffers
|
// Clear buffers
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
// Enable depth test
|
// Enable depth test
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glDepthFunc(GL_LESS);
|
glDepthFunc(GL_LESS);
|
||||||
|
|
||||||
// Use shader
|
// Use shader
|
||||||
shader.use();
|
shader.use();
|
||||||
|
|
||||||
// Set camera matrices
|
// Set camera matrices
|
||||||
const Camera& camera = scene.get_camera();
|
const Camera &camera = scene.get_camera();
|
||||||
Mat4 view = camera.get_view_matrix();
|
Mat4 view = camera.get_view_matrix();
|
||||||
Mat4 projection = camera.get_projection_matrix();
|
Mat4 projection = camera.get_projection_matrix();
|
||||||
|
|
||||||
shader.set_mat4("u_view", view);
|
shader.set_mat4("u_view", view);
|
||||||
shader.set_mat4("u_projection", projection);
|
shader.set_mat4("u_projection", projection);
|
||||||
|
|
||||||
// Render all meshes
|
// Render all meshes
|
||||||
const auto& meshes = scene.get_meshes();
|
const auto &meshes = scene.get_meshes();
|
||||||
const auto& materials = scene.get_materials();
|
const auto &materials = scene.get_materials();
|
||||||
|
|
||||||
for (const auto& mesh : meshes) {
|
for (const auto &mesh : meshes) {
|
||||||
if (!mesh->is_uploaded()) {
|
if (!mesh->is_uploaded()) {
|
||||||
ARE_LOG_WARN("Mesh not uploaded to GPU, skipping");
|
ARE_LOG_WARN("Mesh not uploaded to GPU, skipping");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set model matrix
|
// Set model matrix
|
||||||
Mat4 model = mesh->get_transform();
|
Mat4 model = mesh->get_transform();
|
||||||
shader.set_mat4("u_model", model);
|
shader.set_mat4("u_model", model);
|
||||||
|
|
||||||
// Set material properties
|
// Set material properties
|
||||||
uint material_id = mesh->get_material();
|
uint material_id = mesh->get_material();
|
||||||
if (material_id < materials.size()) {
|
if (material_id < materials.size()) {
|
||||||
const auto& material = materials[material_id];
|
const auto &material = materials[material_id];
|
||||||
|
|
||||||
shader.set_vec3("u_albedo", material->get_albedo());
|
shader.set_vec3("u_albedo", material->get_albedo());
|
||||||
shader.set_float("u_metallic", material->get_metallic());
|
shader.set_float("u_metallic", material->get_metallic());
|
||||||
shader.set_float("u_roughness", material->get_roughness());
|
shader.set_float("u_roughness", material->get_roughness());
|
||||||
shader.set_uint("u_material_id", material_id);
|
shader.set_uint("u_material_id", material_id);
|
||||||
shader.set_float("u_ior", material->get_ior());
|
shader.set_float("u_ior", material->get_ior());
|
||||||
shader.set_vec3("u_emission", material->get_emission());
|
shader.set_vec3("u_emission", material->get_emission());
|
||||||
shader.set_uint("u_material_type", static_cast<uint>(material->get_type()));
|
shader.set_uint("u_material_type", static_cast<uint>(material->get_type()));
|
||||||
|
|
||||||
// Bind textures
|
// Bind textures
|
||||||
auto albedo_tex = material->get_albedo_texture();
|
auto albedo_tex = material->get_albedo_texture();
|
||||||
if (albedo_tex && albedo_tex->is_valid()) {
|
if (albedo_tex && albedo_tex->is_valid()) {
|
||||||
albedo_tex->bind(0);
|
albedo_tex->bind(0);
|
||||||
shader.set_int("u_albedo_map", 0);
|
shader.set_int("u_albedo_map", 0);
|
||||||
shader.set_int("u_has_albedo_map", 1);
|
shader.set_int("u_has_albedo_map", 1);
|
||||||
} else {
|
} else {
|
||||||
shader.set_int("u_has_albedo_map", 0);
|
shader.set_int("u_has_albedo_map", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto normal_tex = material->get_normal_texture();
|
auto normal_tex = material->get_normal_texture();
|
||||||
if (normal_tex && normal_tex->is_valid()) {
|
if (normal_tex && normal_tex->is_valid()) {
|
||||||
normal_tex->bind(1);
|
normal_tex->bind(1);
|
||||||
shader.set_int("u_normal_map", 1);
|
shader.set_int("u_normal_map", 1);
|
||||||
shader.set_int("u_has_normal_map", 1);
|
shader.set_int("u_has_normal_map", 1);
|
||||||
} else {
|
} else {
|
||||||
shader.set_int("u_has_normal_map", 0);
|
shader.set_int("u_has_normal_map", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw mesh
|
// Draw mesh
|
||||||
glBindVertexArray(mesh->get_vao());
|
glBindVertexArray(mesh->get_vao());
|
||||||
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind framebuffer
|
// Unbind framebuffer
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBuffer::resize(uint width, uint height) {
|
void GBuffer::resize(uint width, uint height) {
|
||||||
if (width == width_ && height == height_) return;
|
if (width == width_ && height == height_)
|
||||||
|
return;
|
||||||
|
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
|
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
release();
|
release();
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle GBuffer::get_texture(int index) const {
|
TextureHandle GBuffer::get_texture(int index) const {
|
||||||
if (index < 0 || index >= GBUFFER_COUNT) {
|
if (index < 0 || index >= GBUFFER_COUNT) {
|
||||||
ARE_LOG_ERROR("Invalid G-Buffer texture index");
|
ARE_LOG_ERROR("Invalid G-Buffer texture index");
|
||||||
return INVALID_HANDLE;
|
return INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
return textures_[index];
|
return textures_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBuffer::get_dimensions(uint& width, uint& height) const {
|
void GBuffer::get_dimensions(uint &width, uint &height) const {
|
||||||
width = width_;
|
width = width_;
|
||||||
height = height_;
|
height = height_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,34 @@ void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle
|
||||||
rebuild_bvh(scene);
|
rebuild_bvh(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload scene data
|
// Build texture arrays BEFORE uploading materials (so indices are available)
|
||||||
|
const auto &materials = scene.get_materials();
|
||||||
|
bool has_textures = false;
|
||||||
|
for (const auto &mat : materials) {
|
||||||
|
if (mat->has_texture(TextureSlot::ALBEDO) || mat->has_texture(TextureSlot::NORMAL) || mat->has_texture(TextureSlot::METALLIC) || mat->has_texture(TextureSlot::ROUGHNESS) || mat->has_texture(TextureSlot::AO) || mat->has_texture(TextureSlot::EMISSION)) {
|
||||||
|
has_textures = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_textures) {
|
||||||
|
build_texture_arrays_(scene);
|
||||||
|
|
||||||
|
// Bind texture arrays
|
||||||
|
glActiveTexture(GL_TEXTURE10);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[0]);
|
||||||
|
glActiveTexture(GL_TEXTURE11);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[1]);
|
||||||
|
glActiveTexture(GL_TEXTURE12);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[2]);
|
||||||
|
glActiveTexture(GL_TEXTURE13);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[3]);
|
||||||
|
glActiveTexture(GL_TEXTURE14);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[4]);
|
||||||
|
glActiveTexture(GL_TEXTURE15);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload scene data (materials now have correct texture indices)
|
||||||
upload_scene_data_(scene);
|
upload_scene_data_(scene);
|
||||||
|
|
||||||
// Use compute shader
|
// Use compute shader
|
||||||
|
|
@ -197,35 +224,8 @@ void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle
|
||||||
compute_shader_->set_bool("u_enable_accumulation", config_.enable_accumulation_);
|
compute_shader_->set_bool("u_enable_accumulation", config_.enable_accumulation_);
|
||||||
|
|
||||||
// Enable/disable textures based on material usage
|
// Enable/disable textures based on material usage
|
||||||
const auto &materials = scene.get_materials();
|
|
||||||
bool has_textures = false;
|
|
||||||
for (const auto &mat : materials) {
|
|
||||||
if (mat->has_texture(TextureSlot::ALBEDO) || mat->has_texture(TextureSlot::NORMAL) || mat->has_texture(TextureSlot::METALLIC) || mat->has_texture(TextureSlot::ROUGHNESS) || mat->has_texture(TextureSlot::AO) || mat->has_texture(TextureSlot::EMISSION)) {
|
|
||||||
has_textures = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compute_shader_->set_bool("u_enable_textures", has_textures);
|
compute_shader_->set_bool("u_enable_textures", has_textures);
|
||||||
|
|
||||||
// Build texture arrays if needed
|
|
||||||
if (has_textures) {
|
|
||||||
build_texture_arrays_(scene);
|
|
||||||
|
|
||||||
// Bind texture arrays
|
|
||||||
glActiveTexture(GL_TEXTURE10);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[0]);
|
|
||||||
glActiveTexture(GL_TEXTURE11);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[1]);
|
|
||||||
glActiveTexture(GL_TEXTURE12);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[2]);
|
|
||||||
glActiveTexture(GL_TEXTURE13);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[3]);
|
|
||||||
glActiveTexture(GL_TEXTURE14);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[4]);
|
|
||||||
glActiveTexture(GL_TEXTURE15);
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_arrays_[5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set camera data
|
// Set camera data
|
||||||
const Camera &camera = scene.get_camera();
|
const Camera &camera = scene.get_camera();
|
||||||
compute_shader_->set_vec3("u_camera_position", camera.get_position());
|
compute_shader_->set_vec3("u_camera_position", camera.get_position());
|
||||||
|
|
@ -323,13 +323,13 @@ void RayTracer::upload_scene_data_(const Scene &scene) {
|
||||||
data.type = static_cast<int>(mat->get_type());
|
data.type = static_cast<int>(mat->get_type());
|
||||||
data.ior = mat->get_ior();
|
data.ior = mat->get_ior();
|
||||||
|
|
||||||
// Texture handles (0 = no texture)
|
// Texture array indices (0 = no texture, 1+ = index into array)
|
||||||
data.texture_handles[0] = mat->get_texture(TextureSlot::ALBEDO) ? mat->get_texture(TextureSlot::ALBEDO)->get_handle() : 0;
|
data.texture_handles[0] = mat->get_texture_index(TextureSlot::ALBEDO);
|
||||||
data.texture_handles[1] = mat->get_texture(TextureSlot::NORMAL) ? mat->get_texture(TextureSlot::NORMAL)->get_handle() : 0;
|
data.texture_handles[1] = mat->get_texture_index(TextureSlot::NORMAL);
|
||||||
data.texture_handles[2] = mat->get_texture(TextureSlot::METALLIC) ? mat->get_texture(TextureSlot::METALLIC)->get_handle() : 0;
|
data.texture_handles[2] = mat->get_texture_index(TextureSlot::METALLIC);
|
||||||
data.texture_handles[3] = mat->get_texture(TextureSlot::ROUGHNESS) ? mat->get_texture(TextureSlot::ROUGHNESS)->get_handle() : 0;
|
data.texture_handles[3] = mat->get_texture_index(TextureSlot::ROUGHNESS);
|
||||||
data.texture_handles[4] = mat->get_texture(TextureSlot::AO) ? mat->get_texture(TextureSlot::AO)->get_handle() : 0;
|
data.texture_handles[4] = mat->get_texture_index(TextureSlot::AO);
|
||||||
data.texture_handles[5] = mat->get_texture(TextureSlot::EMISSION) ? mat->get_texture(TextureSlot::EMISSION)->get_handle() : 0;
|
data.texture_handles[5] = mat->get_texture_index(TextureSlot::EMISSION);
|
||||||
|
|
||||||
material_data.push_back(data);
|
material_data.push_back(data);
|
||||||
}
|
}
|
||||||
|
|
@ -408,6 +408,12 @@ void RayTracer::bind_gbuffer_(const GBuffer &gbuffer) {
|
||||||
|
|
||||||
glBindImageTexture(5, gbuffer.get_texture(GBUFFER_MATERIAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
glBindImageTexture(5, gbuffer.get_texture(GBUFFER_MATERIAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||||
glBindImageTexture(6, gbuffer.get_texture(GBUFFER_MATERIAL_ID), 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
|
glBindImageTexture(6, gbuffer.get_texture(GBUFFER_MATERIAL_ID), 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
|
||||||
|
|
||||||
|
// Texcoord
|
||||||
|
glBindImageTexture(7, gbuffer.get_texture(GBUFFER_TEXCOORD), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||||
|
|
||||||
|
// Tangent
|
||||||
|
glBindImageTexture(8, gbuffer.get_texture(GBUFFER_TANGENT), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayTracer::build_texture_arrays_(const Scene &scene) {
|
void RayTracer::build_texture_arrays_(const Scene &scene) {
|
||||||
|
|
@ -453,19 +459,72 @@ void RayTracer::build_texture_arrays_(const Scene &scene) {
|
||||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, width, height,
|
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, width, height,
|
||||||
static_cast<int>(textures[slot].size()), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
static_cast<int>(textures[slot].size()), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
// Copy each texture to array layer
|
// Copy each texture to array layer and set indices on materials
|
||||||
for (size_t i = 0; i < textures[slot].size(); i++) {
|
for (size_t i = 0; i < textures[slot].size(); i++) {
|
||||||
auto &tex = textures[slot][i];
|
auto &tex = textures[slot][i];
|
||||||
GLuint tex_handle = tex->get_handle();
|
GLuint tex_handle = tex->get_handle();
|
||||||
if (tex_handle != 0) {
|
if (tex_handle != 0) {
|
||||||
// Copy texture data using GetTexImage and CopyTexSubImage3D
|
// Get original texture format
|
||||||
std::vector<uint8_t> pixels(width * height * 4);
|
TextureFormat orig_format = tex->get_format();
|
||||||
|
int orig_width = tex->get_width();
|
||||||
|
int orig_height = tex->get_height();
|
||||||
|
|
||||||
|
// Get the correct format for reading
|
||||||
|
GLenum orig_gl_format = GL_RGBA;
|
||||||
|
int channels = 4;
|
||||||
|
switch (orig_format) {
|
||||||
|
case TextureFormat::R8:
|
||||||
|
orig_gl_format = GL_RED;
|
||||||
|
channels = 1;
|
||||||
|
break;
|
||||||
|
case TextureFormat::RG8:
|
||||||
|
orig_gl_format = GL_RG;
|
||||||
|
channels = 2;
|
||||||
|
break;
|
||||||
|
case TextureFormat::RGB8:
|
||||||
|
orig_gl_format = GL_RGB;
|
||||||
|
channels = 3;
|
||||||
|
break;
|
||||||
|
case TextureFormat::RGBA8:
|
||||||
|
orig_gl_format = GL_RGBA;
|
||||||
|
channels = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
orig_gl_format = GL_RGBA;
|
||||||
|
channels = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read texture data with correct format
|
||||||
|
std::vector<uint8_t> orig_pixels(orig_width * orig_height * channels);
|
||||||
glBindTexture(GL_TEXTURE_2D, tex_handle);
|
glBindTexture(GL_TEXTURE_2D, tex_handle);
|
||||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
glGetTexImage(GL_TEXTURE_2D, 0, orig_gl_format, GL_UNSIGNED_BYTE, orig_pixels.data());
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
// Convert to RGBA for texture array (always RGBA8)
|
||||||
|
std::vector<uint8_t> pixels(orig_width * orig_height * 4, 255);
|
||||||
|
for (int y = 0; y < orig_height; y++) {
|
||||||
|
for (int x = 0; x < orig_width; x++) {
|
||||||
|
int src_idx = (y * orig_width + x) * channels;
|
||||||
|
int dst_idx = (y * orig_width + x) * 4;
|
||||||
|
pixels[dst_idx + 0] = orig_pixels[src_idx + 0];
|
||||||
|
pixels[dst_idx + 1] = (channels >= 2) ? orig_pixels[src_idx + 1] : orig_pixels[src_idx + 0];
|
||||||
|
pixels[dst_idx + 2] = (channels >= 3) ? orig_pixels[src_idx + 2] : orig_pixels[src_idx + 0];
|
||||||
|
pixels[dst_idx + 3] = (channels >= 4) ? orig_pixels[src_idx + 3] : 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, static_cast<int>(i),
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, static_cast<int>(i),
|
||||||
width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
|
||||||
|
// Set texture index on all materials using this texture
|
||||||
|
// Index is i+1 because 0 means "no texture" in the shader
|
||||||
|
uint32_t array_index = static_cast<uint32_t>(i) + 1;
|
||||||
|
for (const auto &mat : materials) {
|
||||||
|
if (mat->get_texture(static_cast<TextureSlot>(slot)).get() == tex.get()) {
|
||||||
|
mat->set_texture_index(static_cast<TextureSlot>(slot), array_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ Material::Material()
|
||||||
, roughness_(0.5f)
|
, roughness_(0.5f)
|
||||||
, ior_(1.5f)
|
, ior_(1.5f)
|
||||||
, type_(MaterialType::DIFFUSE)
|
, type_(MaterialType::DIFFUSE)
|
||||||
, textures_() {
|
, textures_()
|
||||||
|
, texture_indices_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Material::~Material() {
|
Material::~Material() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue