diff --git a/all_files.md b/all_files.md new file mode 100644 index 0000000..9121de8 --- /dev/null +++ b/all_files.md @@ -0,0 +1,2820 @@ +### 文件:src/basic/math.cpp + +```cpp +#include "basic/math.h" + +namespace are { + +Mat4 MathUtils::perspective(float fov, float aspect, float near, float far) { + return glm::perspective(fov, aspect, near, far); +} + +Mat4 MathUtils::look_at(const Vec3& eye, const Vec3& center, const Vec3& up) { + return glm::lookAt(eye, center, up); +} + +Vec3 MathUtils::normalize(const Vec3& v) { + return glm::normalize(v); +} + +float MathUtils::dot(const Vec3& a, const Vec3& b) { + return glm::dot(a, b); +} + +Vec3 MathUtils::cross(const Vec3& a, const Vec3& b) { + return glm::cross(a, b); +} + +Vec3 MathUtils::reflect(const Vec3& incident, const Vec3& normal) { + return glm::reflect(incident, normal); +} + +const float* MathUtils::value_ptr(const Mat4& mat) { + return glm::value_ptr(mat); +} + +} // namespace are +``` + +### 文件:src/core/bvh.cpp + +```cpp +#include "core/bvh.h" +#include "utils/logger.h" +#include "basic/constants.h" +#include +#include + +namespace are { + +// AABB implementation +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_); +} + +float AABB::surface_area() const { + 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; +} + +// Triangle implementation +AABB Triangle::get_bounds() const { + AABB bounds(v0_, v0_); + bounds.expand(v1_); + bounds.expand(v2_); + return bounds; +} + +Vec3 Triangle::get_centroid() const { + return (v0_ + v1_ + v2_) / 3.0f; +} + +// BVH implementation +BVH::BVH() { +} + +BVH::~BVH() { + clear(); +} + +bool BVH::build(const std::vector>& meshes) { + clear(); + + Logger::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()) { + Logger::warning("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(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(triangles_.size())); + + Logger::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); + + // 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(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::max(); + + 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(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::max()), + Vec3(std::numeric_limits::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::max()), + Vec3(std::numeric_limits::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()) { + Logger::error("Cannot upload empty BVH to GPU"); + return false; + } + + // Reorder triangles according to BVH layout + std::vector ordered_triangles; + ordered_triangles.reserve(triangles_.size()); + + for (uint idx : triangle_indices_) { + ordered_triangles.push_back(triangles_[idx]); + } + + // Upload nodes + if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER, + nodes_.size() * sizeof(BVHNode), + nodes_.data(), + BufferUsage::STATIC_DRAW)) { + Logger::error("Failed to upload BVH nodes to GPU"); + return false; + } + + // Upload triangles + if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER, + ordered_triangles.size() * sizeof(Triangle), + ordered_triangles.data(), + BufferUsage::STATIC_DRAW)) { + Logger::error("Failed to upload BVH triangles to GPU"); + return false; + } + + Logger::info("BVH uploaded to GPU successfully"); + return true; +} + +void BVH::clear() { + nodes_.clear(); + triangles_.clear(); + triangle_indices_.clear(); +} + +} // namespace are +``` + +### 文件:src/core/gbuffer.cpp + +```cpp +#include "core/gbuffer.h" +#include "utils/logger.h" +#include + +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; + } +} + +GBuffer::~GBuffer() { + release(); +} + +bool GBuffer::initialize() { + if (initialized_) { + Logger::warning("GBuffer already initialized"); + return true; + } + + // Create framebuffer + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + + // Create G-Buffer textures + 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); + + // Attach textures to framebuffer + 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); + + // Create depth texture + 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); + + // Set draw buffers + GLenum draw_buffers[GBUFFER_COUNT] = { + GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION, + GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL, + GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO + }; + glDrawBuffers(GBUFFER_COUNT, draw_buffers); + + // Check framebuffer completeness + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("GBuffer framebuffer is not complete"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + initialized_ = true; + Logger::info("GBuffer initialized successfully"); + return true; +} + +void GBuffer::release() { + if (!initialized_) return; + + 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; + } + } + + if (depth_texture_ != INVALID_HANDLE) { + glDeleteTextures(1, &depth_texture_); + depth_texture_ = INVALID_HANDLE; + } + + initialized_ = false; + Logger::info("GBuffer released"); +} + +void GBuffer::render(const Scene& scene, const Shader& shader) { + if (!initialized_) { + Logger::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()) { + Logger::warning("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); + + // 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(); + } +} + +TextureHandle GBuffer::get_texture(int index) const { + if (index < 0 || index >= GBUFFER_COUNT) { + Logger::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_; +} + +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; +} + +} // namespace are +``` + +### 文件:src/core/raytracer.cpp + +```cpp +#include "core/raytracer.h" +#include "utils/logger.h" +#include "basic/constants.h" +#include + +namespace are { + +RayTracer::RayTracer(uint width, uint height, const RayTracerConfig& config) + : width_(width) + , height_(height) + , config_(config) + , accumulation_texture_(INVALID_HANDLE) + , scene_buffer_(INVALID_HANDLE) + , material_buffer_(INVALID_HANDLE) + , light_buffer_(INVALID_HANDLE) + , bvh_(nullptr) + , bvh_built_(false) + , frame_count_(0) + , initialized_(false) { +} + +RayTracer::~RayTracer() { + release(); +} + +bool RayTracer::initialize() { + if (initialized_) { + Logger::warning("RayTracer already initialized"); + return true; + } + + // Create accumulation texture + glGenTextures(1, &accumulation_texture_); + glBindTexture(GL_TEXTURE_2D, accumulation_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, 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); + + // Create shader storage buffers + glGenBuffers(1, &material_buffer_); + glGenBuffers(1, &light_buffer_); + + // Load compute shader + Logger::info("Loading ray tracing compute shader in RayTracer..."); + if (!compute_shader_.load_compute("shaders/raytracing.comp")) { + Logger::error("Failed to load ray tracing compute shader in RayTracer"); + return false; + } + Logger::info("Ray tracing compute shader loaded in RayTracer"); + + // Initialize BVH if enabled + if (config_.use_bvh_) { + bvh_ = std::make_unique(); + } + + initialized_ = true; + Logger::info("RayTracer initialized successfully"); + return true; +} + +void RayTracer::release() { + if (!initialized_) return; + + if (accumulation_texture_ != INVALID_HANDLE) { + glDeleteTextures(1, &accumulation_texture_); + accumulation_texture_ = INVALID_HANDLE; + } + + if (material_buffer_ != INVALID_HANDLE) { + glDeleteBuffers(1, &material_buffer_); + material_buffer_ = INVALID_HANDLE; + } + + if (light_buffer_ != INVALID_HANDLE) { + glDeleteBuffers(1, &light_buffer_); + light_buffer_ = INVALID_HANDLE; + } + + bvh_node_buffer_.release(); + bvh_triangle_buffer_.release(); + + compute_shader_.release(); + + bvh_.reset(); + bvh_built_ = false; + + initialized_ = false; + Logger::info("RayTracer released"); +} + +bool RayTracer::rebuild_bvh(const Scene& scene) { + if (!config_.use_bvh_) { + Logger::warning("BVH is disabled in configuration"); + return false; + } + + if (!bvh_) { + bvh_ = std::make_unique(); + } + + Logger::info("Building BVH for ray tracing..."); + + if (!bvh_->build(scene.get_meshes())) { + Logger::error("Failed to build BVH"); + return false; + } + + if (!bvh_->upload_to_gpu(bvh_node_buffer_, bvh_triangle_buffer_)) { + Logger::error("Failed to upload BVH to GPU"); + return false; + } + + bvh_built_ = true; + Logger::info("BVH built and uploaded successfully"); + return true; +} + +void RayTracer::trace(const Scene& scene, const GBuffer& gbuffer, TextureHandle output_texture) { + if (!initialized_) { + Logger::error("RayTracer not initialized"); + return; + } + + if (!compute_shader_.is_valid()) { + Logger::error("Ray tracing compute shader not loaded"); + return; + } + + // Build BVH if enabled and not built yet + if (config_.use_bvh_ && !bvh_built_) { + rebuild_bvh(scene); + } + + // Upload scene data + upload_scene_data_(scene); + + // Use compute shader + compute_shader_.use(); + + // Bind G-Buffer textures + bind_gbuffer_(gbuffer); + + // Bind output and accumulation textures + glBindImageTexture(3, output_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); + glBindImageTexture(4, accumulation_texture_, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); + + // Bind BVH buffers if enabled + if (config_.use_bvh_ && bvh_built_) { + bvh_node_buffer_.bind_base(2); + bvh_triangle_buffer_.bind_base(3); + compute_shader_.set_bool("u_use_bvh", true); + compute_shader_.set_uint("u_bvh_node_count", bvh_->get_node_count()); + } else { + compute_shader_.set_bool("u_use_bvh", false); + } + + // Set uniforms + compute_shader_.set_uint("u_frame_count", frame_count_); + compute_shader_.set_uint("u_samples_per_pixel", config_.samples_per_pixel_); + compute_shader_.set_uint("u_max_depth", config_.max_depth_); + compute_shader_.set_uint("u_light_count", static_cast(scene.get_lights().size())); + compute_shader_.set_bool("u_enable_accumulation", config_.enable_accumulation_); + + // Set camera data + const Camera& camera = scene.get_camera(); + compute_shader_.set_vec3("u_camera_position", camera.get_position()); + + Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix()); + compute_shader_.set_mat4("u_inv_view_projection", inv_vp); + + // Dispatch compute shader + uint num_groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X; + uint num_groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y; + + glDispatchCompute(num_groups_x, num_groups_y, 1); + + // Memory barrier + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + // Increment frame count for accumulation + if (config_.enable_accumulation_) { + frame_count_++; + } +} + +void RayTracer::resize(uint width, uint height) { + if (width == width_ && height == height_) return; + + width_ = width; + height_ = height; + + if (initialized_) { + // Recreate accumulation texture + if (accumulation_texture_ != INVALID_HANDLE) { + glDeleteTextures(1, &accumulation_texture_); + } + + glGenTextures(1, &accumulation_texture_); + glBindTexture(GL_TEXTURE_2D, accumulation_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, 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); + + reset_accumulation(); + } +} + +void RayTracer::reset_accumulation() { + frame_count_ = 0; +} + +void RayTracer::set_config(const RayTracerConfig& config) { + bool bvh_changed = (config.use_bvh_ != config_.use_bvh_); + + config_ = config; + reset_accumulation(); + + if (bvh_changed) { + if (config_.use_bvh_ && !bvh_) { + bvh_ = std::make_unique(); + bvh_built_ = false; + } else if (!config_.use_bvh_) { + bvh_.reset(); + bvh_built_ = false; + } + } +} + +void RayTracer::upload_scene_data_(const Scene& scene) { + // Upload materials + const auto& materials = scene.get_materials(); + if (!materials.empty()) { + struct MaterialData { + Vec3 albedo; + float metallic; + Vec3 emission; + float roughness; + int type; + float ior; + Vec2 padding; + }; + + std::vector material_data; + material_data.reserve(materials.size()); + + for (const auto& mat : materials) { + MaterialData data; + data.albedo = mat->get_albedo(); + data.metallic = mat->get_metallic(); + data.emission = mat->get_emission(); + data.roughness = mat->get_roughness(); + data.type = static_cast(mat->get_type()); + data.ior = mat->get_ior(); + material_data.push_back(data); + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_); + glBufferData(GL_SHADER_STORAGE_BUFFER, + material_data.size() * sizeof(MaterialData), + material_data.data(), GL_DYNAMIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_); + } + + // Upload lights + const auto& lights = scene.get_lights(); + if (!lights.empty()) { + struct LightData { + Vec3 position; + int type; + Vec3 direction; + float intensity; + Vec3 color; + float range; + Vec2 spot_angles; + Vec2 padding; + }; + + std::vector light_data; + light_data.reserve(lights.size()); + + for (const auto& light : lights) { + LightData data; + data.position = light->get_position(); + data.type = static_cast(light->get_type()); + data.direction = light->get_direction(); + data.intensity = light->get_intensity(); + data.color = light->get_color(); + data.range = light->get_range(); + data.spot_angles = Vec2(light->get_inner_angle(), light->get_outer_angle()); + light_data.push_back(data); + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_); + glBufferData(GL_SHADER_STORAGE_BUFFER, + light_data.size() * sizeof(LightData), + light_data.data(), GL_DYNAMIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_); + } +} + +void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) { + glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); +} + +void RayTracer::set_compute_shader(const Shader& shader) { + compute_shader_ = shader; + Logger::info("Compute shader set for RayTracer"); +} + +} // namespace are +``` + +### 文件:src/core/renderer.cpp + +```cpp +#include "core/renderer.h" +#include "utils/logger.h" +#include +#include + +namespace are { + +Renderer::Renderer(const RendererConfig &config) + : config_(config) + , initialized_(false) + , frame_count_(0) { +} + +Renderer::~Renderer() { + shutdown(); +} + +bool Renderer::initialize() { + if (initialized_) { + Logger::warning("Renderer already initialized"); + return true; + } + + Logger::info("Initializing Aurora Rendering Engine..."); + + // Initialize shader manager + shader_manager_ = std::make_unique(); + if (!shader_manager_->initialize()) { + Logger::error("Failed to initialize shader manager"); + return false; + } + + // Initialize G-Buffer + gbuffer_ = std::make_unique(config_.width_, config_.height_); + if (!gbuffer_->initialize()) { + Logger::error("Failed to initialize G-Buffer"); + return false; + } + + // Initialize ray tracer + RayTracerConfig rt_config; + rt_config.samples_per_pixel_ = config_.samples_per_pixel_; + rt_config.max_depth_ = config_.max_ray_depth_; + rt_config.enable_shadows_ = true; + rt_config.enable_reflections_ = true; + rt_config.enable_accumulation_ = config_.enable_accumulation_; + rt_config.use_bvh_ = true; + + raytracer_ = std::make_unique(config_.width_, config_.height_, rt_config); + if (!raytracer_->initialize()) { + Logger::error("Failed to initialize ray tracer"); + return false; + } + + // Pass compute shader to ray tracer + const Shader& rt_shader = shader_manager_->get_raytracing_shader(); + if (!rt_shader.is_valid()) { + Logger::error("Ray tracing shader is invalid"); + return false; + } + raytracer_->set_compute_shader(rt_shader); + + // Initialize screen blit + screen_blit_ = std::make_unique(); + if (!screen_blit_->initialize()) { + Logger::error("Failed to initialize screen blit"); + return false; + } + + initialized_ = true; + Logger::info("Aurora Rendering Engine initialized successfully"); + return true; +} + +void Renderer::shutdown() { + if (!initialized_) + return; + + Logger::info("Shutting down Aurora Rendering Engine..."); + + if (screen_blit_) { + screen_blit_->release(); + screen_blit_.reset(); + } + + if (raytracer_) { + raytracer_->release(); + raytracer_.reset(); + } + + if (gbuffer_) { + gbuffer_->release(); + gbuffer_.reset(); + } + + if (shader_manager_) { + shader_manager_->release(); + shader_manager_.reset(); + } + + initialized_ = false; + Logger::info("Aurora Rendering Engine shut down"); +} + +RenderStats Renderer::render(const Scene& scene, TextureHandle output_texture) { + RenderStats stats = {}; + + if (!initialized_) { + Logger::error("Renderer not initialized"); + return stats; + } + + // Start timing + auto start_time = std::chrono::high_resolution_clock::now(); + + // Phase 1: G-Buffer pass + auto gbuffer_start = std::chrono::high_resolution_clock::now(); + + const Shader& gbuffer_shader = shader_manager_->get_gbuffer_shader(); + gbuffer_->render(scene, gbuffer_shader); + + auto gbuffer_end = std::chrono::high_resolution_clock::now(); + stats.gbuffer_time_ms_ = std::chrono::duration(gbuffer_end - gbuffer_start).count(); + + // Phase 2: Ray tracing pass + auto raytrace_start = std::chrono::high_resolution_clock::now(); + + // Create output texture if not provided + TextureHandle rt_output = output_texture; + bool created_temp_texture = false; + + if (rt_output == 0) { + glGenTextures(1, &rt_output); + glBindTexture(GL_TEXTURE_2D, rt_output); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, config_.width_, config_.height_, + 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + created_temp_texture = true; + } + + raytracer_->trace(scene, *gbuffer_, rt_output); + + auto raytrace_end = std::chrono::high_resolution_clock::now(); + stats.raytrace_time_ms_ = std::chrono::duration(raytrace_end - raytrace_start).count(); + + // Phase 3: Blit to screen if output is default framebuffer + if (created_temp_texture && output_texture == 0) { + screen_blit_->blit_fullscreen(rt_output); + glDeleteTextures(1, &rt_output); + } + + // Calculate total frame time + auto end_time = std::chrono::high_resolution_clock::now(); + stats.frame_time_ms_ = std::chrono::duration(end_time - start_time).count(); + + // Count triangles + const auto& meshes = scene.get_meshes(); + for (const auto& mesh : meshes) { + stats.triangle_count_ += mesh->get_indices().size() / 3; + } + + // Estimate ray count (very rough) + stats.ray_count_ = config_.width_ * config_.height_ * config_.samples_per_pixel_ * config_.max_ray_depth_; + + frame_count_++; + + return stats; +} + +void Renderer::resize(uint width, uint height) { + if (width == config_.width_ && height == config_.height_) + return; + + config_.width_ = width; + config_.height_ = height; + + if (initialized_) { + gbuffer_->resize(width, height); + raytracer_->resize(width, height); + + Logger::info("Renderer resized to " + std::to_string(width) + "x" + std::to_string(height)); + } +} + +void Renderer::set_config(const RendererConfig &config) { + bool size_changed = (config.width_ != config_.width_ || config.height_ != config_.height_); + + config_ = config; + + if (initialized_) { + if (size_changed) { + resize(config_.width_, config_.height_); + } + + // Update ray tracer config + RayTracerConfig rt_config = raytracer_->get_config(); + rt_config.samples_per_pixel_ = config_.samples_per_pixel_; + rt_config.max_depth_ = config_.max_ray_depth_; + rt_config.enable_accumulation_ = config_.enable_accumulation_; + raytracer_->set_config(rt_config); + } +} + +} // namespace are +``` + +### 文件:src/core/screen_blit.cpp + +```cpp +#include "core/screen_blit.h" +#include "utils/logger.h" +#include + +namespace are { + +namespace { + const char* VERTEX_SHADER_SOURCE = R"( + #version 430 core + layout(location = 0) in vec2 a_position; + layout(location = 1) in vec2 a_texcoord; + + out vec2 v_texcoord; + + void main() { + v_texcoord = a_texcoord; + gl_Position = vec4(a_position, 0.0, 1.0); + } + )"; + + const char* FRAGMENT_SHADER_SOURCE = R"( + #version 430 core + in vec2 v_texcoord; + out vec4 frag_color; + + uniform sampler2D u_texture; + + void main() { + frag_color = texture(u_texture, v_texcoord); + } + )"; +} + +ScreenBlit::ScreenBlit() + : vao_(0) + , vbo_(0) + , initialized_(false) { +} + +ScreenBlit::~ScreenBlit() { + release(); +} + +bool ScreenBlit::initialize() { + if (initialized_) { + Logger::warning("ScreenBlit already initialized"); + return true; + } + + // Compile shader + if (!shader_.compile(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) { + Logger::error("Failed to compile screen blit shader"); + return false; + } + + // Create fullscreen quad + create_quad_(); + + initialized_ = true; + Logger::info("ScreenBlit initialized successfully"); + return true; +} + +void ScreenBlit::release() { + if (!initialized_) return; + + shader_.release(); + + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } + + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + vbo_ = 0; + } + + initialized_ = false; +} + +void ScreenBlit::blit(TextureHandle texture, int x, int y, uint width, uint height) { + if (!initialized_) { + Logger::error("ScreenBlit not initialized"); + return; + } + + // Set viewport + glViewport(x, y, width, height); + + // Disable depth test + glDisable(GL_DEPTH_TEST); + + // Use shader + shader_.use(); + shader_.set_int("u_texture", 0); + + // Bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + + // Draw quad + glBindVertexArray(vao_); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + // Re-enable depth test + glEnable(GL_DEPTH_TEST); +} + +void ScreenBlit::blit_fullscreen(TextureHandle texture) { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + blit(texture, viewport[0], viewport[1], viewport[2], viewport[3]); +} + +void ScreenBlit::create_quad_() { + // Fullscreen quad vertices (position + texcoord) + float vertices[] = { + // Position // TexCoord + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f + }; + + glGenVertexArrays(1, &vao_); + glGenBuffers(1, &vbo_); + + glBindVertexArray(vao_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Position attribute + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + + // TexCoord attribute + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + + glBindVertexArray(0); +} + +} // namespace are +``` + +### 文件:src/core/shader_manager.cpp + +```cpp +#include "core/shader_manager.h" +#include "utils/logger.h" + +namespace are { + +ShaderManager::ShaderManager() + : initialized_(false) { +} + +ShaderManager::~ShaderManager() { + release(); +} + +bool ShaderManager::initialize() { + if (initialized_) { + Logger::warning("ShaderManager already initialized"); + return true; + } + + Logger::info("Loading built-in shaders..."); + + if (!load_builtin_shaders_()) { + Logger::error("Failed to load built-in shaders"); + return false; + } + + initialized_ = true; + Logger::info("ShaderManager initialized successfully"); + return true; +} + +void ShaderManager::release() { + if (!initialized_) return; + + gbuffer_shader_.release(); + raytracing_shader_.release(); + + for (auto& pair : shader_cache_) { + pair.second.release(); + } + shader_cache_.clear(); + + initialized_ = false; + Logger::info("ShaderManager released"); +} + +Shader ShaderManager::load_shader(const std::string& name, + const std::string& vertex_path, + const std::string& fragment_path) { + // Check cache + auto it = shader_cache_.find(name); + if (it != shader_cache_.end()) { + Logger::info("Shader '" + name + "' loaded from cache"); + return it->second; + } + + // Load shader + Shader shader; + if (!shader.load(vertex_path, fragment_path)) { + Logger::error("Failed to load shader '" + name + "'"); + return Shader(); + } + + shader_cache_[name] = shader; + Logger::info("Shader '" + name + "' loaded successfully"); + return shader; +} + +Shader ShaderManager::load_compute_shader(const std::string& name, + const std::string& compute_path) { + // Check cache + auto it = shader_cache_.find(name); + if (it != shader_cache_.end()) { + Logger::info("Compute shader '" + name + "' loaded from cache"); + return it->second; + } + + // Load shader + Shader shader; + if (!shader.load_compute(compute_path)) { + Logger::error("Failed to load compute shader '" + name + "'"); + return Shader(); + } + + shader_cache_[name] = shader; + Logger::info("Compute shader '" + name + "' loaded successfully"); + return shader; +} + +Shader ShaderManager::get_shader(const std::string& name) const { + auto it = shader_cache_.find(name); + if (it != shader_cache_.end()) { + return it->second; + } + + Logger::warning("Shader '" + name + "' not found in cache"); + return Shader(); +} + +bool ShaderManager::load_builtin_shaders_() { + // Load G-Buffer shader + if (!gbuffer_shader_.load("shaders/gbuffer.vert", "shaders/gbuffer.frag")) { + Logger::error("Failed to load G-Buffer shader"); + return false; + } + shader_cache_["gbuffer"] = gbuffer_shader_; + + // Load ray tracing compute shader + if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) { + Logger::error("Failed to load ray tracing shader"); + return false; + } + shader_cache_["raytracing"] = raytracing_shader_; + + // Load ray tracing compute shader + Logger::info("Loading ray tracing compute shader..."); + if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) { + Logger::error("Failed to load ray tracing shader"); + return false; + } + shader_cache_["raytracing"] = raytracing_shader_; + Logger::info("Ray tracing shader loaded successfully"); + + return true; +} + +} // namespace are +``` + +### 文件:src/scene/camera.cpp + +```cpp +#include "scene/camera.h" +#include "basic/math.h" +#include + +namespace are { + +Camera::Camera() + : position_(0.0f, 0.0f, 5.0f) + , target_(0.0f, 0.0f, 0.0f) + , up_(0.0f, 1.0f, 0.0f) + , projection_type_(ProjectionType::PERSPECTIVE) + , fov_(glm::radians(45.0f)) + , aspect_(16.0f / 9.0f) + , left_(-1.0f) + , right_(1.0f) + , bottom_(-1.0f) + , top_(1.0f) + , near_(0.1f) + , far_(100.0f) + , view_dirty_(true) + , projection_dirty_(true) { +} + +Camera::~Camera() { +} + +void Camera::set_perspective(float fov, float aspect, float near, float far) { + projection_type_ = ProjectionType::PERSPECTIVE; + fov_ = glm::radians(fov); + aspect_ = aspect; + near_ = near; + far_ = far; + projection_dirty_ = true; +} + +void Camera::set_orthographic(float left, float right, float bottom, float top, float near, float far) { + projection_type_ = ProjectionType::ORTHOGRAPHIC; + left_ = left; + right_ = right; + bottom_ = bottom; + top_ = top; + near_ = near; + far_ = far; + projection_dirty_ = true; +} + +void Camera::set_position(const Vec3& position) { + position_ = position; + view_dirty_ = true; +} + +void Camera::set_target(const Vec3& target) { + target_ = target; + view_dirty_ = true; +} + +void Camera::set_up(const Vec3& up) { + up_ = up; + view_dirty_ = true; +} + +Mat4 Camera::get_view_matrix() const { + if (view_dirty_) { + view_matrix_ = MathUtils::look_at(position_, target_, up_); + view_dirty_ = false; + } + return view_matrix_; +} + +Mat4 Camera::get_projection_matrix() const { + if (projection_dirty_) { + if (projection_type_ == ProjectionType::PERSPECTIVE) { + projection_matrix_ = MathUtils::perspective(fov_, aspect_, near_, far_); + } else { + projection_matrix_ = glm::ortho(left_, right_, bottom_, top_, near_, far_); + } + projection_dirty_ = false; + } + return projection_matrix_; +} + +Mat4 Camera::get_view_projection_matrix() const { + return get_projection_matrix() * get_view_matrix(); +} + +Vec3 Camera::get_forward() const { + return MathUtils::normalize(target_ - position_); +} + +Vec3 Camera::get_right() const { + Vec3 forward = get_forward(); + return MathUtils::normalize(MathUtils::cross(forward, up_)); +} + +Vec3 Camera::get_up() const { + Vec3 forward = get_forward(); + Vec3 right = get_right(); + return MathUtils::cross(right, forward); +} + +} // namespace are +``` + +### 文件:src/scene/material.cpp + +```cpp +#include "scene/material.h" + +namespace are { + +Material::Material() + : albedo_(1.0f, 1.0f, 1.0f) + , emission_(0.0f, 0.0f, 0.0f) + , metallic_(0.0f) + , roughness_(0.5f) + , ior_(1.5f) + , type_(MaterialType::DIFFUSE) + , albedo_texture_(nullptr) + , normal_texture_(nullptr) { +} + +Material::~Material() { +} + +void Material::set_albedo(const Vec3& albedo) { + albedo_ = albedo; +} + +void Material::set_emission(const Vec3& emission) { + emission_ = emission; +} + +void Material::set_metallic(float metallic) { + metallic_ = glm::clamp(metallic, 0.0f, 1.0f); +} + +void Material::set_roughness(float roughness) { + roughness_ = glm::clamp(roughness, 0.0f, 1.0f); +} + +void Material::set_ior(float ior) { + ior_ = ior; +} + +void Material::set_type(MaterialType type) { + type_ = type; +} + +void Material::set_albedo_texture(std::shared_ptr texture) { + albedo_texture_ = texture; +} + +void Material::set_normal_texture(std::shared_ptr texture) { + normal_texture_ = texture; +} + +} // namespace are +``` + +### 文件:src/scene/light.cpp + +```cpp +#include "scene/light.h" +#include + +namespace are { + +Light::Light() + : type_(LightType::POINT) + , position_(0.0f, 5.0f, 0.0f) + , direction_(0.0f, -1.0f, 0.0f) + , color_(1.0f, 1.0f, 1.0f) + , intensity_(1.0f) + , range_(10.0f) + , inner_angle_(glm::radians(30.0f)) + , outer_angle_(glm::radians(45.0f)) { +} + +Light::~Light() { +} + +void Light::set_type(LightType type) { + type_ = type; +} + +void Light::set_position(const Vec3& position) { + position_ = position; +} + +void Light::set_direction(const Vec3& direction) { + direction_ = glm::normalize(direction); +} + +void Light::set_color(const Vec3& color) { + color_ = color; +} + +void Light::set_intensity(float intensity) { + intensity_ = intensity; +} + +void Light::set_range(float range) { + range_ = range; +} + +void Light::set_spot_angles(float inner_angle, float outer_angle) { + inner_angle_ = glm::radians(inner_angle); + outer_angle_ = glm::radians(outer_angle); +} + +} // namespace are +``` + +### 文件:src/scene/scene.cpp + +```cpp +#include "scene/scene.h" + +namespace are { + +Scene::Scene() { + // Create default camera + camera_ = std::make_shared(); +} + +Scene::~Scene() { + clear(); +} + +uint Scene::add_mesh(std::shared_ptr mesh) { + meshes_.push_back(mesh); + return static_cast(meshes_.size() - 1); +} + +uint Scene::add_material(std::shared_ptr material) { + materials_.push_back(material); + return static_cast(materials_.size() - 1); +} + +uint Scene::add_light(std::shared_ptr light) { + lights_.push_back(light); + return static_cast(lights_.size() - 1); +} + +void Scene::set_camera(std::shared_ptr camera) { + camera_ = camera; +} + +void Scene::clear() { + meshes_.clear(); + materials_.clear(); + lights_.clear(); +} + +void Scene::update(float delta_time) { + // Reserved for future animation/physics updates + (void)delta_time; // Suppress unused parameter warning +} + +} // namespace are +``` + +### 文件:src/scene/mesh.cpp + +```cpp +#include "scene/mesh.h" +#include "utils/logger.h" +#include + +namespace are { + +Mesh::Mesh() + : material_id_(0) + , transform_(1.0f) + , vao_(0) + , vbo_(0) + , ebo_(0) + , uploaded_(false) { +} + +Mesh::~Mesh() { + release_gpu_resources(); +} + +void Mesh::set_vertices(const std::vector& vertices) { + vertices_ = vertices; + uploaded_ = false; +} + +void Mesh::set_indices(const std::vector& indices) { + indices_ = indices; + uploaded_ = false; +} + +void Mesh::set_material(uint material_id) { + material_id_ = material_id; +} + +void Mesh::set_transform(const Mat4& transform) { + transform_ = transform; +} + +bool Mesh::upload_to_gpu() { + if (uploaded_) { + Logger::warning("Mesh already uploaded to GPU"); + return true; + } + + if (vertices_.empty()) { + Logger::error("Cannot upload mesh: no vertices"); + return false; + } + + if (indices_.empty()) { + Logger::error("Cannot upload mesh: no indices"); + return false; + } + + // Generate VAO + glGenVertexArrays(1, &vao_); + glBindVertexArray(vao_); + + // Generate and upload VBO + glGenBuffers(1, &vbo_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(Vertex), + vertices_.data(), GL_STATIC_DRAW); + + // Generate and upload EBO + glGenBuffers(1, &ebo_); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(uint), + indices_.data(), GL_STATIC_DRAW); + + // Set vertex attributes + // Location 0: Position + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void*)offsetof(Vertex, position_)); + + // Location 1: Normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void*)offsetof(Vertex, normal_)); + + // Location 2: TexCoord + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void*)offsetof(Vertex, texcoord_)); + + // Location 3: Tangent + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void*)offsetof(Vertex, tangent_)); + + glBindVertexArray(0); + + uploaded_ = true; + Logger::info("Mesh uploaded to GPU successfully"); + return true; +} + +void Mesh::release_gpu_resources() { + if (!uploaded_) return; + + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + vao_ = 0; + } + + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + vbo_ = 0; + } + + if (ebo_ != 0) { + glDeleteBuffers(1, &ebo_); + ebo_ = 0; + } + + uploaded_ = false; +} + +} // namespace are +``` + +### 文件:src/resource/buffer.cpp + +```cpp +#include "resource/buffer.h" +#include "utils/logger.h" +#include + +namespace are { + +namespace { + GLenum get_gl_buffer_type(BufferType type) { + switch (type) { + case BufferType::VERTEX_BUFFER: return GL_ARRAY_BUFFER; + case BufferType::INDEX_BUFFER: return GL_ELEMENT_ARRAY_BUFFER; + case BufferType::UNIFORM_BUFFER: return GL_UNIFORM_BUFFER; + case BufferType::SHADER_STORAGE_BUFFER: return GL_SHADER_STORAGE_BUFFER; + default: return GL_ARRAY_BUFFER; + } + } + + GLenum get_gl_usage(BufferUsage usage) { + switch (usage) { + case BufferUsage::STATIC_DRAW: return GL_STATIC_DRAW; + case BufferUsage::DYNAMIC_DRAW: return GL_DYNAMIC_DRAW; + case BufferUsage::STREAM_DRAW: return GL_STREAM_DRAW; + default: return GL_STATIC_DRAW; + } + } +} + +Buffer::Buffer() + : handle_(INVALID_HANDLE) + , type_(BufferType::VERTEX_BUFFER) + , size_(0) + , usage_(BufferUsage::STATIC_DRAW) { +} + +Buffer::~Buffer() { + // Don't auto-release, let user control lifetime +} + +bool Buffer::create(BufferType type, size_t size, const void* data, BufferUsage usage) { + if (handle_ != INVALID_HANDLE) { + Logger::warning("Buffer already created, releasing old buffer"); + release(); + } + + type_ = type; + size_ = size; + usage_ = usage; + + glGenBuffers(1, &handle_); + + GLenum gl_type = get_gl_buffer_type(type); + GLenum gl_usage = get_gl_usage(usage); + + glBindBuffer(gl_type, handle_); + glBufferData(gl_type, size, data, gl_usage); + glBindBuffer(gl_type, 0); + + Logger::info("Buffer created successfully"); + return true; +} + +void Buffer::update(size_t offset, size_t size, const void* data) { + if (handle_ == INVALID_HANDLE) { + Logger::error("Cannot update invalid buffer"); + return; + } + + if (offset + size > size_) { + Logger::error("Buffer update out of bounds"); + return; + } + + GLenum gl_type = get_gl_buffer_type(type_); + + glBindBuffer(gl_type, handle_); + glBufferSubData(gl_type, offset, size, data); + glBindBuffer(gl_type, 0); +} + +void Buffer::bind() const { + if (handle_ == INVALID_HANDLE) { + Logger::warning("Attempting to bind invalid buffer"); + return; + } + + GLenum gl_type = get_gl_buffer_type(type_); + glBindBuffer(gl_type, handle_); +} + +void Buffer::bind_base(uint binding_point) const { + if (handle_ == INVALID_HANDLE) { + Logger::warning("Attempting to bind invalid buffer"); + return; + } + + GLenum gl_type = get_gl_buffer_type(type_); + glBindBufferBase(gl_type, binding_point, handle_); +} + +void Buffer::unbind() const { + GLenum gl_type = get_gl_buffer_type(type_); + glBindBuffer(gl_type, 0); +} + +void Buffer::release() { + if (handle_ != INVALID_HANDLE) { + glDeleteBuffers(1, &handle_); + handle_ = INVALID_HANDLE; + } + + size_ = 0; +} + +} // namespace are +``` + +### 文件:src/resource/model_loader.cpp + +```cpp +#include "resource/model_loader.h" +#include "utils/logger.h" +#include "resource/texture.h" + +// Note: This is a simplified implementation without Assimp +// For full implementation, include Assimp and implement properly + +namespace are { + +bool ModelLoader::load(const std::string& path, + std::vector>& meshes, + std::vector>& materials, + bool flip_uvs) { + Logger::error("ModelLoader requires Assimp library (not implemented in this version)"); + Logger::info("To implement: include , , "); + + // Placeholder implementation + // TODO: Implement with Assimp + /* + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(path, + aiProcess_Triangulate | + aiProcess_GenNormals | + aiProcess_CalcTangentSpace | + (flip_uvs ? aiProcess_FlipUVs : 0)); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + Logger::error("Failed to load model: " + std::string(importer.GetErrorString())); + return false; + } + + std::string directory = path.substr(0, path.find_last_of('/')); + process_node_(scene->mRootNode, scene, meshes, materials, directory); + + Logger::info("Model loaded: " + path); + return true; + */ + + return false; +} + +bool ModelLoader::load_and_upload(const std::string& path, + std::vector>& meshes, + std::vector>& materials, + bool flip_uvs) { + if (!load(path, meshes, materials, flip_uvs)) { + return false; + } + + // Upload all meshes to GPU + for (auto& mesh : meshes) { + if (!mesh->upload_to_gpu()) { + Logger::error("Failed to upload mesh to GPU"); + return false; + } + } + + return true; +} + +void ModelLoader::process_node_(void* node, void* scene, + std::vector>& meshes, + std::vector>& materials, + const std::string& directory) { + // TODO: Implement with Assimp + /* + aiNode* ai_node = static_cast(node); + const aiScene* ai_scene = static_cast(scene); + + // Process all meshes in this node + for (uint i = 0; i < ai_node->mNumMeshes; ++i) { + aiMesh* ai_mesh = ai_scene->mMeshes[ai_node->mMeshes[i]]; + meshes.push_back(process_mesh_(ai_mesh, ai_scene, materials, directory)); + } + + // Process children recursively + for (uint i = 0; i < ai_node->mNumChildren; ++i) { + process_node_(ai_node->mChildren[i], ai_scene, meshes, materials, directory); + } + */ +} + +std::shared_ptr ModelLoader::process_mesh_(void* mesh, void* scene, + std::vector>& materials, + const std::string& directory) { + // TODO: Implement with Assimp + /* + aiMesh* ai_mesh = static_cast(mesh); + const aiScene* ai_scene = static_cast(scene); + + std::vector vertices; + std::vector indices; + + // Process vertices + for (uint i = 0; i < ai_mesh->mNumVertices; ++i) { + Vertex vertex; + + vertex.position_ = Vec3(ai_mesh->mVertices[i].x, + ai_mesh->mVertices[i].y, + ai_mesh->mVertices[i].z); + + if (ai_mesh->HasNormals()) { + vertex.normal_ = Vec3(ai_mesh->mNormals[i].x, + ai_mesh->mNormals[i].y, + ai_mesh->mNormals[i].z); + } + + if (ai_mesh->mTextureCoords[0]) { + vertex.texcoord_ = Vec2(ai_mesh->mTextureCoords[0][i].x, + ai_mesh->mTextureCoords[0][i].y); + } + + if (ai_mesh->HasTangentsAndBitangents()) { + vertex.tangent_ = Vec3(ai_mesh->mTangents[i].x, + ai_mesh->mTangents[i].y, + ai_mesh->mTangents[i].z); + } + + vertices.push_back(vertex); + } + + // Process indices + for (uint i = 0; i < ai_mesh->mNumFaces; ++i) { + aiFace face = ai_mesh->mFaces[i]; + for (uint j = 0; j < face.mNumIndices; ++j) { + indices.push_back(face.mIndices[j]); + } + } + + // Process material + uint material_id = materials.size(); + if (ai_mesh->mMaterialIndex >= 0) { + aiMaterial* ai_material = ai_scene->mMaterials[ai_mesh->mMaterialIndex]; + + auto material = std::make_shared(); + + // Load diffuse color + aiColor3D color; + if (ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) { + material->set_albedo(Vec3(color.r, color.g, color.b)); + } + + // Load textures + load_material_textures_(ai_material, aiTextureType_DIFFUSE, material, directory); + load_material_textures_(ai_material, aiTextureType_NORMALS, material, directory); + + materials.push_back(material); + } + + auto mesh_obj = std::make_shared(); + mesh_obj->set_vertices(vertices); + mesh_obj->set_indices(indices); + mesh_obj->set_material(material_id); + + return mesh_obj; + */ + + return nullptr; +} + +void ModelLoader::load_material_textures_(void* material, int type, + std::shared_ptr& mat, + const std::string& directory) { + // TODO: Implement with Assimp + /* + aiMaterial* ai_material = static_cast(material); + aiTextureType ai_type = static_cast(type); + + for (uint i = 0; i < ai_material->GetTextureCount(ai_type); ++i) { + aiString str; + ai_material->GetTexture(ai_type, i, &str); + + std::string filename = directory + "/" + std::string(str.C_Str()); + + auto texture = std::make_shared(); + if (texture->load_from_file(filename)) { + if (ai_type == aiTextureType_DIFFUSE) { + mat->set_albedo_texture(texture); + } else if (ai_type == aiTextureType_NORMALS) { + mat->set_normal_texture(texture); + } + } + } + */ +} + +} // namespace are +``` + +### 文件:src/resource/shader.cpp + +```cpp +#include "resource/shader.h" +#include "utils/logger.h" +#include "basic/math.h" // 修改为math.h +#include +#include +#include + +namespace are { + +Shader::Shader() + : handle_(INVALID_HANDLE) { +} + +Shader::~Shader() { + // Don't auto-release, let user control lifetime +} + +bool Shader::load(const std::string& vertex_path, const std::string& fragment_path) { + std::string vertex_source = read_file_(vertex_path); + std::string fragment_source = read_file_(fragment_path); + + if (vertex_source.empty() || fragment_source.empty()) { + Logger::error("Failed to read shader files"); + return false; + } + + return compile(vertex_source, fragment_source); +} + +bool Shader::load_compute(const std::string& compute_path) { + std::string compute_source = read_file_(compute_path); + + if (compute_source.empty()) { + Logger::error("Failed to read compute shader file"); + return false; + } + + return compile_compute(compute_source); +} + +bool Shader::compile(const std::string& vertex_source, const std::string& fragment_source) { + uint vertex_shader = compile_shader_(vertex_source, GL_VERTEX_SHADER); + if (vertex_shader == 0) return false; + + uint fragment_shader = compile_shader_(fragment_source, GL_FRAGMENT_SHADER); + if (fragment_shader == 0) { + glDeleteShader(vertex_shader); + return false; + } + + uint shaders[] = { vertex_shader, fragment_shader }; + bool success = link_program_(shaders, 2); + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + + return success; +} + +bool Shader::compile_compute(const std::string& compute_source) { + uint compute_shader = compile_shader_(compute_source, GL_COMPUTE_SHADER); + if (compute_shader == 0) return false; + + uint shaders[] = { compute_shader }; + bool success = link_program_(shaders, 1); + + glDeleteShader(compute_shader); + + return success; +} + +void Shader::use() const { // 改为const + if (handle_ != INVALID_HANDLE) { + glUseProgram(handle_); + } +} + +void Shader::release() { + if (handle_ != INVALID_HANDLE) { + glDeleteProgram(handle_); + handle_ = INVALID_HANDLE; + } + uniform_cache_.clear(); +} + +void Shader::set_bool(const std::string& name, bool value) const { // 新增 + glUniform1i(get_uniform_location_(name), static_cast(value)); +} + +void Shader::set_int(const std::string& name, int value) const { // 改为const + glUniform1i(get_uniform_location_(name), value); +} + +void Shader::set_uint(const std::string& name, uint value) const { // 改为const + glUniform1ui(get_uniform_location_(name), value); +} + +void Shader::set_float(const std::string& name, float value) const { // 改为const + glUniform1f(get_uniform_location_(name), value); +} + +void Shader::set_vec2(const std::string& name, const Vec2& value) const { // 改为const + glUniform2fv(get_uniform_location_(name), 1, &value[0]); +} + +void Shader::set_vec3(const std::string& name, const Vec3& value) const { // 改为const + glUniform3fv(get_uniform_location_(name), 1, &value[0]); +} + +void Shader::set_vec4(const std::string& name, const Vec4& value) const { // 改为const + glUniform4fv(get_uniform_location_(name), 1, &value[0]); +} + +void Shader::set_mat3(const std::string& name, const Mat3& value) const { // 改为const + glUniformMatrix3fv(get_uniform_location_(name), 1, GL_FALSE, &value[0][0]); +} + +void Shader::set_mat4(const std::string& name, const Mat4& value) const { // 改为const + glUniformMatrix4fv(get_uniform_location_(name), 1, GL_FALSE, MathUtils::value_ptr(value)); +} + +int Shader::get_uniform_location_(const std::string& name) const { // 改为const + auto it = uniform_cache_.find(name); + if (it != uniform_cache_.end()) { + return it->second; + } + + int location = glGetUniformLocation(handle_, name.c_str()); + uniform_cache_[name] = location; // mutable允许修改 + + if (location == -1) { + Logger::warning("Uniform '" + name + "' not found in shader"); + } + + return location; +} + +uint Shader::compile_shader_(const std::string& source, uint type) { + uint shader = glCreateShader(type); + const char* source_cstr = source.c_str(); + glShaderSource(shader, 1, &source_cstr, nullptr); + glCompileShader(shader); + + int success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char info_log[512]; + glGetShaderInfoLog(shader, 512, nullptr, info_log); + + std::string type_str = (type == GL_VERTEX_SHADER) ? "VERTEX" : + (type == GL_FRAGMENT_SHADER) ? "FRAGMENT" : "COMPUTE"; + Logger::error("Shader compilation failed (" + type_str + "): " + std::string(info_log)); + + glDeleteShader(shader); + return 0; + } + + return shader; +} + +bool Shader::link_program_(const uint* shaders, uint count) { + handle_ = glCreateProgram(); + + for (uint i = 0; i < count; ++i) { + glAttachShader(handle_, shaders[i]); + } + + glLinkProgram(handle_); + + int success; + glGetProgramiv(handle_, GL_LINK_STATUS, &success); + if (!success) { + char info_log[512]; + glGetProgramInfoLog(handle_, 512, nullptr, info_log); + Logger::error("Shader linking failed: " + std::string(info_log)); + + glDeleteProgram(handle_); + handle_ = INVALID_HANDLE; + return false; + } + + return true; +} + +std::string Shader::read_file_(const std::string& path) { + std::ifstream file(path); + if (!file.is_open()) { + Logger::error("Failed to open file: " + path); + return ""; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +} // namespace are +``` + +### 文件:src/resource/texture.cpp + +```cpp +#include "resource/texture.h" +#include "utils/logger.h" +#include +#include + +namespace are { + +namespace { + GLenum get_gl_internal_format(TextureFormat format) { + switch (format) { + case TextureFormat::R8: return GL_R8; + case TextureFormat::RG8: return GL_RG8; + case TextureFormat::RGB8: return GL_RGB8; + case TextureFormat::RGBA8: return GL_RGBA8; + case TextureFormat::R16F: return GL_R16F; + case TextureFormat::RG16F: return GL_RG16F; + case TextureFormat::RGB16F: return GL_RGB16F; + case TextureFormat::RGBA16F: return GL_RGBA16F; + case TextureFormat::R32F: return GL_R32F; + case TextureFormat::RG32F: return GL_RG32F; + case TextureFormat::RGB32F: return GL_RGB32F; + case TextureFormat::RGBA32F: return GL_RGBA32F; + case TextureFormat::DEPTH24_STENCIL8: return GL_DEPTH24_STENCIL8; + default: return GL_RGBA8; + } + } + + GLenum get_gl_format(TextureFormat format) { + switch (format) { + case TextureFormat::R8: + case TextureFormat::R16F: + case TextureFormat::R32F: + return GL_RED; + case TextureFormat::RG8: + case TextureFormat::RG16F: + case TextureFormat::RG32F: + return GL_RG; + case TextureFormat::RGB8: + case TextureFormat::RGB16F: + case TextureFormat::RGB32F: + return GL_RGB; + case TextureFormat::RGBA8: + case TextureFormat::RGBA16F: + case TextureFormat::RGBA32F: + return GL_RGBA; + case TextureFormat::DEPTH24_STENCIL8: + return GL_DEPTH_STENCIL; + default: + return GL_RGBA; + } + } + + GLenum get_gl_type(TextureFormat format) { + switch (format) { + case TextureFormat::R8: + case TextureFormat::RG8: + case TextureFormat::RGB8: + case TextureFormat::RGBA8: + return GL_UNSIGNED_BYTE; + case TextureFormat::R16F: + case TextureFormat::RG16F: + case TextureFormat::RGB16F: + case TextureFormat::RGBA16F: + case TextureFormat::R32F: + case TextureFormat::RG32F: + case TextureFormat::RGB32F: + case TextureFormat::RGBA32F: + return GL_FLOAT; + case TextureFormat::DEPTH24_STENCIL8: + return GL_UNSIGNED_INT_24_8; + default: + return GL_UNSIGNED_BYTE; + } + } + + GLenum get_gl_filter(TextureFilter filter) { + switch (filter) { + case TextureFilter::NEAREST: return GL_NEAREST; + case TextureFilter::LINEAR: return GL_LINEAR; + case TextureFilter::NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case TextureFilter::LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case TextureFilter::NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + case TextureFilter::LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: return GL_LINEAR; + } + } + + GLenum get_gl_wrap(TextureWrap wrap) { + switch (wrap) { + case TextureWrap::REPEAT: return GL_REPEAT; + case TextureWrap::MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + case TextureWrap::CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + case TextureWrap::CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + default: return GL_REPEAT; + } + } +} + +Texture::Texture() + : handle_(INVALID_HANDLE) + , width_(0) + , height_(0) + , format_(TextureFormat::RGBA8) + , has_mipmaps_(false) { +} + +Texture::~Texture() { + // Don't auto-release, let user control lifetime +} + +bool Texture::load_from_file(const std::string& path, bool generate_mipmaps) { + // Load image using stb_image + int width, height, channels; + stbi_set_flip_vertically_on_load(true); + unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0); + + if (!data) { + Logger::error("Failed to load texture: " + path); + return false; + } + + // Determine format based on channels + TextureFormat format; + switch (channels) { + case 1: format = TextureFormat::R8; break; + case 2: format = TextureFormat::RG8; break; + case 3: format = TextureFormat::RGB8; break; + case 4: format = TextureFormat::RGBA8; break; + default: + Logger::error("Unsupported channel count: " + std::to_string(channels)); + stbi_image_free(data); + return false; + } + + // Create texture + bool success = create(width, height, format); + if (!success) { + stbi_image_free(data); + return false; + } + + // Upload data + success = upload(data, width, height, format); + stbi_image_free(data); + + if (!success) { + return false; + } + + // Generate mipmaps if requested + if (generate_mipmaps) { + this->generate_mipmaps(); + } + + Logger::info("Texture loaded successfully: " + path); + return true; +} + +bool Texture::create(uint width, uint height, TextureFormat format) { + if (handle_ != INVALID_HANDLE) { + Logger::warning("Texture already created, releasing old texture"); + release(); + } + + width_ = width; + height_ = height; + format_ = format; + + glGenTextures(1, &handle_); + glBindTexture(GL_TEXTURE_2D, handle_); + + GLenum internal_format = get_gl_internal_format(format); + GLenum gl_format = get_gl_format(format); + GLenum type = get_gl_type(format); + + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, gl_format, type, nullptr); + + // Set default parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +bool Texture::upload(const void* data, uint width, uint height, TextureFormat format) { + if (handle_ == INVALID_HANDLE) { + Logger::error("Cannot upload to invalid texture"); + return false; + } + + if (width != width_ || height != height_ || format != format_) { + Logger::warning("Upload parameters differ from texture creation, recreating texture"); + create(width, height, format); + } + + glBindTexture(GL_TEXTURE_2D, handle_); + + GLenum gl_format = get_gl_format(format); + GLenum type = get_gl_type(format); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl_format, type, data); + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +void Texture::set_filter(TextureFilter min_filter, TextureFilter mag_filter) { + if (handle_ == INVALID_HANDLE) { + Logger::error("Cannot set filter on invalid texture"); + return; + } + + glBindTexture(GL_TEXTURE_2D, handle_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_gl_filter(min_filter)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_gl_filter(mag_filter)); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::set_wrap(TextureWrap wrap_s, TextureWrap wrap_t) { + if (handle_ == INVALID_HANDLE) { + Logger::error("Cannot set wrap mode on invalid texture"); + return; + } + + glBindTexture(GL_TEXTURE_2D, handle_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, get_gl_wrap(wrap_s)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, get_gl_wrap(wrap_t)); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::generate_mipmaps() { + if (handle_ == INVALID_HANDLE) { + Logger::error("Cannot generate mipmaps for invalid texture"); + return; + } + + glBindTexture(GL_TEXTURE_2D, handle_); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + has_mipmaps_ = true; +} + +void Texture::bind(uint unit) const { + if (handle_ == INVALID_HANDLE) { + Logger::warning("Attempting to bind invalid texture"); + return; + } + + glActiveTexture(GL_TEXTURE0 + unit); + glBindTexture(GL_TEXTURE_2D, handle_); +} + +void Texture::unbind() const { + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::release() { + if (handle_ != INVALID_HANDLE) { + glDeleteTextures(1, &handle_); + handle_ = INVALID_HANDLE; + } + + width_ = 0; + height_ = 0; + has_mipmaps_ = false; +} + +} // namespace are +``` + +### 文件:src/utils/config.cpp + +```cpp +#include "utils/config.h" +#include "utils/logger.h" +#include +#include +#include + +namespace are { + +// Static storage +static std::unordered_map g_config_map; + +// Helper function to trim whitespace +static std::string trim(const std::string& str) { + size_t first = str.find_first_not_of(" \t\r\n"); + if (first == std::string::npos) return ""; + size_t last = str.find_last_not_of(" \t\r\n"); + return str.substr(first, last - first + 1); +} + +bool Config::load(const std::string& path) { + std::ifstream file(path); + if (!file.is_open()) { + Logger::error("Failed to open config file: " + path); + return false; + } + + g_config_map.clear(); + + std::string line; + std::string current_section; + + while (std::getline(file, line)) { + line = trim(line); + + // Skip empty lines and comments + if (line.empty() || line[0] == '#' || line[0] == ';') { + continue; + } + + // Section header + if (line[0] == '[' && line.back() == ']') { + current_section = line.substr(1, line.length() - 2); + continue; + } + + // Key-value pair + size_t pos = line.find('='); + if (pos != std::string::npos) { + std::string key = trim(line.substr(0, pos)); + std::string value = trim(line.substr(pos + 1)); + + // Add section prefix if in a section + if (!current_section.empty()) { + key = current_section + "." + key; + } + + g_config_map[key] = value; + } + } + + Logger::info("Config loaded: " + path + " (" + std::to_string(g_config_map.size()) + " entries)"); + return true; +} + +bool Config::save(const std::string& path) { + std::ofstream file(path); + if (!file.is_open()) { + Logger::error("Failed to open config file for writing: " + path); + return false; + } + + for (const auto& pair : g_config_map) { + file << pair.first << "=" << pair.second << std::endl; + } + + Logger::info("Config saved: " + path); + return true; +} + +std::string Config::get_string(const std::string& key, const std::string& default_value) { + auto it = g_config_map.find(key); + if (it != g_config_map.end()) { + return it->second; + } + return default_value; +} + +int Config::get_int(const std::string& key, int default_value) { + auto it = g_config_map.find(key); + if (it != g_config_map.end()) { + try { + return std::stoi(it->second); + } catch (...) { + Logger::warning("Failed to parse int for key: " + key); + } + } + return default_value; +} + +float Config::get_float(const std::string& key, float default_value) { + auto it = g_config_map.find(key); + if (it != g_config_map.end()) { + try { + return std::stof(it->second); + } catch (...) { + Logger::warning("Failed to parse float for key: " + key); + } + } + return default_value; +} + +bool Config::get_bool(const std::string& key, bool default_value) { + auto it = g_config_map.find(key); + if (it != g_config_map.end()) { + std::string value = it->second; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + if (value == "true" || value == "1" || value == "yes" || value == "on") { + return true; + } + if (value == "false" || value == "0" || value == "no" || value == "off") { + return false; + } + } + return default_value; +} + +void Config::set_string(const std::string& key, const std::string& value) { + g_config_map[key] = value; +} + +void Config::set_int(const std::string& key, int value) { + g_config_map[key] = std::to_string(value); +} + +void Config::set_float(const std::string& key, float value) { + g_config_map[key] = std::to_string(value); +} + +void Config::set_bool(const std::string& key, bool value) { + g_config_map[key] = value ? "true" : "false"; +} + +} // namespace are +``` + +### 文件:src/utils/logger.cpp + +```cpp +#include "utils/logger.h" +#include +#include +#include +#include +#include + +namespace are { + +// Static members +static LogLevel g_min_level = LogLevel::DEBUG; +static std::ofstream g_log_file; +static bool g_initialized = false; + +bool Logger::initialize(const std::string& log_file) { + if (g_initialized) { + return true; + } + + if (!log_file.empty()) { + g_log_file.open(log_file, std::ios::out | std::ios::app); + if (!g_log_file.is_open()) { + std::cerr << "Failed to open log file: " << log_file << std::endl; + return false; + } + } + + g_initialized = true; + return true; +} + +void Logger::shutdown() { + if (g_log_file.is_open()) { + g_log_file.close(); + } + g_initialized = false; +} + +static std::string get_current_time() { + auto now = std::time(nullptr); + auto tm = *std::localtime(&now); + std::ostringstream oss; + oss << std::put_time(&tm, "%H:%M:%S"); + return oss.str(); +} + +static std::string level_to_string(LogLevel level) { + switch (level) { + case LogLevel::DEBUG: return "DEBUG"; + case LogLevel::INFO: return "INFO"; + case LogLevel::WARNING: return "WARN"; + case LogLevel::ERROR: return "ERROR"; + case LogLevel::FATAL: return "FATAL"; + default: return "UNKNOWN"; + } +} + +void Logger::log(LogLevel level, const std::string& message) { + if (level < g_min_level) return; + + std::string time_str = get_current_time(); + std::string level_str = level_to_string(level); + std::string formatted = "[" + time_str + "] [" + level_str + "] " + message; + + // Console output + if (level >= LogLevel::ERROR) { + std::cerr << formatted << std::endl; + } else { + std::cout << formatted << std::endl; + } + + // File output + if (g_log_file.is_open()) { + g_log_file << formatted << std::endl; + g_log_file.flush(); + } +} + +void Logger::debug(const std::string& message) { + log(LogLevel::DEBUG, message); +} + +void Logger::info(const std::string& message) { + log(LogLevel::INFO, message); +} + +void Logger::warning(const std::string& message) { + log(LogLevel::WARNING, message); +} + +void Logger::error(const std::string& message) { + log(LogLevel::ERROR, message); +} + +void Logger::fatal(const std::string& message) { + log(LogLevel::FATAL, message); +} + +void Logger::set_level(LogLevel level) { + g_min_level = level; +} + +} // namespace are +``` + diff --git a/all_headers.md b/all_headers.md new file mode 100644 index 0000000..fd42fdd --- /dev/null +++ b/all_headers.md @@ -0,0 +1,1762 @@ +### 文件:include/basic/types.h + +```cpp +#ifndef ARE_INCLUDE_BASIC_TYPES_H +#define ARE_INCLUDE_BASIC_TYPES_H + +#include +#include +#include +#include +#include + +namespace are { + +/// @brief Basic vector types using GLM +using Vec2 = glm::vec2; +using Vec3 = glm::vec3; +using Vec4 = glm::vec4; + +/// @brief Basic matrix types using GLM +using Mat3 = glm::mat3; +using Mat4 = glm::mat4; + +/// @brief Basic integer types +using uint = uint32_t; +using uchar = uint8_t; + +/// @brief Handle types for GPU resources +using TextureHandle = uint; +using BufferHandle = uint; +using ShaderHandle = uint; +using FramebufferHandle = uint; + +/// @brief Invalid handle constant +constexpr uint INVALID_HANDLE = 0; + +/// @brief Vertex structure for mesh data +struct Vertex { + Vec3 position_; + Vec3 normal_; + Vec2 texcoord_; + Vec3 tangent_; +}; + +/// @brief Ray structure for ray tracing +struct Ray { + Vec3 origin_; + Vec3 direction_; + float t_min_; + float t_max_; +}; + +/// @brief Hit information for ray-surface intersection +struct HitInfo { + bool hit_; + float t_; + Vec3 position_; + Vec3 normal_; + Vec2 texcoord_; + uint material_id_; +}; + +/// @brief Rendering statistics +struct RenderStats { + float frame_time_ms_; + uint triangle_count_; + uint ray_count_; + float gbuffer_time_ms_; + float raytrace_time_ms_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_BASIC_TYPES_H +``` + +### 文件:include/basic/constants.h + +```cpp +#ifndef ARE_INCLUDE_BASIC_CONSTANTS_H +#define ARE_INCLUDE_BASIC_CONSTANTS_H + +namespace are { + +/// @brief Maximum number of lights in scene +constexpr int MAX_LIGHTS = 16; + +/// @brief Maximum ray tracing depth +constexpr int MAX_RAY_DEPTH = 8; + +/// @brief Default samples per pixel for ray tracing +constexpr int DEFAULT_SPP = 1; + +/// @brief G-Buffer attachment indices +constexpr int GBUFFER_POSITION = 0; +constexpr int GBUFFER_NORMAL = 1; +constexpr int GBUFFER_ALBEDO = 2; +constexpr int GBUFFER_COUNT = 3; + +/// @brief Compute shader work group size +constexpr int COMPUTE_GROUP_SIZE_X = 16; +constexpr int COMPUTE_GROUP_SIZE_Y = 16; + +/// @brief Mathematical constants +constexpr float PI = 3.14159265359f; +constexpr float INV_PI = 0.31830988618f; +constexpr float EPSILON = 1e-4f; + +} // namespace are + +#endif // ARE_INCLUDE_BASIC_CONSTANTS_H +``` + +### 文件:include/basic/math.h + +```cpp +#ifndef ARE_INCLUDE_BASIC_MATH_UTILS_H +#define ARE_INCLUDE_BASIC_MATH_UTILS_H + +#include "types.h" +#include +#include + +namespace are { + +/// @brief Math utility functions wrapping GLM +class MathUtils { +public: + /// @brief Create perspective projection matrix + /// @param fov Field of view in radians + /// @param aspect Aspect ratio + /// @param near Near plane distance + /// @param far Far plane distance + /// @return Projection matrix + static Mat4 perspective(float fov, float aspect, float near, float far); + + /// @brief Create look-at view matrix + /// @param eye Camera position + /// @param center Look-at target + /// @param up Up vector + /// @return View matrix + static Mat4 look_at(const Vec3& eye, const Vec3& center, const Vec3& up); + + /// @brief Normalize a vector + /// @param v Input vector + /// @return Normalized vector + static Vec3 normalize(const Vec3& v); + + /// @brief Calculate dot product + /// @param a First vector + /// @param b Second vector + /// @return Dot product + static float dot(const Vec3& a, const Vec3& b); + + /// @brief Calculate cross product + /// @param a First vector + /// @param b Second vector + /// @return Cross product + static Vec3 cross(const Vec3& a, const Vec3& b); + + /// @brief Reflect vector around normal + /// @param incident Incident vector + /// @param normal Surface normal + /// @return Reflected vector + static Vec3 reflect(const Vec3& incident, const Vec3& normal); + + /// @brief Get pointer to matrix data (for OpenGL) + /// @param mat Input matrix + /// @return Pointer to matrix data + static const float* value_ptr(const Mat4& mat); +}; + +} // namespace are + +#endif // ARE_INCLUDE_BASIC_MATH_UTILS_H +``` + +### 文件:include/core/bvh.h + +```cpp +#ifndef ARE_INCLUDE_CORE_BVH_H +#define ARE_INCLUDE_CORE_BVH_H + +#include "basic/types.h" +#include "scene/mesh.h" +#include "resource/buffer.h" +#include +#include + +namespace are { + +/// @brief Axis-aligned bounding box +struct AABB { + Vec3 min_; + Vec3 max_; + + /// @brief Construct AABB from min and max points + AABB(const Vec3& min = Vec3(0.0f), const Vec3& max = Vec3(0.0f)) + : min_(min), max_(max) {} + + /// @brief Expand AABB to include point + void expand(const Vec3& point); + + /// @brief Expand AABB to include another AABB + void expand(const AABB& other); + + /// @brief Get center of AABB + Vec3 center() const { return (min_ + max_) * 0.5f; } + + /// @brief Get surface area of AABB + float surface_area() const; + + /// @brief Check if AABB is valid + bool is_valid() const; +}; + +/// @brief Triangle primitive for BVH +struct Triangle { + Vec3 v0_, v1_, v2_; + Vec3 n0_, n1_, n2_; + Vec2 uv0_, uv1_, uv2_; + uint material_id_; + + /// @brief Get bounding box of triangle + AABB get_bounds() const; + + /// @brief Get centroid of triangle + Vec3 get_centroid() const; +}; + +/// @brief BVH node for GPU +struct BVHNode { + Vec3 aabb_min_; + uint left_first_; // Left child index or first primitive index + Vec3 aabb_max_; + uint count_; // 0 for interior node, >0 for leaf node +}; + +/// @brief Bounding Volume Hierarchy for ray tracing acceleration +class BVH { +public: + /// @brief Constructor + BVH(); + + /// @brief Destructor + ~BVH(); + + /// @brief Build BVH from meshes + /// @param meshes Mesh list + /// @return True if build succeeded + bool build(const std::vector>& meshes); + + /// @brief Upload BVH to GPU + /// @param node_buffer Buffer for BVH nodes + /// @param triangle_buffer Buffer for triangles + /// @return True if upload succeeded + bool upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer); + + /// @brief Get total node count + /// @return Node count + uint get_node_count() const { return static_cast(nodes_.size()); } + + /// @brief Get total triangle count + /// @return Triangle count + uint get_triangle_count() const { return static_cast(triangles_.size()); } + + /// @brief Clear BVH data + void clear(); + +private: + std::vector nodes_; + std::vector triangles_; + std::vector triangle_indices_; + + /// @brief Recursively build BVH + /// @param node_idx Current node index + /// @param first_prim First primitive index + /// @param prim_count Primitive count + void build_recursive_(uint node_idx, uint first_prim, uint prim_count); + + /// @brief Find best split using SAH + /// @param first_prim First primitive index + /// @param prim_count Primitive count + /// @param axis Split axis (output) + /// @param split_pos Split position (output) + /// @return Split cost + float find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos); + + /// @brief Calculate node bounds + /// @param first_prim First primitive index + /// @param prim_count Primitive count + /// @return Bounding box + AABB calculate_bounds_(uint first_prim, uint prim_count); + + /// @brief Calculate centroid bounds + /// @param first_prim First primitive index + /// @param prim_count Primitive count + /// @return Centroid bounding box + AABB calculate_centroid_bounds_(uint first_prim, uint prim_count); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_BVH_H +``` + +### 文件:include/core/gbuffer.h + +```cpp +#ifndef ARE_INCLUDE_CORE_GBUFFER_H +#define ARE_INCLUDE_CORE_GBUFFER_H + +#include "basic/types.h" +#include "basic/constants.h" +#include "scene/scene.h" +#include "resource/shader.h" + +namespace are { + +/// @brief G-Buffer manager for deferred rendering +class GBuffer { +public: + /// @brief Constructor + /// @param width Buffer width + /// @param height Buffer height + GBuffer(uint width, uint height); + + /// @brief Destructor + ~GBuffer(); + + /// @brief Initialize G-Buffer (create framebuffer and textures) + /// @return True if initialization succeeded + bool initialize(); + + /// @brief Release G-Buffer resources + void release(); + + /// @brief Render scene to G-Buffer + /// @param scene Scene to render + /// @param shader Shader program for G-Buffer pass + void render(const Scene& scene, const Shader& shader); + + /// @brief Resize G-Buffer + /// @param width New width + /// @param height New height + void resize(uint width, uint height); + + /// @brief Get texture handle for specific buffer + /// @param index Buffer index (GBUFFER_POSITION, GBUFFER_NORMAL, etc.) + /// @return Texture handle + TextureHandle get_texture(int index) const; + + /// @brief Get framebuffer handle + /// @return Framebuffer handle + FramebufferHandle get_framebuffer() const { return fbo_; } + + /// @brief Get buffer dimensions + /// @param width Output width + /// @param height Output height + void get_dimensions(uint& width, uint& height) const; + +private: + uint width_; + uint height_; + FramebufferHandle fbo_; + TextureHandle textures_[GBUFFER_COUNT]; + TextureHandle depth_texture_; + + bool initialized_; + + /// @brief Create texture for G-Buffer attachment + /// @param internal_format OpenGL internal format + /// @param format OpenGL format + /// @param type OpenGL type + /// @return Texture handle + TextureHandle create_texture_(uint internal_format, uint format, uint type); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_GBUFFER_H +``` + +### 文件:include/core/raytracer.h + +```cpp +#ifndef ARE_INCLUDE_CORE_RAYTRACER_H +#define ARE_INCLUDE_CORE_RAYTRACER_H + +#include "basic/types.h" +#include "core/bvh.h" // 添加 +#include "core/gbuffer.h" +#include "resource/buffer.h" +#include "resource/shader.h" +#include "scene/scene.h" + +namespace are { + +/// @brief Ray tracing configuration +struct RayTracerConfig { + uint samples_per_pixel_; + uint max_depth_; + bool enable_shadows_; + bool enable_reflections_; + bool enable_accumulation_; + bool use_bvh_; // 添加BVH开关 +}; + +/// @brief Compute shader based ray tracer +class RayTracer { +public: + /// @brief Constructor + /// @param width Output width + /// @param height Output height + /// @param config Ray tracer configuration + RayTracer(uint width, uint height, const RayTracerConfig &config); + + /// @brief Destructor + ~RayTracer(); + + /// @brief Initialize ray tracer + /// @return True if initialization succeeded + bool initialize(); + + /// @brief Release resources + void release(); + + /// @brief Trace rays using G-Buffer as input + /// @param scene Scene data + /// @param gbuffer G-Buffer containing geometry information + /// @param output_texture Output texture for ray traced result + void trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle output_texture); + + /// @brief Resize output + /// @param width New width + /// @param height New height + void resize(uint width, uint height); + + /// @brief Reset accumulation buffer + void reset_accumulation(); + + /// @brief Get current configuration + /// @return Current configuration + const RayTracerConfig &get_config() const { + return config_; + } + + /// @brief Update configuration + /// @param config New configuration + void set_config(const RayTracerConfig &config); + + /// @brief Rebuild BVH from scene + /// @param scene Scene to build BVH from + /// @return True if build succeeded + bool rebuild_bvh(const Scene &scene); + + /// @brief Set compute shader (called by renderer) + /// @param shader Compute shader + void set_compute_shader(const Shader &shader); + +private: + uint width_; + uint height_; + RayTracerConfig config_; + + Shader compute_shader_; + TextureHandle accumulation_texture_; + BufferHandle scene_buffer_; + BufferHandle material_buffer_; + BufferHandle light_buffer_; + + // BVH related + std::unique_ptr bvh_; // 添加 + Buffer bvh_node_buffer_; // 添加 + Buffer bvh_triangle_buffer_; // 添加 + bool bvh_built_; // 添加 + + uint frame_count_; + bool initialized_; + + /// @brief Upload scene data to GPU buffers + /// @param scene Scene to upload + void upload_scene_data_(const Scene &scene); + + /// @brief Bind G-Buffer textures to compute shader + /// @param gbuffer G-Buffer to bind + void bind_gbuffer_(const GBuffer &gbuffer); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_RAYTRACER_H +``` + +### 文件:include/core/renderer.h + +```cpp +#ifndef ARE_INCLUDE_CORE_RENDERER_H +#define ARE_INCLUDE_CORE_RENDERER_H + +#include "basic/types.h" +#include "scene/scene.h" +#include "core/gbuffer.h" +#include "core/raytracer.h" +#include "core/screen_blit.h" +#include "core/shader_manager.h" +#include + +namespace are { + +/// @brief Main renderer configuration +struct RendererConfig { + uint width_; + uint height_; + uint samples_per_pixel_; + uint max_ray_depth_; + bool enable_denoising_; + bool enable_accumulation_; +}; + +/// @brief Main rendering engine interface +class Renderer { +public: + /// @brief Constructor + /// @param config Renderer configuration + Renderer(const RendererConfig& config); + + /// @brief Destructor + ~Renderer(); + + /// @brief Initialize renderer (OpenGL context must be current) + /// @return True if initialization succeeded + bool initialize(); + + /// @brief Shutdown renderer and release resources + void shutdown(); + + /// @brief Render a frame + /// @param scene Scene to render + /// @param output_texture Output texture handle (0 for default framebuffer) + /// @return Rendering statistics + RenderStats render(const Scene& scene, TextureHandle output_texture = 0); + + /// @brief Resize render targets + /// @param width New width + /// @param height New height + void resize(uint width, uint height); + + /// @brief Get current configuration + /// @return Current configuration + const RendererConfig& get_config() const { return config_; } + + /// @brief Update configuration + /// @param config New configuration + void set_config(const RendererConfig& config); + +private: + RendererConfig config_; + std::unique_ptr gbuffer_; + std::unique_ptr raytracer_; + std::unique_ptr shader_manager_; + std::unique_ptr screen_blit_; + + bool initialized_; + uint frame_count_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_RENDERER_H +``` + +### 文件:include/core/screen_blit.h + +```cpp +#ifndef ARE_INCLUDE_CORE_SCREEN_BLIT_H +#define ARE_INCLUDE_CORE_SCREEN_BLIT_H + +#include "basic/types.h" +#include "resource/shader.h" + +namespace are { + +/// @brief Screen blit utility for rendering texture to screen +class ScreenBlit { +public: + /// @brief Constructor + ScreenBlit(); + + /// @brief Destructor + ~ScreenBlit(); + + /// @brief Initialize screen blit + /// @return True if initialization succeeded + bool initialize(); + + /// @brief Release resources + void release(); + + /// @brief Blit texture to screen + /// @param texture Texture to blit + /// @param x Screen X position + /// @param y Screen Y position + /// @param width Blit width + /// @param height Blit height + void blit(TextureHandle texture, int x, int y, uint width, uint height); + + /// @brief Blit texture to full screen + /// @param texture Texture to blit + void blit_fullscreen(TextureHandle texture); + +private: + Shader shader_; + uint vao_; + uint vbo_; + bool initialized_; + + /// @brief Create fullscreen quad + void create_quad_(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_SCREEN_BLIT_H +``` + +### 文件:include/core/shader_manager.h + +```cpp +#ifndef ARE_INCLUDE_CORE_SHADER_MANAGER_H +#define ARE_INCLUDE_CORE_SHADER_MANAGER_H + +#include "basic/types.h" +#include "resource/shader.h" +#include +#include + +namespace are { + +/// @brief Shader manager for loading and caching shaders +class ShaderManager { +public: + /// @brief Constructor + ShaderManager(); + + /// @brief Destructor + ~ShaderManager(); + + /// @brief Initialize shader manager and load built-in shaders + /// @return True if initialization succeeded + bool initialize(); + + /// @brief Release all shaders + void release(); + + /// @brief Load shader from files + /// @param name Shader name for caching + /// @param vertex_path Vertex shader file path + /// @param fragment_path Fragment shader file path + /// @return Shader object + Shader load_shader(const std::string& name, + const std::string& vertex_path, + const std::string& fragment_path); + + /// @brief Load compute shader from file + /// @param name Shader name for caching + /// @param compute_path Compute shader file path + /// @return Shader object + Shader load_compute_shader(const std::string& name, + const std::string& compute_path); + + /// @brief Get cached shader by name + /// @param name Shader name + /// @return Shader object (invalid if not found) + Shader get_shader(const std::string& name) const; + + /// @brief Get G-Buffer shader + /// @return G-Buffer shader + const Shader& get_gbuffer_shader() const { return gbuffer_shader_; } + + /// @brief Get ray tracing compute shader + /// @return Ray tracing shader + const Shader& get_raytracing_shader() const { return raytracing_shader_; } + +private: + std::unordered_map shader_cache_; + Shader gbuffer_shader_; + Shader raytracing_shader_; + + bool initialized_; + + /// @brief Load built-in shaders + /// @return True if loading succeeded + bool load_builtin_shaders_(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_SHADER_MANAGER_H +``` + +### 文件:include/scene/camera.h + +```cpp +#ifndef ARE_INCLUDE_SCENE_CAMERA_H +#define ARE_INCLUDE_SCENE_CAMERA_H + +#include "basic/types.h" + +namespace are { + +/// @brief Camera projection type +enum class ProjectionType { + PERSPECTIVE, + ORTHOGRAPHIC +}; + +/// @brief Camera for rendering +class Camera { +public: + /// @brief Constructor + Camera(); + + /// @brief Destructor + ~Camera(); + + /// @brief Set perspective projection + /// @param fov Field of view in degrees + /// @param aspect Aspect ratio + /// @param near Near plane + /// @param far Far plane + void set_perspective(float fov, float aspect, float near, float far); + + /// @brief Set orthographic projection + /// @param left Left plane + /// @param right Right plane + /// @param bottom Bottom plane + /// @param top Top plane + /// @param near Near plane + /// @param far Far plane + void set_orthographic(float left, float right, float bottom, float top, float near, float far); + + /// @brief Set camera position + /// @param position Position + void set_position(const Vec3& position); + + /// @brief Set camera target + /// @param target Target position + void set_target(const Vec3& target); + + /// @brief Set camera up vector + /// @param up Up vector + void set_up(const Vec3& up); + + /// @brief Get view matrix + /// @return View matrix + Mat4 get_view_matrix() const; + + /// @brief Get projection matrix + /// @return Projection matrix + Mat4 get_projection_matrix() const; + + /// @brief Get view-projection matrix + /// @return View-projection matrix + Mat4 get_view_projection_matrix() const; + + /// @brief Get camera position + /// @return Position + const Vec3& get_position() const { return position_; } + + /// @brief Get camera forward direction + /// @return Forward direction + Vec3 get_forward() const; + + /// @brief Get camera right direction + /// @return Right direction + Vec3 get_right() const; + + /// @brief Get camera up direction + /// @return Up direction + Vec3 get_up() const; + +private: + Vec3 position_; + Vec3 target_; + Vec3 up_; + + ProjectionType projection_type_; + + // Perspective parameters + float fov_; + float aspect_; + + // Orthographic parameters + float left_, right_, bottom_, top_; + + // Common parameters + float near_; + float far_; + + mutable Mat4 view_matrix_; + mutable Mat4 projection_matrix_; + mutable bool view_dirty_; + mutable bool projection_dirty_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_CAMERA_H +``` + +### 文件:include/scene/material.h + +```cpp +#ifndef ARE_INCLUDE_SCENE_MATERIAL_H +#define ARE_INCLUDE_SCENE_MATERIAL_H + +#include "basic/types.h" +#include "resource/texture.h" +#include + +namespace are { + +/// @brief Material type enumeration +enum class MaterialType { + DIFFUSE = 0, + METAL = 1, + DIELECTRIC = 2, + EMISSIVE = 3 +}; + +/// @brief Material properties +class Material { +public: + /// @brief Constructor + Material(); + + /// @brief Destructor + ~Material(); + + /// @brief Set albedo color + /// @param albedo Albedo color + void set_albedo(const Vec3& albedo); + + /// @brief Set emission color + /// @param emission Emission color + void set_emission(const Vec3& emission); + + /// @brief Set metallic value + /// @param metallic Metallic (0-1) + void set_metallic(float metallic); + + /// @brief Set roughness value + /// @param roughness Roughness (0-1) + void set_roughness(float roughness); + + /// @brief Set index of refraction + /// @param ior Index of refraction + void set_ior(float ior); + + /// @brief Set material type + /// @param type Material type + void set_type(MaterialType type); + + /// @brief Set albedo texture + /// @param texture Albedo texture + void set_albedo_texture(std::shared_ptr texture); + + /// @brief Set normal map + /// @param texture Normal map texture + void set_normal_texture(std::shared_ptr texture); + + /// @brief Get albedo color + /// @return Albedo color + const Vec3& get_albedo() const { return albedo_; } + + /// @brief Get emission color + /// @return Emission color + const Vec3& get_emission() const { return emission_; } + + /// @brief Get metallic value + /// @return Metallic + float get_metallic() const { return metallic_; } + + /// @brief Get roughness value + /// @return Roughness + float get_roughness() const { return roughness_; } + + /// @brief Get index of refraction + /// @return IOR + float get_ior() const { return ior_; } + + /// @brief Get material type + /// @return Material type + MaterialType get_type() const { return type_; } + + /// @brief Get albedo texture + /// @return Albedo texture (nullptr if none) + std::shared_ptr get_albedo_texture() const { return albedo_texture_; } + + /// @brief Get normal texture + /// @return Normal texture (nullptr if none) + std::shared_ptr get_normal_texture() const { return normal_texture_; } + +private: + Vec3 albedo_; + Vec3 emission_; + float metallic_; + float roughness_; + float ior_; + MaterialType type_; + + std::shared_ptr albedo_texture_; + std::shared_ptr normal_texture_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MATERIAL_H +``` + +### 文件:include/scene/light.h + +```cpp +#ifndef ARE_INCLUDE_SCENE_LIGHT_H +#define ARE_INCLUDE_SCENE_LIGHT_H + +#include "basic/types.h" + +namespace are { + +/// @brief Light type enumeration +enum class LightType { + DIRECTIONAL = 0, + POINT = 1, + SPOT = 2 +}; + +/// @brief Light source +class Light { +public: + /// @brief Constructor + Light(); + + /// @brief Destructor + ~Light(); + + /// @brief Set light type + /// @param type Light type + void set_type(LightType type); + + /// @brief Set light position (for point and spot lights) + /// @param position Light position + void set_position(const Vec3 &position); + + /// @brief Set light direction (for directional and spot lights) + /// @param direction Light direction + void set_direction(const Vec3 &direction); + + /// @brief Set light color + /// @param color Light color + void set_color(const Vec3 &color); + + /// @brief Set light intensity + /// @param intensity Light intensity + void set_intensity(float intensity); + + /// @brief Set light range (for point and spot lights) + /// @param range Light range + void set_range(float range); + + /// @brief Set spot light angles + /// @param inner_angle Inner cone angle in degrees + /// @param outer_angle Outer cone angle in degrees + void set_spot_angles(float inner_angle, float outer_angle); + + /// @brief Get light type + /// @return Light type + LightType get_type() const { + return type_; + } + + /// @brief Get light position + /// @return Light position + const Vec3 &get_position() const { + return position_; + } + + /// @brief Get light direction + /// @return Light direction + const Vec3 &get_direction() const { + return direction_; + } + + /// @brief Get light color + /// @return Light color + const Vec3 &get_color() const { + return color_; + } + + /// @brief Get light intensity + /// @return Light intensity + float get_intensity() const { + return intensity_; + } + + /// @brief Get light range + /// @return Light range + float get_range() const { + return range_; + } + + /// @brief Get spot light inner angle + /// @return Inner angle in radians + float get_inner_angle() const { + return inner_angle_; + } + + /// @brief Get spot light outer angle + /// @return Outer angle in radians + float get_outer_angle() const { + return outer_angle_; + } + +private: + LightType type_; + Vec3 position_; + Vec3 direction_; + Vec3 color_; + float intensity_; + float range_; + float inner_angle_; + float outer_angle_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_LIGHT_H +``` + +### 文件:include/scene/scene.h + +```cpp +#ifndef ARE_INCLUDE_SCENE_SCENE_H +#define ARE_INCLUDE_SCENE_SCENE_H + +#include "basic/types.h" +#include "scene/camera.h" +#include "scene/mesh.h" +#include "scene/material.h" +#include "scene/light.h" +#include +#include + +namespace are { + +/// @brief Scene container holding all scene objects +class Scene { +public: + /// @brief Constructor + Scene(); + + /// @brief Destructor + ~Scene(); + + /// @brief Add mesh to scene + /// @param mesh Mesh to add + /// @return Mesh index + uint add_mesh(std::shared_ptr mesh); + + /// @brief Add material to scene + /// @param material Material to add + /// @return Material index + uint add_material(std::shared_ptr material); + + /// @brief Add light to scene + /// @param light Light to add + /// @return Light index + uint add_light(std::shared_ptr light); + + /// @brief Set active camera + /// @param camera Camera to set + void set_camera(std::shared_ptr camera); + + /// @brief Get active camera + /// @return Active camera + const Camera& get_camera() const { return *camera_; } + + /// @brief Get all meshes + /// @return Mesh list + const std::vector>& get_meshes() const { return meshes_; } + + /// @brief Get all materials + /// @return Material list + const std::vector>& get_materials() const { return materials_; } + + /// @brief Get all lights + /// @return Light list + const std::vector>& get_lights() const { return lights_; } + + /// @brief Clear all scene objects + void clear(); + + /// @brief Update scene (animations, transforms, etc.) + /// @param delta_time Time since last update + void update(float delta_time); + +private: + std::shared_ptr camera_; + std::vector> meshes_; + std::vector> materials_; + std::vector> lights_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_SCENE_H +``` + +### 文件:include/scene/mesh.h + +```cpp +#ifndef ARE_INCLUDE_SCENE_MESH_H +#define ARE_INCLUDE_SCENE_MESH_H + +#include "basic/types.h" +#include + +namespace are { + +/// @brief Mesh data container +class Mesh { +public: + /// @brief Constructor + Mesh(); + + /// @brief Destructor + ~Mesh(); + + /// @brief Set vertex data + /// @param vertices Vertex array + void set_vertices(const std::vector& vertices); + + /// @brief Set index data + /// @param indices Index array + void set_indices(const std::vector& indices); + + /// @brief Set material index + /// @param material_id Material index + void set_material(uint material_id); + + /// @brief Set transform matrix + /// @param transform Transform matrix + void set_transform(const Mat4& transform); + + /// @brief Get vertices + /// @return Vertex array + const std::vector& get_vertices() const { return vertices_; } + + /// @brief Get indices + /// @return Index array + const std::vector& get_indices() const { return indices_; } + + /// @brief Get material index + /// @return Material index + uint get_material() const { return material_id_; } + + /// @brief Get transform matrix + /// @return Transform matrix + const Mat4& get_transform() const { return transform_; } + + /// @brief Upload mesh data to GPU + /// @return True if upload succeeded + bool upload_to_gpu(); + + /// @brief Release GPU resources + void release_gpu_resources(); + + /// @brief Get VAO handle + /// @return VAO handle + uint get_vao() const { return vao_; } + + /// @brief Check if mesh is uploaded to GPU + /// @return True if uploaded + bool is_uploaded() const { return uploaded_; } + +private: + std::vector vertices_; + std::vector indices_; + uint material_id_; + Mat4 transform_; + + uint vao_; + uint vbo_; + uint ebo_; + bool uploaded_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MESH_H +``` + +### 文件:include/resource/buffer.h + +```cpp +#ifndef ARE_INCLUDE_RESOURCE_BUFFER_H +#define ARE_INCLUDE_RESOURCE_BUFFER_H + +#include "basic/types.h" + +namespace are { + +/// @brief Buffer usage hint +enum class BufferUsage { + STATIC_DRAW, + DYNAMIC_DRAW, + STREAM_DRAW +}; + +/// @brief Buffer type +enum class BufferType { + VERTEX_BUFFER, + INDEX_BUFFER, + UNIFORM_BUFFER, + SHADER_STORAGE_BUFFER +}; + +/// @brief GPU buffer resource +class Buffer { +public: + /// @brief Constructor + Buffer(); + + /// @brief Destructor + ~Buffer(); + + /// @brief Create buffer + /// @param type Buffer type + /// @param size Buffer size in bytes + /// @param data Initial data (nullptr for empty buffer) + /// @param usage Usage hint + /// @return True if creation succeeded + bool create(BufferType type, size_t size, const void* data, BufferUsage usage); + + /// @brief Update buffer data + /// @param offset Offset in bytes + /// @param size Size in bytes + /// @param data Data to upload + void update(size_t offset, size_t size, const void* data); + + /// @brief Bind buffer + void bind() const; + + /// @brief Bind buffer to binding point (for UBO/SSBO) + /// @param binding_point Binding point index + void bind_base(uint binding_point) const; + + /// @brief Unbind buffer + void unbind() const; + + /// @brief Release buffer resources + void release(); + + /// @brief Get buffer handle + /// @return Buffer handle + BufferHandle get_handle() const { return handle_; } + + /// @brief Get buffer size + /// @return Size in bytes + size_t get_size() const { return size_; } + + /// @brief Get buffer type + /// @return Buffer type + BufferType get_type() const { return type_; } + + /// @brief Check if buffer is valid + /// @return True if valid + bool is_valid() const { return handle_ != INVALID_HANDLE; } + +private: + BufferHandle handle_; + BufferType type_; + size_t size_; + BufferUsage usage_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RESOURCE_BUFFER_H +``` + +### 文件:include/resource/model_loader.h + +```cpp +#ifndef ARE_INCLUDE_RESOURCE_MODEL_LOADER_H +#define ARE_INCLUDE_RESOURCE_MODEL_LOADER_H + +#include "basic/types.h" +#include "scene/mesh.h" +#include "scene/material.h" +#include +#include +#include + +namespace are { + +/// @brief Model loader using Assimp +class ModelLoader { +public: + /// @brief Load model from file + /// @param path Model file path + /// @param meshes Output mesh list + /// @param materials Output material list + /// @param flip_uvs Flip UV coordinates vertically + /// @return True if loading succeeded + static bool load(const std::string& path, + std::vector>& meshes, + std::vector>& materials, + bool flip_uvs = true); + + /// @brief Load model and automatically upload to GPU + /// @param path Model file path + /// @param meshes Output mesh list + /// @param materials Output material list + /// @param flip_uvs Flip UV coordinates vertically + /// @return True if loading succeeded + static bool load_and_upload(const std::string& path, + std::vector>& meshes, + std::vector>& materials, + bool flip_uvs = true); + +private: + /// @brief Process Assimp node recursively + static void process_node_(void* node, void* scene, + std::vector>& meshes, + std::vector>& materials, + const std::string& directory); + + /// @brief Process Assimp mesh + static std::shared_ptr process_mesh_(void* mesh, void* scene, + std::vector>& materials, + const std::string& directory); + + /// @brief Load material textures + static void load_material_textures_(void* material, int type, + std::shared_ptr& mat, + const std::string& directory); +}; + +} // namespace are + +#endif // ARE_INCLUDE_RESOURCE_MODEL_LOADER_H +``` + +### 文件:include/resource/shader.h + +```cpp +#ifndef ARE_INCLUDE_RESOURCE_SHADER_H +#define ARE_INCLUDE_RESOURCE_SHADER_H + +#include "basic/types.h" +#include +#include + +namespace are { + +/// @brief Shader program resource +class Shader { +public: + /// @brief Constructor + Shader(); + + /// @brief Destructor + ~Shader(); + + /// @brief Load and compile shader from files + /// @param vertex_path Vertex shader path + /// @param fragment_path Fragment shader path + /// @return True if compilation succeeded + bool load(const std::string& vertex_path, const std::string& fragment_path); + + /// @brief Load and compile compute shader + /// @param compute_path Compute shader path + /// @return True if compilation succeeded + bool load_compute(const std::string& compute_path); + + /// @brief Compile shader from source strings + /// @param vertex_source Vertex shader source + /// @param fragment_source Fragment shader source + /// @return True if compilation succeeded + bool compile(const std::string& vertex_source, const std::string& fragment_source); + + /// @brief Compile compute shader from source + /// @param compute_source Compute shader source + /// @return True if compilation succeeded + bool compile_compute(const std::string& compute_source); + + /// @brief Use/activate shader program + void use() const; // 改为const + + /// @brief Release shader resources + void release(); + + /// @brief Set uniform boolean + /// @param name Uniform name + /// @param value Value + void set_bool(const std::string& name, bool value) const; // 新增,const + + /// @brief Set uniform integer + /// @param name Uniform name + /// @param value Value + void set_int(const std::string& name, int value) const; // 改为const + + /// @brief Set uniform unsigned integer + /// @param name Uniform name + /// @param value Value + void set_uint(const std::string& name, uint value) const; // 改为const + + /// @brief Set uniform float + /// @param name Uniform name + /// @param value Value + void set_float(const std::string& name, float value) const; // 改为const + + /// @brief Set uniform vec2 + /// @param name Uniform name + /// @param value Value + void set_vec2(const std::string& name, const Vec2& value) const; // 改为const + + /// @brief Set uniform vec3 + /// @param name Uniform name + /// @param value Value + void set_vec3(const std::string& name, const Vec3& value) const; // 改为const + + /// @brief Set uniform vec4 + /// @param name Uniform name + /// @param value Value + void set_vec4(const std::string& name, const Vec4& value) const; // 改为const + + /// @brief Set uniform mat3 + /// @param name Uniform name + /// @param value Value + void set_mat3(const std::string& name, const Mat3& value) const; // 改为const + + /// @brief Set uniform mat4 + /// @param name Uniform name + /// @param value Value + void set_mat4(const std::string& name, const Mat4& value) const; // 改为const + + /// @brief Get shader program handle + /// @return Shader handle + ShaderHandle get_handle() const { return handle_; } + + /// @brief Check if shader is valid + /// @return True if valid + bool is_valid() const { return handle_ != INVALID_HANDLE; } + +private: + ShaderHandle handle_; + mutable std::unordered_map uniform_cache_; // 改为mutable + + /// @brief Get uniform location (with caching) + /// @param name Uniform name + /// @return Uniform location + int get_uniform_location_(const std::string& name) const; // 改为const + + /// @brief Compile shader stage + /// @param source Shader source code + /// @param type Shader type (GL_VERTEX_SHADER, etc.) + /// @return Shader object handle (0 on failure) + uint compile_shader_(const std::string& source, uint type); + + /// @brief Link shader program + /// @param shaders Array of shader object handles + /// @param count Number of shaders + /// @return True if linking succeeded + bool link_program_(const uint* shaders, uint count); + + /// @brief Read file content + /// @param path File path + /// @return File content + std::string read_file_(const std::string& path); +}; + +} // namespace are + +#endif // ARE_INCLUDE_RESOURCE_SHADER_H +``` + +### 文件:include/resource/texture.h + +```cpp +#ifndef ARE_INCLUDE_RESOURCE_TEXTURE_H +#define ARE_INCLUDE_RESOURCE_TEXTURE_H + +#include "basic/types.h" +#include + +namespace are { + +/// @brief Texture format enumeration +enum class TextureFormat { + R8, + RG8, + RGB8, + RGBA8, + R16F, + RG16F, + RGB16F, + RGBA16F, + R32F, + RG32F, + RGB32F, + RGBA32F, + DEPTH24_STENCIL8 +}; + +/// @brief Texture filter mode +enum class TextureFilter { + NEAREST, + LINEAR, + NEAREST_MIPMAP_NEAREST, + LINEAR_MIPMAP_NEAREST, + NEAREST_MIPMAP_LINEAR, + LINEAR_MIPMAP_LINEAR +}; + +/// @brief Texture wrap mode +enum class TextureWrap { + REPEAT, + MIRRORED_REPEAT, + CLAMP_TO_EDGE, + CLAMP_TO_BORDER +}; + +/// @brief Texture resource +class Texture { +public: + /// @brief Constructor + Texture(); + + /// @brief Destructor + ~Texture(); + + /// @brief Load texture from file + /// @param path File path + /// @param generate_mipmaps Generate mipmaps + /// @return True if loading succeeded + bool load_from_file(const std::string& path, bool generate_mipmaps = true); + + /// @brief Create empty texture + /// @param width Texture width + /// @param height Texture height + /// @param format Texture format + /// @return True if creation succeeded + bool create(uint width, uint height, TextureFormat format); + + /// @brief Upload data to texture + /// @param data Pixel data + /// @param width Data width + /// @param height Data height + /// @param format Data format + /// @return True if upload succeeded + bool upload(const void* data, uint width, uint height, TextureFormat format); + + /// @brief Set texture filter mode + /// @param min_filter Minification filter + /// @param mag_filter Magnification filter + void set_filter(TextureFilter min_filter, TextureFilter mag_filter); + + /// @brief Set texture wrap mode + /// @param wrap_s Wrap mode for S coordinate + /// @param wrap_t Wrap mode for T coordinate + void set_wrap(TextureWrap wrap_s, TextureWrap wrap_t); + + /// @brief Generate mipmaps + void generate_mipmaps(); + + /// @brief Bind texture to texture unit + /// @param unit Texture unit + void bind(uint unit) const; + + /// @brief Unbind texture + void unbind() const; + + /// @brief Release texture resources + void release(); + + /// @brief Get texture handle + /// @return Texture handle + TextureHandle get_handle() const { return handle_; } + + /// @brief Get texture width + /// @return Width + uint get_width() const { return width_; } + + /// @brief Get texture height + /// @return Height + uint get_height() const { return height_; } + + /// @brief Get texture format + /// @return Format + TextureFormat get_format() const { return format_; } + + /// @brief Check if texture is valid + /// @return True if valid + bool is_valid() const { return handle_ != INVALID_HANDLE; } + +private: + TextureHandle handle_; + uint width_; + uint height_; + TextureFormat format_; + bool has_mipmaps_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RESOURCE_TEXTURE_H +``` + +### 文件:include/utils/config.h + +```cpp +#ifndef ARE_INCLUDE_UTILS_CONFIG_H +#define ARE_INCLUDE_UTILS_CONFIG_H + +#include +#include + +namespace are { + +/// @brief Configuration manager for loading engine settings +/// @note This module should be implemented by the user +class Config { +public: + /// @brief Load configuration from file + /// @param path Configuration file path + /// @return True if loading succeeded + static bool load(const std::string& path); + + /// @brief Save configuration to file + /// @param path Configuration file path + /// @return True if saving succeeded + static bool save(const std::string& path); + + /// @brief Get string value + /// @param key Configuration key + /// @param default_value Default value if key not found + /// @return Configuration value + static std::string get_string(const std::string& key, const std::string& default_value = ""); + + /// @brief Get integer value + /// @param key Configuration key + /// @param default_value Default value if key not found + /// @return Configuration value + static int get_int(const std::string& key, int default_value = 0); + + /// @brief Get float value + /// @param key Configuration key + /// @param default_value Default value if key not found + /// @return Configuration value + static float get_float(const std::string& key, float default_value = 0.0f); + + /// @brief Get boolean value + /// @param key Configuration key + /// @param default_value Default value if key not found + /// @return Configuration value + static bool get_bool(const std::string& key, bool default_value = false); + + /// @brief Set string value + /// @param key Configuration key + /// @param value Configuration value + static void set_string(const std::string& key, const std::string& value); + + /// @brief Set integer value + /// @param key Configuration key + /// @param value Configuration value + static void set_int(const std::string& key, int value); + + /// @brief Set float value + /// @param key Configuration key + /// @param value Configuration value + static void set_float(const std::string& key, float value); + + /// @brief Set boolean value + /// @param key Configuration key + /// @param value Configuration value + static void set_bool(const std::string& key, bool value); +}; + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_CONFIG_H +``` + +### 文件:include/utils/logger.h + +```cpp +#ifndef ARE_INCLUDE_UTILS_LOGGER_H +#define ARE_INCLUDE_UTILS_LOGGER_H + +#include + +namespace are { + +/// @brief Log level enumeration +enum class LogLevel { + DEBUG, + INFO, + WARNING, + ERROR, + FATAL +}; + +/// @brief Logger interface for engine logging +/// @note This module should be implemented by the user +class Logger { +public: + /// @brief Initialize logger + /// @param log_file Log file path (empty for console only) + /// @return True if initialization succeeded + static bool initialize(const std::string& log_file = ""); + + /// @brief Shutdown logger + static void shutdown(); + + /// @brief Log message + /// @param level Log level + /// @param message Message content + static void log(LogLevel level, const std::string& message); + + /// @brief Log debug message + /// @param message Message content + static void debug(const std::string& message); + + /// @brief Log info message + /// @param message Message content + static void info(const std::string& message); + + /// @brief Log warning message + /// @param message Message content + static void warning(const std::string& message); + + /// @brief Log error message + /// @param message Message content + static void error(const std::string& message); + + /// @brief Log fatal message + /// @param message Message content + static void fatal(const std::string& message); + + /// @brief Set minimum log level + /// @param level Minimum level to log + static void set_level(LogLevel level); +}; + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_LOGGER_H +``` + diff --git a/examples/cornell_box b/examples/cornell_box index c8bcff2..befa73f 100644 Binary files a/examples/cornell_box and b/examples/cornell_box differ diff --git a/examples/cornell_box.cpp b/examples/cornell_box.cpp index 752de40..3b20586 100644 --- a/examples/cornell_box.cpp +++ b/examples/cornell_box.cpp @@ -225,7 +225,7 @@ void setup_cornell_box() { g_scene->add_mesh(tall_box); // Short box (metal, right side) - auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), metal_id); + auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), /*metal_id*/white_id); short_box->upload_to_gpu(); g_scene->add_mesh(short_box); diff --git a/include/basic/constants.h b/include/basic/constants.h index 12c631d..60733fa 100644 --- a/include/basic/constants.h +++ b/include/basic/constants.h @@ -16,7 +16,9 @@ constexpr int DEFAULT_SPP = 1; constexpr int GBUFFER_POSITION = 0; constexpr int GBUFFER_NORMAL = 1; constexpr int GBUFFER_ALBEDO = 2; -constexpr int GBUFFER_COUNT = 3; +constexpr int GBUFFER_MATERIAL = 3; +constexpr int GBUFFER_MATERIAL_ID = 4; +constexpr int GBUFFER_COUNT = 5; /// @brief Compute shader work group size constexpr int COMPUTE_GROUP_SIZE_X = 16; diff --git a/include/core/bvh.h b/include/core/bvh.h index e45bd07..d71f841 100644 --- a/include/core/bvh.h +++ b/include/core/bvh.h @@ -56,6 +56,24 @@ struct BVHNode { uint count_; // 0 for interior node, >0 for leaf node }; +/// @brief GPU-friendly BVH node layout (std430 aligned) +struct BVHNodeGpu { + Vec4 aabb_min_left_first_; ///< xyz = aabb min, w = left_first (uint) + Vec4 aabb_max_count_; ///< xyz = aabb max, w = count (uint, 0 for interior) +}; + +/// @brief GPU-friendly triangle layout (std430 aligned) +struct TriangleGpu { + Vec4 v0_material_; ///< xyz = v0, w = material_id (uint) + Vec4 v1_; ///< xyz = v1, w = reserved + Vec4 v2_; ///< xyz = v2, w = reserved + Vec4 n0_; ///< xyz = n0, w = reserved + Vec4 n1_; ///< xyz = n1, w = reserved + Vec4 n2_; ///< xyz = n2, w = reserved + Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1 + Vec4 uv2_; ///< xy = uv2, zw = reserved +}; + /// @brief Bounding Volume Hierarchy for ray tracing acceleration class BVH { public: diff --git a/shaders/gbuffer.frag b/shaders/gbuffer.frag index 61a7c4a..fbed580 100644 --- a/shaders/gbuffer.frag +++ b/shaders/gbuffer.frag @@ -1,11 +1,5 @@ #version 430 core -// Material types -const uint MATERIAL_DIFFUSE = 0u; -const uint MATERIAL_METAL = 1u; -const uint MATERIAL_DIELECTRIC = 2u; -const uint MATERIAL_EMISSIVE = 3u; - in VS_OUT { vec3 frag_pos; vec3 normal; @@ -17,33 +11,31 @@ layout(location = 0) out vec4 g_position; 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; -// Material uniforms uniform vec3 u_albedo; uniform float u_metallic; uniform float u_roughness; uniform float u_ior; uniform vec3 u_emission; uniform uint u_material_type; +uniform uint u_material_id; uniform bool u_has_albedo_map; uniform sampler2D u_albedo_map; void main() { - // Position g_position = vec4(fs_in.frag_pos, 1.0); - - // Normal - vec3 normal = normalize(fs_in.normal); - g_normal = vec4(normal, 0.0); - - // Albedo + + vec3 n = normalize(fs_in.normal); + g_normal = vec4(n, 0.0); + vec3 albedo = u_albedo; if (u_has_albedo_map) { albedo *= texture(u_albedo_map, fs_in.texcoord).rgb; } g_albedo = vec4(albedo, 1.0); - - // Material properties + g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type)); + g_material_id = u_material_id; } diff --git a/shaders/raytracing.comp b/shaders/raytracing.comp index afb40ba..52b9d93 100644 --- a/shaders/raytracing.comp +++ b/shaders/raytracing.comp @@ -1,26 +1,35 @@ #version 430 core -// Constants #define PI 3.14159265359 #define INV_PI 0.31830988618 #define EPSILON 1e-4 #define MAX_FLOAT 3.402823466e38 -#define MAX_RAY_DEPTH 8 -#define MAX_LIGHTS 16 #define RR_THRESHOLD 0.1 -// Material types #define MATERIAL_DIFFUSE 0 #define MATERIAL_METAL 1 #define MATERIAL_DIELECTRIC 2 #define MATERIAL_EMISSIVE 3 -// Light types #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 #define LIGHT_SPOT 2 -// Structures +layout(local_size_x = 16, local_size_y = 16) in; + +// G-Buffer inputs +layout(binding = 0, rgba32f) uniform readonly image2D g_position; +layout(binding = 1, rgba32f) uniform readonly image2D g_normal; +layout(binding = 2, rgba8) uniform readonly image2D g_albedo; + +// New: material params + material id +layout(binding = 5, rgba32f) uniform readonly image2D g_material; +layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id; + +// Output +layout(binding = 3, rgba32f) uniform image2D output_image; +layout(binding = 4, rgba32f) uniform image2D accumulation_image; + struct Material { vec3 albedo; float metallic; @@ -60,31 +69,29 @@ struct ScatterResult { bool scattered; vec3 attenuation; Ray scattered_ray; - float pdf; }; - -layout(local_size_x = 16, local_size_y = 16) in; - -// G-Buffer inputs -layout(binding = 0, rgba32f) uniform readonly image2D g_position; -layout(binding = 1, rgba32f) uniform readonly image2D g_normal; -layout(binding = 2, rgba8) uniform readonly image2D g_albedo; - -// Output -layout(binding = 3, rgba32f) uniform image2D output_image; -layout(binding = 4, rgba32f) uniform image2D accumulation_image; - -// Scene data -layout(std430, binding = 0) readonly buffer MaterialBuffer { - Material materials[]; +struct BVHNodeGpu { + vec4 aabb_min_left_first; // xyz min, w = left_first (uint bits in float) + vec4 aabb_max_count; // xyz max, w = count (uint bits in float) }; -layout(std430, binding = 1) readonly buffer LightBuffer { - Light lights[]; +struct TriangleGpu { + vec4 v0_material; // xyz v0, w material_id (uint bits in float) + vec4 v1; + vec4 v2; + vec4 n0; + vec4 n1; + vec4 n2; + vec4 uv0_uv1; // xy uv0, zw uv1 + vec4 uv2; // xy uv2 }; -// Uniforms +layout(std430, binding = 0) readonly buffer MaterialBuffer { Material materials[]; }; +layout(std430, binding = 1) readonly buffer LightBuffer { Light lights[]; }; +layout(std430, binding = 2) readonly buffer BVHNodeBuffer { BVHNodeGpu bvh_nodes[]; }; +layout(std430, binding = 3) readonly buffer TriangleBuffer { TriangleGpu bvh_tris[]; }; + uniform uint u_frame_count; uniform uint u_samples_per_pixel; uniform uint u_max_depth; @@ -93,25 +100,12 @@ uniform vec3 u_camera_position; uniform mat4 u_inv_view_projection; uniform bool u_enable_accumulation; uniform bool u_use_bvh; +uniform uint u_bvh_node_count; // ============================================================================ -// Utility Functions +// Utility // ============================================================================ -/** - * @brief Saturate float value to [0, 1] - */ -float saturate(float x) { - return clamp(x, 0.0, 1.0); -} - -/** - * @brief Saturate vec3 value to [0, 1] - */ -vec3 saturate(vec3 x) { - return clamp(x, vec3(0.0), vec3(1.0)); -} - /** * @brief Check if vector is near zero */ @@ -136,44 +130,28 @@ vec3 refract_vector(vec3 uv, vec3 n, float etai_over_etat) { return r_out_perp + r_out_parallel; } +uint as_uint(float f) { return floatBitsToUint(f); } +float as_float(uint u) { return uintBitsToFloat(u); } + // ============================================================================ -// Random Number Generation +// RNG (PCG) // ============================================================================ -/** - * @brief PCG hash function for random number generation - */ uint pcg_hash(uint seed) { uint state = seed * 747796405u + 2891336453u; uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; return (word >> 22u) ^ word; } -/** - * @brief Generate random float in [0, 1) - */ float random_float(inout uint seed) { seed = pcg_hash(seed); return float(seed) / 4294967296.0; } -/** - * @brief Generate random vec2 - */ -vec2 random_vec2(inout uint seed) { - return vec2(random_float(seed), random_float(seed)); -} - -/** - * @brief Generate random vec3 - */ vec3 random_vec3(inout uint seed) { return vec3(random_float(seed), random_float(seed), random_float(seed)); } -/** - * @brief Generate random vector in unit sphere - */ vec3 random_in_unit_sphere(inout uint seed) { while (true) { vec3 p = 2.0 * random_vec3(seed) - vec3(1.0); @@ -181,506 +159,408 @@ vec3 random_in_unit_sphere(inout uint seed) { } } -/** - * @brief Generate random unit vector - */ vec3 random_unit_vector(inout uint seed) { return normalize(random_in_unit_sphere(seed)); } // ============================================================================ -// Sampling Functions +// Camera ray // ============================================================================ /** - * @brief Cosine-weighted hemisphere sampling - * @return Sampled direction in local space (z-up) + * @brief Generate primary ray in world space */ -vec3 cosine_weighted_hemisphere(inout uint seed) { - vec2 r = random_vec2(seed); - float phi = 2.0 * PI * r.x; - float cos_theta = sqrt(r.y); - float sin_theta = sqrt(1.0 - r.y); - - return vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta); -} +Ray generate_camera_ray(ivec2 pixel_coords, ivec2 image_size, inout uint seed) { + vec2 jitter = vec2(random_float(seed), random_float(seed)); + vec2 uv = (vec2(pixel_coords) + jitter) / vec2(image_size); + vec2 ndc = uv * 2.0 - 1.0; -/** - * @brief Build orthonormal basis from normal - */ -void build_onb(vec3 normal, out vec3 tangent, out vec3 bitangent) { - vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - tangent = normalize(cross(up, normal)); - bitangent = cross(normal, tangent); -} + vec4 p_near = u_inv_view_projection * vec4(ndc, 0.0, 1.0); + vec4 p_far = u_inv_view_projection * vec4(ndc, 1.0, 1.0); + vec3 near_ws = p_near.xyz / p_near.w; + vec3 far_ws = p_far.xyz / p_far.w; -/** - * @brief Transform direction from local to world space - */ -vec3 local_to_world(vec3 local_dir, vec3 normal) { - vec3 tangent, bitangent; - build_onb(normal, tangent, bitangent); - return tangent * local_dir.x + bitangent * local_dir.y + normal * local_dir.z; -} - -/** - * @brief Sample GGX distribution for microfacet normal - */ -vec3 sample_ggx(vec3 normal, float roughness, inout uint seed) { - vec2 r = random_vec2(seed); - float a = roughness * roughness; - float a2 = a * a; - - float phi = 2.0 * PI * r.x; - float cos_theta = sqrt((1.0 - r.y) / (1.0 + (a2 - 1.0) * r.y)); - float sin_theta = sqrt(1.0 - cos_theta * cos_theta); - - vec3 local_h = vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta); - return local_to_world(local_h, normal); + Ray r; + r.origin = near_ws; + r.direction = normalize(far_ws - near_ws); + return r; } // ============================================================================ -// BRDF Functions +// Intersection // ============================================================================ /** - * @brief Schlick's approximation for Fresnel reflectance + * @brief Ray-AABB intersection */ +bool intersect_aabb(Ray ray, vec3 aabb_min, vec3 aabb_max, float t_max) { + vec3 inv_d = 1.0 / ray.direction; + vec3 t0 = (aabb_min - ray.origin) * inv_d; + vec3 t1 = (aabb_max - ray.origin) * inv_d; + + vec3 tmin3 = min(t0, t1); + vec3 tmax3 = max(t0, t1); + + float tmin = max(max(tmin3.x, tmin3.y), tmin3.z); + float tmax2 = min(min(tmax3.x, tmax3.y), tmax3.z); + + return (tmax2 >= max(tmin, 0.0)) && (tmin <= t_max); +} + +/** + * @brief Moller-Trumbore triangle intersection + */ +bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) { + vec3 v0 = tri.v0_material.xyz; + vec3 v1 = tri.v1.xyz; + vec3 v2 = tri.v2.xyz; + + vec3 e1 = v1 - v0; + vec3 e2 = v2 - v0; + vec3 pvec = cross(ray.direction, e2); + float det = dot(e1, pvec); + + if (abs(det) < EPSILON) return false; + float inv_det = 1.0 / det; + + vec3 tvec = ray.origin - v0; + float u = dot(tvec, pvec) * inv_det; + if (u < 0.0 || u > 1.0) return false; + + vec3 qvec = cross(tvec, e1); + float v = dot(ray.direction, qvec) * inv_det; + if (v < 0.0 || u + v > 1.0) return false; + + float t = dot(e2, qvec) * inv_det; + if (t < EPSILON || t >= hit.t) return false; + + // Interpolate normal/uv + float w = 1.0 - u - v; + vec3 n0 = tri.n0.xyz; + vec3 n1 = tri.n1.xyz; + vec3 n2 = tri.n2.xyz; + + vec2 uv0 = tri.uv0_uv1.xy; + vec2 uv1 = tri.uv0_uv1.zw; + vec2 uv2 = tri.uv2.xy; + + 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; + hit.material_id = as_uint(tri.v0_material.w); + return true; +} + +/** + * @brief BVH traversal (closest hit) + */ +HitInfo trace_ray_bvh(Ray ray) { + HitInfo hit; + hit.hit = false; + hit.t = MAX_FLOAT; + + if (!u_use_bvh || u_bvh_node_count == 0u) { + return hit; + } + + // Small fixed stack + uint stack[64]; + int sp = 0; + stack[sp++] = 0u; + + while (sp > 0) { + uint node_idx = stack[--sp]; + if (node_idx >= u_bvh_node_count) continue; + + BVHNodeGpu node = bvh_nodes[node_idx]; + vec3 bmin = node.aabb_min_left_first.xyz; + vec3 bmax = node.aabb_max_count.xyz; + uint left_first = as_uint(node.aabb_min_left_first.w); + uint count = as_uint(node.aabb_max_count.w); + + if (!intersect_aabb(ray, bmin, bmax, hit.t)) continue; + + if (count > 0u) { + for (uint i = 0u; i < count; ++i) { + TriangleGpu tri = bvh_tris[left_first + i]; + intersect_triangle(ray, tri, hit); + } + } else { + // Interior: push children + uint left = left_first; + uint right = left_first + 1u; + + // Depth-first; no sorting (simple) + if (sp < 63) stack[sp++] = right; + if (sp < 63) stack[sp++] = left; + } + } + + return hit; +} + +/** + * @brief Any-hit BVH for shadow ray + */ +bool trace_any_bvh(Ray ray, float t_max) { + if (!u_use_bvh || u_bvh_node_count == 0u) return false; + + uint stack[64]; + int sp = 0; + stack[sp++] = 0u; + + HitInfo hit; + hit.hit = false; + hit.t = t_max; + + while (sp > 0) { + uint node_idx = stack[--sp]; + if (node_idx >= u_bvh_node_count) continue; + + BVHNodeGpu node = bvh_nodes[node_idx]; + vec3 bmin = node.aabb_min_left_first.xyz; + vec3 bmax = node.aabb_max_count.xyz; + uint left_first = as_uint(node.aabb_min_left_first.w); + uint count = as_uint(node.aabb_max_count.w); + + if (!intersect_aabb(ray, bmin, bmax, hit.t)) continue; + + if (count > 0u) { + for (uint i = 0u; i < count; ++i) { + TriangleGpu tri = bvh_tris[left_first + i]; + if (intersect_triangle(ray, tri, hit)) { + return true; + } + } + } else { + uint left = left_first; + uint right = left_first + 1u; + if (sp < 63) stack[sp++] = right; + if (sp < 63) stack[sp++] = left; + } + } + + return false; +} + +// ============================================================================ +// Material + scattering +// ============================================================================ + vec3 fresnel_schlick(float cos_theta, vec3 f0) { return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0); } -/** - * @brief Fresnel reflectance for dielectrics - */ float fresnel_dielectric(float cos_theta, float ior) { float r0 = (1.0 - ior) / (1.0 + ior); r0 = r0 * r0; return r0 + (1.0 - r0) * pow(1.0 - cos_theta, 5.0); } -/** - * @brief GGX normal distribution function - */ -float distribution_ggx(vec3 N, vec3 H, float roughness) { - float a = roughness * roughness; - float a2 = a * a; - float NdotH = max(dot(N, H), 0.0); - float NdotH2 = NdotH * NdotH; - - float nom = a2; - float denom = (NdotH2 * (a2 - 1.0) + 1.0); - denom = PI * denom * denom; - - return nom / max(denom, EPSILON); -} - -/** - * @brief Smith's geometry function for GGX - */ -float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) { - float NdotV = max(dot(N, V), 0.0); - float NdotL = max(dot(N, L), 0.0); - float r = roughness + 1.0; - float k = (r * r) / 8.0; - - float ggx1 = NdotV / (NdotV * (1.0 - k) + k); - float ggx2 = NdotL / (NdotL * (1.0 - k) + k); - - return ggx1 * ggx2; -} - -// ============================================================================ -// Material Scattering -// ============================================================================ - -/** - * @brief Scatter ray for diffuse material - */ ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, Material mat, inout uint seed) { - ScatterResult result; - result.scattered = true; - result.attenuation = mat.albedo; - - // Cosine-weighted hemisphere sampling - vec3 local_dir = cosine_weighted_hemisphere(seed); - vec3 scatter_direction = local_to_world(local_dir, hit.normal); - - // Prevent degenerate scatter direction - if (near_zero(scatter_direction)) { - scatter_direction = hit.normal; - } - - result.scattered_ray.origin = hit.position + hit.normal * EPSILON; - result.scattered_ray.direction = normalize(scatter_direction); - result.pdf = max(dot(hit.normal, result.scattered_ray.direction), 0.0) * INV_PI; - - return result; + ScatterResult r; + r.scattered = true; + r.attenuation = mat.albedo; + + vec3 dir = hit.normal + random_unit_vector(seed); + if (near_zero(dir)) dir = hit.normal; + + r.scattered_ray.origin = hit.position + hit.normal * EPSILON; + r.scattered_ray.direction = normalize(dir); + return r; } -/** - * @brief Scatter ray for metal material - */ ScatterResult scatter_metal(Ray ray_in, HitInfo hit, Material mat, inout uint seed) { - ScatterResult result; - + ScatterResult r; + vec3 reflected = reflect_vector(normalize(ray_in.direction), hit.normal); - - // Add roughness perturbation vec3 fuzz = mat.roughness * random_in_unit_sphere(seed); - vec3 scatter_direction = reflected + fuzz; - - result.scattered = dot(scatter_direction, hit.normal) > 0.0; - - if (result.scattered) { - result.scattered_ray.origin = hit.position + hit.normal * EPSILON; - result.scattered_ray.direction = normalize(scatter_direction); - - vec3 V = -normalize(ray_in.direction); - vec3 H = normalize(V + result.scattered_ray.direction); - vec3 F0 = mat.albedo; - - result.attenuation = fresnel_schlick(max(dot(H, V), 0.0), F0); - result.pdf = 1.0; // Delta distribution approximation - } else { - result.attenuation = vec3(0.0); - result.pdf = 0.0; - } - - return result; + vec3 dir = reflected + fuzz; + + r.scattered = dot(dir, hit.normal) > 0.0; + r.attenuation = mat.albedo; + r.scattered_ray.origin = hit.position + hit.normal * EPSILON; + r.scattered_ray.direction = normalize(dir); + return r; } -/** - * @brief Scatter ray for dielectric material (glass) - */ ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout uint seed) { - ScatterResult result; - result.scattered = true; - result.attenuation = vec3(1.0); - - float refraction_ratio = dot(ray_in.direction, hit.normal) < 0.0 ? - (1.0 / mat.ior) : mat.ior; - - vec3 unit_direction = normalize(ray_in.direction); - float cos_theta = min(dot(-unit_direction, hit.normal), 1.0); - float sin_theta = sqrt(1.0 - cos_theta * cos_theta); - + ScatterResult r; + r.scattered = true; + r.attenuation = vec3(1.0); + + vec3 unit_dir = normalize(ray_in.direction); + float cos_theta = min(dot(-unit_dir, hit.normal), 1.0); + float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); + + float refraction_ratio = dot(unit_dir, hit.normal) < 0.0 ? (1.0 / mat.ior) : mat.ior; bool cannot_refract = refraction_ratio * sin_theta > 1.0; - float reflectance = fresnel_dielectric(cos_theta, refraction_ratio); - - vec3 direction; - if (cannot_refract || reflectance > random_float(seed)) { - direction = reflect_vector(unit_direction, hit.normal); + float reflect_prob = fresnel_dielectric(cos_theta, refraction_ratio); + + vec3 dir; + if (cannot_refract || random_float(seed) < reflect_prob) { + dir = reflect_vector(unit_dir, hit.normal); } else { - direction = refract_vector(unit_direction, hit.normal, refraction_ratio); + dir = refract_vector(unit_dir, hit.normal, refraction_ratio); } - - result.scattered_ray.origin = hit.position + direction * EPSILON; - result.scattered_ray.direction = normalize(direction); - result.pdf = 1.0; // Delta distribution - - return result; + + r.scattered_ray.origin = hit.position + dir * EPSILON; + r.scattered_ray.direction = normalize(dir); + return r; } -/** - * @brief Scatter ray based on material type - */ ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed) { - if (mat.type == MATERIAL_DIFFUSE) { - return scatter_diffuse(ray_in, hit, mat, seed); - } else if (mat.type == MATERIAL_METAL) { - return scatter_metal(ray_in, hit, mat, seed); - } else if (mat.type == MATERIAL_DIELECTRIC) { - return scatter_dielectric(ray_in, hit, mat, seed); - } else { - // Emissive material doesn't scatter - ScatterResult result; - result.scattered = false; - result.attenuation = vec3(0.0); - result.pdf = 0.0; - return result; - } + if (mat.type == MATERIAL_DIFFUSE) return scatter_diffuse(ray_in, hit, mat, seed); + if (mat.type == MATERIAL_METAL) return scatter_metal(ray_in, hit, mat, seed); + if (mat.type == MATERIAL_DIELECTRIC) return scatter_dielectric(ray_in, hit, mat, seed); + + ScatterResult r; + r.scattered = false; + r.attenuation = vec3(0.0); + return r; } // ============================================================================ -// Scene Intersection (G-Buffer based) +// Direct lighting (with shadow ray) // ============================================================================ -/** - * @brief Trace ray against G-Buffer (single bounce only) - * @note This is a simplified version - full path tracing needs scene geometry - */ -HitInfo trace_ray_gbuffer(Ray ray, ivec2 pixel_coords) { - HitInfo hit; - - // Read from G-Buffer at current pixel - vec4 position_data = imageLoad(g_position, pixel_coords); - vec4 normal_data = imageLoad(g_normal, pixel_coords); - vec4 albedo_data = imageLoad(g_albedo, pixel_coords); - - if (position_data.w > 0.5) { - hit.hit = true; - hit.position = position_data.xyz; - hit.normal = normalize(normal_data.xyz); - hit.material_id = uint(albedo_data.a * 255.0 + 0.5); - hit.t = length(hit.position - ray.origin); - } else { - hit.hit = false; - hit.t = MAX_FLOAT; - } - - return hit; -} - -// ============================================================================ -// Path Tracing Core -// ============================================================================ - - -/** - * @brief Sample direct lighting from light sources - */ -vec3 sample_direct_lighting(vec3 position, vec3 normal, Material mat, inout uint seed) { +vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) { if (u_light_count == 0u) return vec3(0.0); - - vec3 direct_light = vec3(0.0); - - // Sample one random light (could be improved with MIS) + + // sample one light uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count; Light light = lights[light_idx]; - - vec3 light_dir; - float light_distance; - float pdf_light = 1.0 / float(u_light_count); - + + vec3 L; + float dist = MAX_FLOAT; + vec3 radiance = vec3(0.0); + if (light.type == LIGHT_POINT) { - vec3 to_light = light.position - position; - light_distance = length(to_light); - light_dir = to_light / light_distance; - - if (light_distance > light.range) return vec3(0.0); - - float NdotL = max(dot(normal, light_dir), 0.0); - if (NdotL > 0.0) { - float attenuation = 1.0 / max(light_distance * light_distance, 0.01); - vec3 radiance = light.color * light.intensity * attenuation; - - // Simple BRDF evaluation (diffuse) - direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light; - } + vec3 to_light = light.position - hit.position; + dist = length(to_light); + if (dist > light.range) return vec3(0.0); + L = to_light / dist; + + float atten = 1.0 / max(dist * dist, 0.01); + radiance = light.color * light.intensity * atten; } else if (light.type == LIGHT_DIRECTIONAL) { - light_dir = normalize(-light.direction); - float NdotL = max(dot(normal, light_dir), 0.0); - - if (NdotL > 0.0) { - vec3 radiance = light.color * light.intensity; - direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light; - } + L = normalize(-light.direction); + radiance = light.color * light.intensity; + } else { + return vec3(0.0); } - - return direct_light; + + float n_dot_l = max(dot(hit.normal, L), 0.0); + if (n_dot_l <= 0.0) return vec3(0.0); + + // shadow ray + Ray shadow_ray; + shadow_ray.origin = hit.position + hit.normal * EPSILON; + shadow_ray.direction = L; + + float t_max = (light.type == LIGHT_POINT) ? (dist - EPSILON) : MAX_FLOAT; + if (trace_any_bvh(shadow_ray, t_max)) { + return vec3(0.0); + } + + float pdf_light = 1.0 / float(u_light_count); + vec3 brdf = mat.albedo * INV_PI; // diffuse direct only (simple) + return brdf * radiance * n_dot_l / max(pdf_light, EPSILON); } -/** - * @brief Trace path and accumulate radiance - */ -vec3 trace_path(Ray initial_ray, ivec2 pixel_coords, inout uint seed) { +// ============================================================================ +// Path tracing +// ============================================================================ + +Material fetch_material(uint material_id) { + uint cnt = uint(materials.length()); + if (material_id < cnt) return materials[material_id]; + + Material m; + m.albedo = vec3(0.5); + m.metallic = 0.0; + m.emission = vec3(0.0); + m.roughness = 0.5; + m.type = MATERIAL_DIFFUSE; + m.ior = 1.5; + return m; +} + +vec3 environment_color(vec3 dir) { + // simple dark sky + return vec3(0.1, 0.1, 0.15); +} + +vec3 trace_path(Ray ray, inout uint seed) { vec3 radiance = vec3(0.0); vec3 throughput = vec3(1.0); - Ray current_ray = initial_ray; - - uint mat_count = uint(materials.length()); - - for (uint depth = 0u; depth < u_max_depth; depth++) { - // Trace ray (only first bounce uses G-Buffer) - HitInfo hit; - if (depth == 0u) { - hit = trace_ray_gbuffer(current_ray, pixel_coords); - } else { - // For subsequent bounces, we can't trace without full scene geometry - // This is a limitation of G-Buffer based approach - // In a full path tracer, you'd trace against the actual scene here - hit.hit = false; - } - - if (!hit.hit) { - // Hit sky/background - vec3 sky_color = vec3(0.1, 0.1, 0.15); - radiance += throughput * sky_color; - break; - } - - // Get material - Material mat; - if (hit.material_id < mat_count) { - mat = materials[hit.material_id]; - } else { - // Fallback material - mat.albedo = vec3(0.5); - mat.metallic = 0.0; - mat.roughness = 0.5; - mat.emission = vec3(0.0); - mat.type = MATERIAL_DIFFUSE; - mat.ior = 1.5; - } - - // Add emission - radiance += throughput * mat.emission; - - // Sample direct lighting (only for diffuse surfaces) - if (mat.type == MATERIAL_DIFFUSE && depth == 0u) { - radiance += throughput * sample_direct_lighting(hit.position, hit.normal, mat, seed); - } - - // Scatter ray - ScatterResult scatter = scatter_ray(current_ray, hit, mat, seed); - - if (!scatter.scattered || scatter.pdf < EPSILON) { - break; - } - - // Update throughput - throughput *= scatter.attenuation; - - // Russian roulette path termination - if (depth > 3u) { - float rr_probability = max(throughput.r, max(throughput.g, throughput.b)); - if (rr_probability < RR_THRESHOLD || random_float(seed) > rr_probability) { - break; - } - throughput /= rr_probability; - } - - // Continue with scattered ray - current_ray = scatter.scattered_ray; - - // Safety check for throughput - if (all(lessThan(throughput, vec3(EPSILON)))) { - break; - } - } - - return radiance; -} -/** - * @brief Enhanced direct lighting with G-Buffer - */ -vec3 render_direct_lighting(ivec2 pixel_coords, inout uint seed) { - // Read G-Buffer - vec4 position_data = imageLoad(g_position, pixel_coords); - vec4 normal_data = imageLoad(g_normal, pixel_coords); - vec4 albedo_data = imageLoad(g_albedo, pixel_coords); - - if (position_data.w < 0.5) { - return vec3(0.1, 0.1, 0.15); // Sky - } - - vec3 position = position_data.xyz; - vec3 normal = normalize(normal_data.xyz); - uint material_id = uint(albedo_data.a * 255.0 + 0.5); - - // Get material - Material mat; - uint mat_count = uint(materials.length()); - - if (material_id < mat_count) { - mat = materials[material_id]; - } else { - // Fallback: use G-Buffer albedo - mat.albedo = albedo_data.rgb; - mat.metallic = 0.0; - mat.roughness = 0.5; - mat.emission = vec3(0.0); - mat.type = MATERIAL_DIFFUSE; - mat.ior = 1.5; - } - - vec3 color = vec3(0.0); - - // Emission - color += mat.emission; - - // View direction - vec3 view_dir = normalize(u_camera_position - position); - - // Direct lighting from all lights - for (uint i = 0u; i < u_light_count; i++) { - Light light = lights[i]; - vec3 light_dir; - float light_distance; - float attenuation = 1.0; - - if (light.type == LIGHT_POINT) { - vec3 to_light = light.position - position; - light_distance = length(to_light); - light_dir = to_light / light_distance; - attenuation = 1.0 / max(light_distance * light_distance, 0.01); - - if (light_distance > light.range) continue; - } else if (light.type == LIGHT_DIRECTIONAL) { - light_dir = normalize(-light.direction); - light_distance = MAX_FLOAT; - } else { - continue; + for (uint depth = 0u; depth < u_max_depth; ++depth) { + HitInfo hit = trace_ray_bvh(ray); + if (!hit.hit) { + radiance += throughput * environment_color(ray.direction); + break; } - - float NdotL = max(dot(normal, light_dir), 0.0); - - if (NdotL > 0.0) { - vec3 H = normalize(view_dir + light_dir); - float NdotV = max(dot(normal, view_dir), 0.0); - float NdotH = max(dot(normal, H), 0.0); - float HdotV = max(dot(H, view_dir), 0.0); - - // Cook-Torrance BRDF - vec3 F0 = mix(vec3(0.04), mat.albedo, mat.metallic); - vec3 F = fresnel_schlick(HdotV, F0); - float D = distribution_ggx(normal, H, max(mat.roughness, 0.04)); - float G = geometry_smith(normal, view_dir, light_dir, mat.roughness); - - vec3 numerator = D * G * F; - float denominator = 4.0 * NdotV * NdotL + EPSILON; - vec3 specular = numerator / denominator; - - vec3 kS = F; - vec3 kD = (vec3(1.0) - kS) * (1.0 - mat.metallic); - - vec3 radiance = light.color * light.intensity * attenuation; - color += (kD * mat.albedo * INV_PI + specular) * radiance * NdotL; + + Material mat = fetch_material(hit.material_id); + + // emission + radiance += throughput * mat.emission; + + // direct light (only for diffuse to keep simple) + if (mat.type == MATERIAL_DIFFUSE) { + radiance += throughput * eval_direct_lighting(hit, mat, seed); } + + ScatterResult sc = scatter_ray(ray, hit, mat, seed); + if (!sc.scattered) break; + + throughput *= sc.attenuation; + + // RR + if (depth > 3u) { + float p = max(throughput.r, max(throughput.g, throughput.b)); + p = clamp(p, 0.0, 0.95); + if (p < RR_THRESHOLD || random_float(seed) > p) break; + throughput /= p; + } + + ray = sc.scattered_ray; + + if (all(lessThan(throughput, vec3(EPSILON)))) break; } - - // Ambient occlusion approximation - color += mat.albedo * 0.03; - - return color; + + return radiance; } void main() { ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); ivec2 image_size = imageSize(output_image); - - if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) { - return; - } - - // Initialize random seed + if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) return; + uint base_seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x); uint seed = base_seed + u_frame_count * 719393u; - - vec3 color = render_direct_lighting(pixel_coords, seed); - - // Clamp + + vec3 color = vec3(0.0); + + // Multi-sample + uint spp = max(u_samples_per_pixel, 1u); + for (uint s = 0u; s < spp; ++s) { + Ray cam_ray = generate_camera_ray(pixel_coords, image_size, seed); + color += trace_path(cam_ray, seed); + } + color /= float(spp); + color = clamp(color, vec3(0.0), vec3(10.0)); - - // Accumulation + if (u_enable_accumulation && u_frame_count > 0u) { vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb; - float weight = 1.0 / float(u_frame_count + 1u); - color = mix(accumulated, color, weight); + float w = 1.0 / float(u_frame_count + 1u); + color = mix(accumulated, color, w); } - + imageStore(accumulation_image, pixel_coords, vec4(color, 1.0)); imageStore(output_image, pixel_coords, vec4(color, 1.0)); } diff --git a/src/core/bvh.cpp b/src/core/bvh.cpp index d8dfbf6..e1c747e 100644 --- a/src/core/bvh.cpp +++ b/src/core/bvh.cpp @@ -257,33 +257,62 @@ bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) { Logger::error("Cannot upload empty BVH to GPU"); return false; } - + // Reorder triangles according to BVH layout std::vector ordered_triangles; ordered_triangles.reserve(triangles_.size()); - for (uint idx : triangle_indices_) { ordered_triangles.push_back(triangles_[idx]); } - - // Upload nodes + + // Pack nodes to GPU layout + std::vector 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 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); + + 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); + + tri_gpu[i] = g; + } + if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER, - nodes_.size() * sizeof(BVHNode), - nodes_.data(), - BufferUsage::STATIC_DRAW)) { + node_gpu.size() * sizeof(BVHNodeGpu), + node_gpu.data(), + BufferUsage::STATIC_DRAW)) { Logger::error("Failed to upload BVH nodes to GPU"); return false; } - - // Upload triangles + if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER, - ordered_triangles.size() * sizeof(Triangle), - ordered_triangles.data(), - BufferUsage::STATIC_DRAW)) { + tri_gpu.size() * sizeof(TriangleGpu), + tri_gpu.data(), + BufferUsage::STATIC_DRAW)) { Logger::error("Failed to upload BVH triangles to GPU"); return false; } - + Logger::info("BVH uploaded to GPU successfully"); return true; } diff --git a/src/core/gbuffer.cpp b/src/core/gbuffer.cpp index b3e01d0..fb96df0 100644 --- a/src/core/gbuffer.cpp +++ b/src/core/gbuffer.cpp @@ -24,51 +24,57 @@ bool GBuffer::initialize() { Logger::warning("GBuffer already initialized"); return true; } - - // Create framebuffer + glGenFramebuffers(1, &fbo_); glBindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // Create G-Buffer textures + 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); - - // Attach textures to framebuffer + 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 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); + GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL, - GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0); + GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO, - GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0); - - // Create depth texture + 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_); 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); + 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); - - // Set draw buffers + GL_TEXTURE_2D, depth_texture_, 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_ALBEDO, + GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL, + GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID }; glDrawBuffers(GBUFFER_COUNT, draw_buffers); - - // Check framebuffer completeness + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { Logger::error("GBuffer framebuffer is not complete"); glBindFramebuffer(GL_FRAMEBUFFER, 0); return false; } - + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + initialized_ = true; Logger::info("GBuffer initialized successfully"); return true; @@ -76,28 +82,40 @@ bool GBuffer::initialize() { void GBuffer::release() { if (!initialized_) return; - + 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; } } - + if (depth_texture_ != INVALID_HANDLE) { glDeleteTextures(1, &depth_texture_); depth_texture_ = INVALID_HANDLE; } - + initialized_ = false; Logger::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; +} + void GBuffer::render(const Scene& scene, const Shader& shader) { if (!initialized_) { Logger::error("GBuffer not initialized"); @@ -150,6 +168,9 @@ void GBuffer::render(const Scene& scene, const Shader& shader) { 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(material->get_type())); // Bind textures auto albedo_tex = material->get_albedo_texture(); @@ -206,16 +227,4 @@ void GBuffer::get_dimensions(uint& width, uint& height) const { height = height_; } -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; -} - } // namespace are diff --git a/src/core/raytracer.cpp b/src/core/raytracer.cpp index a425b58..4525357 100644 --- a/src/core/raytracer.cpp +++ b/src/core/raytracer.cpp @@ -304,8 +304,11 @@ void RayTracer::upload_scene_data_(const Scene& scene) { void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) { glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); - glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); - glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); + glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); + + 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); } void RayTracer::set_compute_shader(const Shader& shader) { diff --git a/src/core/shader_manager.cpp b/src/core/shader_manager.cpp index 15c41a9..88cd629 100644 --- a/src/core/shader_manager.cpp +++ b/src/core/shader_manager.cpp @@ -104,15 +104,8 @@ bool ShaderManager::load_builtin_shaders_() { return false; } shader_cache_["gbuffer"] = gbuffer_shader_; - - // Load ray tracing compute shader - if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) { - Logger::error("Failed to load ray tracing shader"); - return false; - } - shader_cache_["raytracing"] = raytracing_shader_; - // Load ray tracing compute shader + // Load ray tracing compute shader Logger::info("Loading ray tracing compute shader..."); if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) { Logger::error("Failed to load ray tracing shader"); @@ -120,7 +113,7 @@ bool ShaderManager::load_builtin_shaders_() { } shader_cache_["raytracing"] = raytracing_shader_; Logger::info("Ray tracing shader loaded successfully"); - + return true; } diff --git a/write.sh b/write.sh new file mode 100644 index 0000000..8d08aa4 --- /dev/null +++ b/write.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# query.sh - 遍历指定文件夹中的 .h 文件并生成 all_headers.md + +# 检查是否提供了目录参数 +if [ $# -ne 1 ]; then + echo "用法: $0 <目标文件夹路径>" + exit 1 +fi + +TARGET_DIR="$1" + +# 检查提供的路径是否为一个存在的目录 +if [ ! -d "$TARGET_DIR" ]; then + echo "错误: 目录 '$TARGET_DIR' 不存在。" + exit 1 +fi + +# 输出文件 +OUTPUT_FILE="all_files.md" + +# 清空或创建输出文件 +> "$OUTPUT_FILE" + +echo "正在扫描目录: $TARGET_DIR" +# 使用 find 命令查找所有 .h 文件 +H_FILES=$(find "$TARGET_DIR" -type f -name "*.cpp") + +# 检查是否找到了 .h 文件 +if [ -z "$H_FILES" ]; then + echo "在目录 '$TARGET_DIR' 及其子目录中未找到任何 .h 文件。" + exit 0 +fi + +# 遍历找到的每个 .h 文件 +for header_file in $H_FILES; do + # 获取相对于脚本执行位置的相对路径 + RELATIVE_PATH=$(realpath --relative-to=. "$header_file") + + # 写入分隔符和文件名 + { + echo "### 文件:$RELATIVE_PATH" + echo "" + echo '```cpp' + cat "$header_file" + echo '```' + echo "" # 添加一个空行,使文件之间有分隔 + } >> "$OUTPUT_FILE" + + echo "已处理: $RELATIVE_PATH" +done + +echo "" +echo "处理完成!所有头文件内容已合并到 $OUTPUT_FILE 中。"