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)
|
||||
auto textured_material = std::make_shared<Material>();
|
||||
textured_material->set_albedo(Vec3(1.0f, 1.0f, 1.0f));
|
||||
textured_material->set_metallic(0.0f);
|
||||
textured_material->set_roughness(0.8f);
|
||||
textured_material->set_type(MaterialType::DIFFUSE);
|
||||
textured_material->set_metallic(1.0f);
|
||||
textured_material->set_roughness(1.0f);
|
||||
// textured_material->set_type(MaterialType::DIFFUSE);
|
||||
textured_material->set_type(MaterialType::METAL);
|
||||
|
||||
// Load textures
|
||||
auto albedo_tex = std::make_shared<are::Texture>();
|
||||
if (albedo_tex->load_from_file("examples/assets/normal_map_cornell_box/albedo.png")) {
|
||||
textured_material->set_albedo_texture(albedo_tex);
|
||||
ARE_LOG_INFO("Loaded albedo texture");
|
||||
} else {
|
||||
ARE_LOG_WARN("Failed to load albedo texture");
|
||||
}
|
||||
// auto albedo_tex = std::make_shared<are::Texture>();
|
||||
// if (albedo_tex->load_from_file("examples/assets/normal_map_cornell_box/albedo.png")) {
|
||||
// textured_material->set_albedo_texture(albedo_tex);
|
||||
// ARE_LOG_INFO("Loaded albedo texture");
|
||||
// } else {
|
||||
// ARE_LOG_WARN("Failed to load albedo texture");
|
||||
// }
|
||||
|
||||
auto normal_tex = std::make_shared<are::Texture>();
|
||||
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(0.0f, 0.0f, 1.0f),
|
||||
white_id);
|
||||
metal_id);
|
||||
// white_id);
|
||||
back_wall->upload_to_gpu();
|
||||
g_scene->add_mesh(back_wall);
|
||||
|
||||
|
|
@ -361,7 +363,7 @@ void setup_cornell_box() {
|
|||
g_camera->set_position(g_cameraPos);
|
||||
g_camera->set_target(g_cameraTarget);
|
||||
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);
|
||||
|
||||
// Add point light
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ constexpr int GBUFFER_NORMAL = 1;
|
|||
constexpr int GBUFFER_ALBEDO = 2;
|
||||
constexpr int GBUFFER_MATERIAL = 3;
|
||||
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
|
||||
constexpr int COMPUTE_GROUP_SIZE_X = 16;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ struct Triangle {
|
|||
Vec3 v0_, v1_, v2_;
|
||||
Vec3 n0_, n1_, n2_;
|
||||
Vec2 uv0_, uv1_, uv2_;
|
||||
Vec3 t0_, t1_, t2_; // Tangents for each vertex
|
||||
uint material_id_;
|
||||
|
||||
// Get bounding box of triangle
|
||||
|
|
@ -75,6 +76,8 @@ struct TriangleGpu {
|
|||
Vec4 n2_; ///< xyz = n2, w = reserved
|
||||
Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1
|
||||
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
|
||||
|
|
|
|||
|
|
@ -229,6 +229,24 @@ public:
|
|||
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:
|
||||
Vec3 albedo_;
|
||||
Vec3 emission_;
|
||||
|
|
@ -238,6 +256,7 @@ private:
|
|||
MaterialType type_;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ layout(location = 1) out vec4 g_normal;
|
|||
layout(location = 2) out vec4 g_albedo;
|
||||
layout(location = 3) out vec4 g_material;
|
||||
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 float u_metallic;
|
||||
|
|
@ -38,4 +40,10 @@ void main() {
|
|||
|
||||
g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type));
|
||||
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 = 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
|
||||
layout(binding = 3, rgba32f) uniform image2D output_image;
|
||||
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||
|
|
@ -64,6 +70,7 @@ struct HitInfo {
|
|||
vec3 position;
|
||||
vec3 normal;
|
||||
vec2 texcoord;
|
||||
vec3 tangent;
|
||||
uint material_id;
|
||||
int material_type; // material type from G-Buffer
|
||||
};
|
||||
|
|
@ -88,6 +95,8 @@ struct TriangleGpu {
|
|||
vec4 n2;
|
||||
vec4 uv0_uv1; // xy uv0, zw uv1
|
||||
vec4 uv2; // xy uv2
|
||||
vec4 t0; // tangent at v0
|
||||
vec4 t1; // tangent at v1
|
||||
};
|
||||
|
||||
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 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.t = t;
|
||||
hit.position = ray.origin + t * ray.direction;
|
||||
hit.normal = normalize(n0 * w + n1 * u + n2 * 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);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -378,6 +397,7 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
hit.position = vec3(0.0);
|
||||
hit.normal = vec3(0.0, 1.0, 0.0);
|
||||
hit.texcoord = vec2(0.0);
|
||||
hit.tangent = vec3(0.0);
|
||||
hit.material_id = 0u;
|
||||
hit.material_type = 0;
|
||||
|
||||
|
|
@ -395,10 +415,20 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
// material type stored in g_material.w
|
||||
vec4 mat = imageLoad(g_material, pixel_coords);
|
||||
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.position = p;
|
||||
hit.normal = n;
|
||||
hit.texcoord = texcoord;
|
||||
hit.tangent = tangent;
|
||||
hit.material_id = mid;
|
||||
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
|
||||
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;
|
||||
|
||||
// Albedo texture
|
||||
|
|
@ -556,7 +586,7 @@ ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed
|
|||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Apply PBR textures
|
||||
vec3 tangent0 = normalize(cross(hit0.normal, vec3(0.0, 1.0, 0.0)));
|
||||
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);
|
||||
// Apply PBR textures (use tangent from G-Buffer if available)
|
||||
apply_material_textures(mat0, hit0.normal, hit0.texcoord, hit0.tangent);
|
||||
|
||||
radiance += throughput * mat0.emission;
|
||||
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);
|
||||
|
||||
// Apply PBR textures
|
||||
vec3 tangent = normalize(cross(hit.normal, vec3(0.0, 1.0, 0.0)));
|
||||
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);
|
||||
// Apply PBR textures (use tangent from intersection)
|
||||
apply_material_textures(mat, hit.normal, hit.texcoord, hit.tangent);
|
||||
|
||||
radiance += throughput * mat.emission;
|
||||
if (mat.type == MATERIAL_DIFFUSE) {
|
||||
|
|
|
|||
541
src/core/bvh.cpp
541
src/core/bvh.cpp
|
|
@ -1,41 +1,41 @@
|
|||
#include "core/bvh.h"
|
||||
#include "utils/logger.h"
|
||||
#include "basic/constants.h"
|
||||
#include "utils/logger.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace are {
|
||||
|
||||
// AABB implementation
|
||||
void AABB::expand(const Vec3& point) {
|
||||
min_ = glm::min(min_, point);
|
||||
max_ = glm::max(max_, point);
|
||||
void AABB::expand(const Vec3 &point) {
|
||||
min_ = glm::min(min_, point);
|
||||
max_ = glm::max(max_, point);
|
||||
}
|
||||
|
||||
void AABB::expand(const AABB& other) {
|
||||
min_ = glm::min(min_, other.min_);
|
||||
max_ = glm::max(max_, other.max_);
|
||||
void AABB::expand(const AABB &other) {
|
||||
min_ = glm::min(min_, other.min_);
|
||||
max_ = glm::max(max_, other.max_);
|
||||
}
|
||||
|
||||
float AABB::surface_area() const {
|
||||
Vec3 extent = max_ - min_;
|
||||
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
|
||||
Vec3 extent = max_ - min_;
|
||||
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
|
||||
}
|
||||
|
||||
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
|
||||
AABB Triangle::get_bounds() const {
|
||||
AABB bounds(v0_, v0_);
|
||||
bounds.expand(v1_);
|
||||
bounds.expand(v2_);
|
||||
return bounds;
|
||||
AABB bounds(v0_, v0_);
|
||||
bounds.expand(v1_);
|
||||
bounds.expand(v2_);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Vec3 Triangle::get_centroid() const {
|
||||
return (v0_ + v1_ + v2_) / 3.0f;
|
||||
return (v0_ + v1_ + v2_) / 3.0f;
|
||||
}
|
||||
|
||||
// BVH implementation
|
||||
|
|
@ -43,290 +43,299 @@ BVH::BVH() {
|
|||
}
|
||||
|
||||
BVH::~BVH() {
|
||||
clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
bool BVH::build(const std::vector<std::shared_ptr<Mesh>>& meshes) {
|
||||
clear();
|
||||
|
||||
ARE_LOG_INFO("Building BVH...");
|
||||
|
||||
// Extract all triangles from meshes
|
||||
for (const auto& mesh : meshes) {
|
||||
const auto& vertices = mesh->get_vertices();
|
||||
const auto& indices = mesh->get_indices();
|
||||
uint material_id = mesh->get_material();
|
||||
Mat4 transform = mesh->get_transform();
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i += 3) {
|
||||
Triangle tri;
|
||||
|
||||
// Transform vertices
|
||||
Vec4 v0 = transform * Vec4(vertices[indices[i]].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);
|
||||
|
||||
tri.v0_ = Vec3(v0) / v0.w;
|
||||
tri.v1_ = Vec3(v1) / v1.w;
|
||||
tri.v2_ = Vec3(v2) / v2.w;
|
||||
|
||||
// Transform normals
|
||||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
|
||||
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
|
||||
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
|
||||
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
|
||||
|
||||
// Copy UVs
|
||||
tri.uv0_ = vertices[indices[i]].texcoord_;
|
||||
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
|
||||
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
|
||||
|
||||
tri.material_id_ = material_id;
|
||||
|
||||
triangles_.push_back(tri);
|
||||
}
|
||||
}
|
||||
|
||||
if (triangles_.empty()) {
|
||||
ARE_LOG_WARN("No triangles to build BVH");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize triangle indices
|
||||
triangle_indices_.resize(triangles_.size());
|
||||
for (size_t i = 0; i < triangles_.size(); ++i) {
|
||||
triangle_indices_[i] = static_cast<uint>(i);
|
||||
}
|
||||
|
||||
// Reserve space for nodes (estimate)
|
||||
nodes_.reserve(triangles_.size() * 2);
|
||||
|
||||
// Create root node
|
||||
nodes_.emplace_back();
|
||||
|
||||
// Build BVH recursively
|
||||
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
|
||||
|
||||
ARE_LOG_INFO("BVH built: " + std::to_string(nodes_.size()) + " nodes, " +
|
||||
std::to_string(triangles_.size()) + " triangles");
|
||||
|
||||
return true;
|
||||
bool BVH::build(const std::vector<std::shared_ptr<Mesh>> &meshes) {
|
||||
clear();
|
||||
|
||||
ARE_LOG_INFO("Building BVH...");
|
||||
|
||||
// Extract all triangles from meshes
|
||||
for (const auto &mesh : meshes) {
|
||||
const auto &vertices = mesh->get_vertices();
|
||||
const auto &indices = mesh->get_indices();
|
||||
uint material_id = mesh->get_material();
|
||||
Mat4 transform = mesh->get_transform();
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i += 3) {
|
||||
Triangle tri;
|
||||
|
||||
// Transform vertices
|
||||
Vec4 v0 = transform * Vec4(vertices[indices[i]].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);
|
||||
|
||||
tri.v0_ = Vec3(v0) / v0.w;
|
||||
tri.v1_ = Vec3(v1) / v1.w;
|
||||
tri.v2_ = Vec3(v2) / v2.w;
|
||||
|
||||
// Transform normals
|
||||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
|
||||
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
|
||||
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
|
||||
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
|
||||
|
||||
// Transform tangents
|
||||
tri.t0_ = glm::normalize(normal_matrix * vertices[indices[i]].tangent_);
|
||||
tri.t1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].tangent_);
|
||||
tri.t2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].tangent_);
|
||||
|
||||
// Copy UVs
|
||||
tri.uv0_ = vertices[indices[i]].texcoord_;
|
||||
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
|
||||
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
|
||||
|
||||
tri.material_id_ = material_id;
|
||||
|
||||
triangles_.push_back(tri);
|
||||
}
|
||||
}
|
||||
|
||||
if (triangles_.empty()) {
|
||||
ARE_LOG_WARN("No triangles to build BVH");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize triangle indices
|
||||
triangle_indices_.resize(triangles_.size());
|
||||
for (size_t i = 0; i < triangles_.size(); ++i) {
|
||||
triangle_indices_[i] = static_cast<uint>(i);
|
||||
}
|
||||
|
||||
// Reserve space for nodes (estimate)
|
||||
nodes_.reserve(triangles_.size() * 2);
|
||||
|
||||
// Create root node
|
||||
nodes_.emplace_back();
|
||||
|
||||
// Build BVH recursively
|
||||
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
|
||||
|
||||
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) {
|
||||
BVHNode& node = nodes_[node_idx];
|
||||
|
||||
// Calculate bounds
|
||||
AABB bounds = calculate_bounds_(first_prim, prim_count);
|
||||
node.aabb_min_ = bounds.min_;
|
||||
node.aabb_max_ = bounds.max_;
|
||||
|
||||
// Leaf node threshold
|
||||
const uint LEAF_SIZE = 4;
|
||||
|
||||
if (prim_count <= LEAF_SIZE) {
|
||||
// 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()) {
|
||||
BVHNode &node = nodes_[node_idx];
|
||||
|
||||
// Calculate bounds
|
||||
AABB bounds = calculate_bounds_(first_prim, prim_count);
|
||||
node.aabb_min_ = bounds.min_;
|
||||
node.aabb_max_ = bounds.max_;
|
||||
|
||||
// Leaf node threshold
|
||||
const uint LEAF_SIZE = 4;
|
||||
|
||||
if (prim_count <= LEAF_SIZE) {
|
||||
// Create leaf node
|
||||
node.left_first_ = first_prim;
|
||||
node.count_ = prim_count;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if split is beneficial
|
||||
float no_split_cost = prim_count * bounds.surface_area();
|
||||
if (split_cost >= no_split_cost) {
|
||||
// Create leaf node
|
||||
node.left_first_ = first_prim;
|
||||
node.count_ = prim_count;
|
||||
return;
|
||||
}
|
||||
|
||||
// Partition primitives
|
||||
uint mid = first_prim;
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||||
float centroid = tri.get_centroid()[axis];
|
||||
|
||||
if (centroid < split_pos) {
|
||||
std::swap(triangle_indices_[i], triangle_indices_[mid]);
|
||||
mid++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have primitives on both sides
|
||||
if (mid == first_prim || mid == first_prim + prim_count) {
|
||||
mid = first_prim + prim_count / 2;
|
||||
}
|
||||
|
||||
// Create interior node
|
||||
uint left_count = mid - first_prim;
|
||||
uint right_count = prim_count - left_count;
|
||||
|
||||
node.left_first_ = static_cast<uint>(nodes_.size());
|
||||
node.count_ = 0;
|
||||
|
||||
// Create child nodes
|
||||
nodes_.emplace_back();
|
||||
nodes_.emplace_back();
|
||||
|
||||
// Recursively build children
|
||||
build_recursive_(node.left_first_, first_prim, left_count);
|
||||
build_recursive_(node.left_first_ + 1, mid, right_count);
|
||||
|
||||
// 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.count_ = prim_count;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if split is beneficial
|
||||
float no_split_cost = prim_count * bounds.surface_area();
|
||||
if (split_cost >= no_split_cost) {
|
||||
// Create leaf node
|
||||
node.left_first_ = first_prim;
|
||||
node.count_ = prim_count;
|
||||
return;
|
||||
}
|
||||
|
||||
// Partition primitives
|
||||
uint mid = first_prim;
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||
float centroid = tri.get_centroid()[axis];
|
||||
|
||||
if (centroid < split_pos) {
|
||||
std::swap(triangle_indices_[i], triangle_indices_[mid]);
|
||||
mid++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have primitives on both sides
|
||||
if (mid == first_prim || mid == first_prim + prim_count) {
|
||||
mid = first_prim + prim_count / 2;
|
||||
}
|
||||
|
||||
// Create interior node
|
||||
uint left_count = mid - first_prim;
|
||||
uint right_count = prim_count - left_count;
|
||||
|
||||
node.left_first_ = static_cast<uint>(nodes_.size());
|
||||
node.count_ = 0;
|
||||
|
||||
// Create child nodes
|
||||
nodes_.emplace_back();
|
||||
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 best_cost = std::numeric_limits<float>::max();
|
||||
float BVH::find_best_split_(uint first_prim, uint prim_count, int &axis, float &split_pos) {
|
||||
float best_cost = std::numeric_limits<float>::max();
|
||||
axis = 0, split_pos = 0.0f;
|
||||
|
||||
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
|
||||
|
||||
// Try each axis
|
||||
for (int a = 0; a < 3; ++a) {
|
||||
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
|
||||
if (extent < EPSILON) continue;
|
||||
|
||||
// Try multiple split positions
|
||||
const int NUM_BINS = 16;
|
||||
for (int i = 1; i < NUM_BINS; ++i) {
|
||||
float t = static_cast<float>(i) / NUM_BINS;
|
||||
float pos = centroid_bounds.min_[a] + t * extent;
|
||||
|
||||
// Count primitives and calculate bounds for each side
|
||||
AABB left_bounds, right_bounds;
|
||||
uint left_count = 0, right_count = 0;
|
||||
|
||||
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
|
||||
Triangle& tri = triangles_[triangle_indices_[j]];
|
||||
float centroid = tri.get_centroid()[a];
|
||||
|
||||
if (centroid < pos) {
|
||||
left_bounds.expand(tri.get_bounds());
|
||||
left_count++;
|
||||
} else {
|
||||
right_bounds.expand(tri.get_bounds());
|
||||
right_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate SAH cost
|
||||
if (left_count == 0 || right_count == 0) continue;
|
||||
|
||||
float cost = left_count * left_bounds.surface_area() +
|
||||
right_count * right_bounds.surface_area();
|
||||
|
||||
if (cost < best_cost) {
|
||||
best_cost = cost;
|
||||
axis = a;
|
||||
split_pos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_cost;
|
||||
|
||||
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
|
||||
|
||||
// Try each axis
|
||||
for (int a = 0; a < 3; ++a) {
|
||||
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
|
||||
if (extent < EPSILON)
|
||||
continue;
|
||||
|
||||
// Try multiple split positions
|
||||
const int NUM_BINS = 16;
|
||||
for (int i = 1; i < NUM_BINS; ++i) {
|
||||
float t = static_cast<float>(i) / NUM_BINS;
|
||||
float pos = centroid_bounds.min_[a] + t * extent;
|
||||
|
||||
// Count primitives and calculate bounds for each side
|
||||
AABB left_bounds, right_bounds;
|
||||
uint left_count = 0, right_count = 0;
|
||||
|
||||
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
|
||||
Triangle &tri = triangles_[triangle_indices_[j]];
|
||||
float centroid = tri.get_centroid()[a];
|
||||
|
||||
if (centroid < pos) {
|
||||
left_bounds.expand(tri.get_bounds());
|
||||
left_count++;
|
||||
} else {
|
||||
right_bounds.expand(tri.get_bounds());
|
||||
right_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate SAH cost
|
||||
if (left_count == 0 || right_count == 0)
|
||||
continue;
|
||||
|
||||
float cost = left_count * left_bounds.surface_area() + right_count * right_bounds.surface_area();
|
||||
|
||||
if (cost < best_cost) {
|
||||
best_cost = cost;
|
||||
axis = a;
|
||||
split_pos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_cost;
|
||||
}
|
||||
|
||||
AABB BVH::calculate_bounds_(uint first_prim, uint prim_count) {
|
||||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
||||
Vec3(std::numeric_limits<float>::lowest())};
|
||||
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||||
bounds.expand(tri.get_bounds());
|
||||
}
|
||||
|
||||
return bounds;
|
||||
AABB bounds { Vec3(std::numeric_limits<float>::max()),
|
||||
Vec3(std::numeric_limits<float>::lowest()) };
|
||||
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||
bounds.expand(tri.get_bounds());
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
AABB BVH::calculate_centroid_bounds_(uint first_prim, uint prim_count) {
|
||||
AABB bounds{Vec3(std::numeric_limits<float>::max()),
|
||||
Vec3(std::numeric_limits<float>::lowest())};
|
||||
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle& tri = triangles_[triangle_indices_[i]];
|
||||
bounds.expand(tri.get_centroid());
|
||||
}
|
||||
|
||||
return bounds;
|
||||
AABB bounds { Vec3(std::numeric_limits<float>::max()),
|
||||
Vec3(std::numeric_limits<float>::lowest()) };
|
||||
|
||||
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
|
||||
Triangle &tri = triangles_[triangle_indices_[i]];
|
||||
bounds.expand(tri.get_centroid());
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) {
|
||||
if (nodes_.empty() || triangles_.empty()) {
|
||||
ARE_LOG_ERROR("Cannot upload empty BVH to GPU");
|
||||
return false;
|
||||
}
|
||||
bool BVH::upload_to_gpu(Buffer &node_buffer, Buffer &triangle_buffer) {
|
||||
if (nodes_.empty() || triangles_.empty()) {
|
||||
ARE_LOG_ERROR("Cannot upload empty BVH to GPU");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reorder triangles according to BVH layout
|
||||
std::vector<Triangle> ordered_triangles;
|
||||
ordered_triangles.reserve(triangles_.size());
|
||||
for (uint idx : triangle_indices_) {
|
||||
ordered_triangles.push_back(triangles_[idx]);
|
||||
}
|
||||
// Reorder triangles according to BVH layout
|
||||
std::vector<Triangle> ordered_triangles;
|
||||
ordered_triangles.reserve(triangles_.size());
|
||||
for (uint idx : triangle_indices_) {
|
||||
ordered_triangles.push_back(triangles_[idx]);
|
||||
}
|
||||
|
||||
// Pack nodes to GPU layout
|
||||
std::vector<BVHNodeGpu> node_gpu;
|
||||
node_gpu.resize(nodes_.size());
|
||||
for (size_t i = 0; i < nodes_.size(); ++i) {
|
||||
const BVHNode& n = nodes_[i];
|
||||
BVHNodeGpu g;
|
||||
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_));
|
||||
node_gpu[i] = g;
|
||||
}
|
||||
// Pack nodes to GPU layout
|
||||
std::vector<BVHNodeGpu> node_gpu;
|
||||
node_gpu.resize(nodes_.size());
|
||||
for (size_t i = 0; i < nodes_.size(); ++i) {
|
||||
const BVHNode &n = nodes_[i];
|
||||
BVHNodeGpu g;
|
||||
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_));
|
||||
node_gpu[i] = g;
|
||||
}
|
||||
|
||||
// Pack triangles to GPU layout
|
||||
std::vector<TriangleGpu> tri_gpu;
|
||||
tri_gpu.resize(ordered_triangles.size());
|
||||
for (size_t i = 0; i < ordered_triangles.size(); ++i) {
|
||||
const Triangle& t = ordered_triangles[i];
|
||||
// Pack triangles to GPU layout
|
||||
std::vector<TriangleGpu> tri_gpu;
|
||||
tri_gpu.resize(ordered_triangles.size());
|
||||
for (size_t i = 0; i < ordered_triangles.size(); ++i) {
|
||||
const Triangle &t = ordered_triangles[i];
|
||||
|
||||
TriangleGpu g{};
|
||||
g.v0_material_ = Vec4(t.v0_, glm::uintBitsToFloat(t.material_id_));
|
||||
g.v1_ = Vec4(t.v1_, 0.0f);
|
||||
g.v2_ = Vec4(t.v2_, 0.0f);
|
||||
TriangleGpu g {};
|
||||
g.v0_material_ = Vec4(t.v0_, glm::uintBitsToFloat(t.material_id_));
|
||||
g.v1_ = Vec4(t.v1_, 0.0f);
|
||||
g.v2_ = Vec4(t.v2_, 0.0f);
|
||||
|
||||
g.n0_ = Vec4(t.n0_, 0.0f);
|
||||
g.n1_ = Vec4(t.n1_, 0.0f);
|
||||
g.n2_ = Vec4(t.n2_, 0.0f);
|
||||
g.n0_ = Vec4(t.n0_, 0.0f);
|
||||
g.n1_ = Vec4(t.n1_, 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.uv2_ = Vec4(t.uv2_.x, t.uv2_.y, 0.0f, 0.0f);
|
||||
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);
|
||||
|
||||
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,
|
||||
node_gpu.size() * sizeof(BVHNodeGpu),
|
||||
node_gpu.data(),
|
||||
BufferUsage::STATIC_DRAW)) {
|
||||
ARE_LOG_ERROR("Failed to upload BVH nodes to GPU");
|
||||
return false;
|
||||
}
|
||||
tri_gpu[i] = g;
|
||||
}
|
||||
|
||||
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||
tri_gpu.size() * sizeof(TriangleGpu),
|
||||
tri_gpu.data(),
|
||||
BufferUsage::STATIC_DRAW)) {
|
||||
ARE_LOG_ERROR("Failed to upload BVH triangles to GPU");
|
||||
return false;
|
||||
}
|
||||
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||
node_gpu.size() * sizeof(BVHNodeGpu),
|
||||
node_gpu.data(),
|
||||
BufferUsage::STATIC_DRAW)) {
|
||||
ARE_LOG_ERROR("Failed to upload BVH nodes to GPU");
|
||||
return false;
|
||||
}
|
||||
|
||||
ARE_LOG_INFO("BVH uploaded to GPU successfully");
|
||||
return true;
|
||||
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||
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() {
|
||||
nodes_.clear();
|
||||
triangles_.clear();
|
||||
triangle_indices_.clear();
|
||||
nodes_.clear();
|
||||
triangles_.clear();
|
||||
triangle_indices_.clear();
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -5,226 +5,240 @@
|
|||
namespace are {
|
||||
|
||||
GBuffer::GBuffer(uint width, uint height)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, fbo_(INVALID_HANDLE)
|
||||
, depth_texture_(INVALID_HANDLE)
|
||||
, initialized_(false) {
|
||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||
textures_[i] = INVALID_HANDLE;
|
||||
}
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, fbo_(INVALID_HANDLE)
|
||||
, depth_texture_(INVALID_HANDLE)
|
||||
, initialized_(false) {
|
||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||
textures_[i] = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
GBuffer::~GBuffer() {
|
||||
release();
|
||||
release();
|
||||
}
|
||||
|
||||
bool GBuffer::initialize() {
|
||||
if (initialized_) {
|
||||
ARE_LOG_WARN("GBuffer already initialized");
|
||||
return true;
|
||||
}
|
||||
if (initialized_) {
|
||||
ARE_LOG_WARN("GBuffer already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
textures_[GBUFFER_POSITION] = 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_POSITION] = 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);
|
||||
|
||||
// New: material params (metallic, roughness, ior, type)
|
||||
textures_[GBUFFER_MATERIAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
// New: material params (metallic, roughness, ior, type)
|
||||
textures_[GBUFFER_MATERIAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
|
||||
// New: material id (integer)
|
||||
textures_[GBUFFER_MATERIAL_ID] = create_texture_(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT);
|
||||
// New: material id (integer)
|
||||
textures_[GBUFFER_MATERIAL_ID] = create_texture_(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
||||
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);
|
||||
// New: texcoord + tangent (xy = texcoord, zw = tangent)
|
||||
textures_[GBUFFER_TEXCOORD] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
|
||||
glGenTextures(1, &depth_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||||
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);
|
||||
// New: tangent (xyz = tangent, w = unused)
|
||||
textures_[GBUFFER_TANGENT] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
|
||||
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
|
||||
};
|
||||
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
||||
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);
|
||||
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) {
|
||||
ARE_LOG_ERROR("GBuffer framebuffer is not complete");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return false;
|
||||
}
|
||||
glGenTextures(1, &depth_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||||
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);
|
||||
|
||||
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;
|
||||
ARE_LOG_INFO("GBuffer initialized successfully");
|
||||
return true;
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ARE_LOG_ERROR("GBuffer framebuffer is not complete");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
initialized_ = true;
|
||||
ARE_LOG_INFO("GBuffer initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GBuffer::release() {
|
||||
if (!initialized_) return;
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
if (fbo_ != INVALID_HANDLE) {
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
fbo_ = INVALID_HANDLE;
|
||||
}
|
||||
if (fbo_ != INVALID_HANDLE) {
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
fbo_ = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||
if (textures_[i] != INVALID_HANDLE) {
|
||||
glDeleteTextures(1, &textures_[i]);
|
||||
textures_[i] = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < GBUFFER_COUNT; ++i) {
|
||||
if (textures_[i] != INVALID_HANDLE) {
|
||||
glDeleteTextures(1, &textures_[i]);
|
||||
textures_[i] = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_texture_ != INVALID_HANDLE) {
|
||||
glDeleteTextures(1, &depth_texture_);
|
||||
depth_texture_ = INVALID_HANDLE;
|
||||
}
|
||||
if (depth_texture_ != INVALID_HANDLE) {
|
||||
glDeleteTextures(1, &depth_texture_);
|
||||
depth_texture_ = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
ARE_LOG_INFO("GBuffer released");
|
||||
initialized_ = false;
|
||||
ARE_LOG_INFO("GBuffer released");
|
||||
}
|
||||
|
||||
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
||||
TextureHandle texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
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_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
TextureHandle texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
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_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void GBuffer::render(const Scene& scene, const Shader& shader) {
|
||||
if (!initialized_) {
|
||||
ARE_LOG_ERROR("GBuffer not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
glViewport(0, 0, width_, height_);
|
||||
|
||||
// Clear buffers
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Enable depth test
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Use shader
|
||||
shader.use();
|
||||
|
||||
// Set camera matrices
|
||||
const Camera& camera = scene.get_camera();
|
||||
Mat4 view = camera.get_view_matrix();
|
||||
Mat4 projection = camera.get_projection_matrix();
|
||||
|
||||
shader.set_mat4("u_view", view);
|
||||
shader.set_mat4("u_projection", projection);
|
||||
|
||||
// Render all meshes
|
||||
const auto& meshes = scene.get_meshes();
|
||||
const auto& materials = scene.get_materials();
|
||||
|
||||
for (const auto& mesh : meshes) {
|
||||
if (!mesh->is_uploaded()) {
|
||||
ARE_LOG_WARN("Mesh not uploaded to GPU, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set model matrix
|
||||
Mat4 model = mesh->get_transform();
|
||||
shader.set_mat4("u_model", model);
|
||||
|
||||
// Set material properties
|
||||
uint material_id = mesh->get_material();
|
||||
if (material_id < materials.size()) {
|
||||
const auto& material = materials[material_id];
|
||||
|
||||
shader.set_vec3("u_albedo", material->get_albedo());
|
||||
shader.set_float("u_metallic", material->get_metallic());
|
||||
shader.set_float("u_roughness", material->get_roughness());
|
||||
shader.set_uint("u_material_id", material_id);
|
||||
void GBuffer::render(const Scene &scene, const Shader &shader) {
|
||||
if (!initialized_) {
|
||||
ARE_LOG_ERROR("GBuffer not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
glViewport(0, 0, width_, height_);
|
||||
|
||||
// Clear buffers
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Enable depth test
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Use shader
|
||||
shader.use();
|
||||
|
||||
// Set camera matrices
|
||||
const Camera &camera = scene.get_camera();
|
||||
Mat4 view = camera.get_view_matrix();
|
||||
Mat4 projection = camera.get_projection_matrix();
|
||||
|
||||
shader.set_mat4("u_view", view);
|
||||
shader.set_mat4("u_projection", projection);
|
||||
|
||||
// Render all meshes
|
||||
const auto &meshes = scene.get_meshes();
|
||||
const auto &materials = scene.get_materials();
|
||||
|
||||
for (const auto &mesh : meshes) {
|
||||
if (!mesh->is_uploaded()) {
|
||||
ARE_LOG_WARN("Mesh not uploaded to GPU, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set model matrix
|
||||
Mat4 model = mesh->get_transform();
|
||||
shader.set_mat4("u_model", model);
|
||||
|
||||
// Set material properties
|
||||
uint material_id = mesh->get_material();
|
||||
if (material_id < materials.size()) {
|
||||
const auto &material = materials[material_id];
|
||||
|
||||
shader.set_vec3("u_albedo", material->get_albedo());
|
||||
shader.set_float("u_metallic", material->get_metallic());
|
||||
shader.set_float("u_roughness", material->get_roughness());
|
||||
shader.set_uint("u_material_id", material_id);
|
||||
shader.set_float("u_ior", material->get_ior());
|
||||
shader.set_vec3("u_emission", material->get_emission());
|
||||
shader.set_uint("u_material_type", static_cast<uint>(material->get_type()));
|
||||
|
||||
// Bind textures
|
||||
auto albedo_tex = material->get_albedo_texture();
|
||||
if (albedo_tex && albedo_tex->is_valid()) {
|
||||
albedo_tex->bind(0);
|
||||
shader.set_int("u_albedo_map", 0);
|
||||
shader.set_int("u_has_albedo_map", 1);
|
||||
} else {
|
||||
shader.set_int("u_has_albedo_map", 0);
|
||||
}
|
||||
|
||||
auto normal_tex = material->get_normal_texture();
|
||||
if (normal_tex && normal_tex->is_valid()) {
|
||||
normal_tex->bind(1);
|
||||
shader.set_int("u_normal_map", 1);
|
||||
shader.set_int("u_has_normal_map", 1);
|
||||
} else {
|
||||
shader.set_int("u_has_normal_map", 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw mesh
|
||||
glBindVertexArray(mesh->get_vao());
|
||||
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// Unbind framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Bind textures
|
||||
auto albedo_tex = material->get_albedo_texture();
|
||||
if (albedo_tex && albedo_tex->is_valid()) {
|
||||
albedo_tex->bind(0);
|
||||
shader.set_int("u_albedo_map", 0);
|
||||
shader.set_int("u_has_albedo_map", 1);
|
||||
} else {
|
||||
shader.set_int("u_has_albedo_map", 0);
|
||||
}
|
||||
|
||||
auto normal_tex = material->get_normal_texture();
|
||||
if (normal_tex && normal_tex->is_valid()) {
|
||||
normal_tex->bind(1);
|
||||
shader.set_int("u_normal_map", 1);
|
||||
shader.set_int("u_has_normal_map", 1);
|
||||
} else {
|
||||
shader.set_int("u_has_normal_map", 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw mesh
|
||||
glBindVertexArray(mesh->get_vao());
|
||||
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// Unbind framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void GBuffer::resize(uint width, uint height) {
|
||||
if (width == width_ && height == height_) return;
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
if (initialized_) {
|
||||
release();
|
||||
initialize();
|
||||
}
|
||||
if (width == width_ && height == height_)
|
||||
return;
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
if (initialized_) {
|
||||
release();
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
TextureHandle GBuffer::get_texture(int index) const {
|
||||
if (index < 0 || index >= GBUFFER_COUNT) {
|
||||
ARE_LOG_ERROR("Invalid G-Buffer texture index");
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
return textures_[index];
|
||||
if (index < 0 || index >= GBUFFER_COUNT) {
|
||||
ARE_LOG_ERROR("Invalid G-Buffer texture index");
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
return textures_[index];
|
||||
}
|
||||
|
||||
void GBuffer::get_dimensions(uint& width, uint& height) const {
|
||||
width = width_;
|
||||
height = height_;
|
||||
void GBuffer::get_dimensions(uint &width, uint &height) const {
|
||||
width = width_;
|
||||
height = height_;
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -162,7 +162,34 @@ void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle
|
|||
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);
|
||||
|
||||
// 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_);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
const Camera &camera = scene.get_camera();
|
||||
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.ior = mat->get_ior();
|
||||
|
||||
// Texture handles (0 = no texture)
|
||||
data.texture_handles[0] = mat->get_texture(TextureSlot::ALBEDO) ? mat->get_texture(TextureSlot::ALBEDO)->get_handle() : 0;
|
||||
data.texture_handles[1] = mat->get_texture(TextureSlot::NORMAL) ? mat->get_texture(TextureSlot::NORMAL)->get_handle() : 0;
|
||||
data.texture_handles[2] = mat->get_texture(TextureSlot::METALLIC) ? mat->get_texture(TextureSlot::METALLIC)->get_handle() : 0;
|
||||
data.texture_handles[3] = mat->get_texture(TextureSlot::ROUGHNESS) ? mat->get_texture(TextureSlot::ROUGHNESS)->get_handle() : 0;
|
||||
data.texture_handles[4] = mat->get_texture(TextureSlot::AO) ? mat->get_texture(TextureSlot::AO)->get_handle() : 0;
|
||||
data.texture_handles[5] = mat->get_texture(TextureSlot::EMISSION) ? mat->get_texture(TextureSlot::EMISSION)->get_handle() : 0;
|
||||
// Texture array indices (0 = no texture, 1+ = index into array)
|
||||
data.texture_handles[0] = mat->get_texture_index(TextureSlot::ALBEDO);
|
||||
data.texture_handles[1] = mat->get_texture_index(TextureSlot::NORMAL);
|
||||
data.texture_handles[2] = mat->get_texture_index(TextureSlot::METALLIC);
|
||||
data.texture_handles[3] = mat->get_texture_index(TextureSlot::ROUGHNESS);
|
||||
data.texture_handles[4] = mat->get_texture_index(TextureSlot::AO);
|
||||
data.texture_handles[5] = mat->get_texture_index(TextureSlot::EMISSION);
|
||||
|
||||
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(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) {
|
||||
|
|
@ -453,19 +459,72 @@ void RayTracer::build_texture_arrays_(const Scene &scene) {
|
|||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, width, height,
|
||||
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++) {
|
||||
auto &tex = textures[slot][i];
|
||||
GLuint tex_handle = tex->get_handle();
|
||||
if (tex_handle != 0) {
|
||||
// Copy texture data using GetTexImage and CopyTexSubImage3D
|
||||
std::vector<uint8_t> pixels(width * height * 4);
|
||||
// Get original texture format
|
||||
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);
|
||||
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);
|
||||
|
||||
// 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),
|
||||
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)
|
||||
, ior_(1.5f)
|
||||
, type_(MaterialType::DIFFUSE)
|
||||
, textures_() {
|
||||
, textures_()
|
||||
, texture_indices_() {
|
||||
}
|
||||
|
||||
Material::~Material() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue