From c1c062180d4d5221461a02336ef32584aa14edfc Mon Sep 17 00:00:00 2001 From: ternaryop8479 Date: Sun, 8 Feb 2026 21:46:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=85=89=E6=A0=85=E5=8C=96?= =?UTF-8?q?=E6=B8=B2=E6=9F=93G-Buffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 4 + all_headers.md | 4336 +++++++++++++++++ examples/01_phase2_test/CMakeLists.txt | 10 + examples/01_phase2_test/main.cpp | 326 ++ examples/02_phase3_test/CMakeLists.txt | 18 + examples/02_phase3_test/main.cpp | 478 ++ examples/02_visual_test/CMakeLists.txt | 10 + examples/02_visual_test/main.cpp | 329 ++ examples/03_phase4_test/CMakeLists.txt | 10 + examples/03_phase4_test/main.cpp | 410 ++ files.md | 1831 +++++++ include/are/acceleration/bvh.h | 12 + .../are/{renderer => rasterizer}/gbuffer.h | 0 include/are/raytracer/compute_raytracer.h | 4 +- include/are/renderer/renderer.h | 2 + include/are/texture/sampler.h | 5 +- new_conversation.md | 4 +- phase3.md | 605 +++ phase4.md | 270 + phase5.md | 310 ++ request2.md | 322 ++ shaders/gbuffer/gbuffer.frag | 33 + shaders/gbuffer/gbuffer.vert | 35 + src/acceleration/bvh.cpp | 464 ++ src/acceleration/bvh_builder.cpp | 198 + src/acceleration/bvh_node.cpp | 13 + src/geometry/aabb.cpp | 66 +- src/geometry/transform.cpp | 74 +- src/geometry/triangle.cpp | 224 +- src/geometry/vertex.cpp | 5 +- src/rasterizer/gbuffer.cpp | 252 + src/rasterizer/rasterizer.cpp | 281 ++ src/rasterizer/shader_program.cpp | 251 + src/raytracer/cpu_raytracer.cpp | 306 ++ src/raytracer/hit_record.cpp | 34 + src/raytracer/ray.cpp | 41 + src/raytracer/raytracer.cpp | 22 + src/scene/camera.cpp | 88 +- src/scene/directional_light.cpp | 59 + src/scene/light.cpp | 35 + src/scene/material.cpp | 28 +- src/scene/mesh.cpp | 202 + src/scene/point_light.cpp | 97 + src/scene/scene_manager.cpp | 303 ++ src/scene/spot_light.cpp | 152 + src/texture/compute_texture.cpp | 61 + src/texture/sampler.cpp | 35 + src/texture/texture.cpp | 205 + src/texture/texture_manager.cpp | 191 + write.sh | 54 + 50 files changed, 12916 insertions(+), 189 deletions(-) create mode 100644 all_headers.md create mode 100644 examples/01_phase2_test/CMakeLists.txt create mode 100644 examples/01_phase2_test/main.cpp create mode 100644 examples/02_phase3_test/CMakeLists.txt create mode 100644 examples/02_phase3_test/main.cpp create mode 100644 examples/02_visual_test/CMakeLists.txt create mode 100644 examples/02_visual_test/main.cpp create mode 100644 examples/03_phase4_test/CMakeLists.txt create mode 100644 examples/03_phase4_test/main.cpp rename include/are/{renderer => rasterizer}/gbuffer.h (100%) create mode 100644 phase3.md create mode 100644 phase4.md create mode 100644 phase5.md create mode 100644 request2.md create mode 100644 shaders/gbuffer/gbuffer.frag create mode 100644 shaders/gbuffer/gbuffer.vert create mode 100644 src/acceleration/bvh.cpp create mode 100644 src/acceleration/bvh_builder.cpp create mode 100644 src/acceleration/bvh_node.cpp create mode 100644 src/rasterizer/gbuffer.cpp create mode 100644 src/rasterizer/rasterizer.cpp create mode 100644 src/rasterizer/shader_program.cpp create mode 100644 src/raytracer/cpu_raytracer.cpp create mode 100644 src/raytracer/hit_record.cpp create mode 100644 src/raytracer/ray.cpp create mode 100644 src/raytracer/raytracer.cpp create mode 100644 src/scene/directional_light.cpp create mode 100644 src/scene/light.cpp create mode 100644 src/scene/mesh.cpp create mode 100644 src/scene/point_light.cpp create mode 100644 src/scene/scene_manager.cpp create mode 100644 src/scene/spot_light.cpp create mode 100644 src/texture/compute_texture.cpp create mode 100644 src/texture/sampler.cpp create mode 100644 src/texture/texture.cpp create mode 100644 src/texture/texture_manager.cpp create mode 100644 write.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index c5dbc77..1ed5c3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,10 @@ if(ARE_BUILD_EXAMPLES) # Add example subdirectories add_subdirectory(examples/00_phase1_test) + add_subdirectory(examples/01_phase2_test) + add_subdirectory(examples/02_visual_test) + add_subdirectory(examples/02_phase3_test) + add_subdirectory(examples/03_phase4_test) message(STATUS "Examples will be built") endif() diff --git a/all_headers.md b/all_headers.md new file mode 100644 index 0000000..60bcb6c --- /dev/null +++ b/all_headers.md @@ -0,0 +1,4336 @@ +我其实已经实现了所有头文件了,包括所有模块的所有接口以及使用方法都在里面,所以让我直接给你所有头文件的完整源码吧: + +### 文件:include/are/are.h + +```cpp +/** + * @file are.h + * @brief Aurora Rendering Engine - Main header file + * + * This header includes all public interfaces of the Aurora Rendering Engine. + * Users only need to include this single header to access all functionality. + * + * @author Ternary_Operator + * @version 1.0 + */ + +#ifndef ARE_INCLUDE_ARE_H +#define ARE_INCLUDE_ARE_H + +// Core modules +#include +#include +#include +#include + +// Platform modules +#include +#include + +// Geometry modules +#include +#include +#include +#include + +// Scene modules +#include +#include +#include +#include +#include +#include +#include +#include + +// Acceleration modules +#include + +// Renderer modules +#include +#include + +// Utility modules +#include +#include + +/** + * @namespace are + * @brief Main namespace for Aurora Rendering Engine + */ +namespace are { + +/** + * @brief Get the version string of the engine + * @return Version string in format "major.minor.patch" + */ +const char* get_version(); + +/** + * @brief Initialize the Aurora Rendering Engine + * @return true if initialization succeeded, false otherwise + */ +bool initialize(); + +/** + * @brief Shutdown the Aurora Rendering Engine + */ +void shutdown(); + +} // namespace are + +#endif // ARE_INCLUDE_ARE_H +``` + +### 文件:include/are/core/types.h + +```cpp +/** + * @file types.h + * @brief Basic type definitions and constants + */ + +#ifndef ARE_INCLUDE_CORE_TYPES_H +#define ARE_INCLUDE_CORE_TYPES_H + +#include +#include + +namespace are { + +// Floating point precision +using Real = float; + +// Common vector types +using Vec2 = glm::vec2; +using Vec3 = glm::vec3; +using Vec4 = glm::vec4; + +using Vec2i = glm::ivec2; +using Vec3i = glm::ivec3; +using Vec4i = glm::ivec4; + +// Matrix types +using Mat3 = glm::mat3; +using Mat4 = glm::mat4; + +// Color type (RGBA) +using Color = Vec4; + +// Handle types for resource management +using MeshHandle = uint32_t; +using MaterialHandle = uint32_t; +using LightHandle = uint32_t; +using TextureHandle = uint32_t; + +// Invalid handle constant +constexpr uint32_t are_invalid_handle = 0xFFFFFFFF; + +// Mathematical constants +constexpr Real are_pi = 3.14159265358979323846f; +constexpr Real are_two_pi = 6.28318530717958647692f; +constexpr Real are_inv_pi = 0.31830988618379067154f; +constexpr Real are_inv_two_pi = 0.15915494309189533577f; +constexpr Real are_epsilon = 1e-6f; + +// Ray tracing backend types +enum class RayTracingBackend { + ARE_RT_BACKEND_CPU, + ARE_RT_BACKEND_COMPUTE_SHADER +}; + +// Tone mapping operators +enum class ToneMappingOperator { + ARE_TONEMAP_NONE, + ARE_TONEMAP_REINHARD, + ARE_TONEMAP_ACES +}; + +// G-Buffer visualization modes +enum class GBufferVisualizationMode { + ARE_GBUFFER_VIS_NONE, + ARE_GBUFFER_VIS_POSITION, + ARE_GBUFFER_VIS_NORMAL, + ARE_GBUFFER_VIS_ALBEDO, + ARE_GBUFFER_VIS_METALLIC, + ARE_GBUFFER_VIS_ROUGHNESS, + ARE_GBUFFER_VIS_DEPTH +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_TYPES_H +``` + +### 文件:include/are/core/config.h + +```cpp +/** + * @file config.h + * @brief Configuration system for the rendering engine + */ + +#ifndef ARE_INCLUDE_CORE_CONFIG_H +#define ARE_INCLUDE_CORE_CONFIG_H + +#include +#include + +namespace are { + +/** + * @struct WindowConfig + * @brief Configuration for window creation + */ +struct WindowConfig { + int width = 1280; ///< Window width in pixels + int height = 720; ///< Window height in pixels + std::string title = "Aurora Rendering Engine"; ///< Window title + bool resizable = true; ///< Whether window is resizable + bool vsync = true; ///< Enable vertical synchronization + int samples = 1; ///< MSAA samples (1 = disabled) +}; + +/** + * @struct RayTracingConfig + * @brief Configuration for ray tracing + */ +struct RayTracingConfig { + RayTracingBackend backend = RayTracingBackend::ARE_RT_BACKEND_COMPUTE_SHADER; + int spp = 64; ///< Samples per pixel + int max_depth = 8; ///< Maximum ray bounce depth + bool enable_gi = true; ///< Enable global illumination + bool enable_ao = true; ///< Enable ambient occlusion + bool enable_soft_shadows = false; ///< Enable soft shadows + int ao_samples = 16; ///< AO sample count + Real ao_radius = 1.0f; ///< AO sampling radius +}; + +/** + * @struct RenderConfig + * @brief General rendering configuration + */ +struct RenderConfig { + ToneMappingOperator tonemap_op = ToneMappingOperator::ARE_TONEMAP_ACES; + Real exposure = 1.0f; ///< Exposure value for tone mapping + bool use_hdr = true; ///< Use HDR rendering pipeline + GBufferVisualizationMode gbuffer_vis_mode = GBufferVisualizationMode::ARE_GBUFFER_VIS_NONE; +}; + +/** + * @struct PerformanceConfig + * @brief Performance-related configuration + */ +struct PerformanceConfig { + int num_threads = 0; ///< Number of threads (0 = auto-detect) + bool enable_bvh_multithreading = true; ///< Use multithreading for BVH construction + bool enable_profiling = false; ///< Enable performance profiling +}; + +/** + * @struct PathConfig + * @brief File path configuration + */ +struct PathConfig { + std::string shader_dir = "shaders/"; ///< Directory containing shader files + std::string texture_dir = "textures/"; ///< Default texture directory + std::string output_dir = "output/"; ///< Default output directory +}; + +/** + * @class AreConfig + * @brief Main configuration class for Aurora Rendering Engine + */ +class AreConfig { +public: + WindowConfig window; ///< Window configuration + RayTracingConfig ray_tracing; ///< Ray tracing configuration + RenderConfig render; ///< Rendering configuration + PerformanceConfig performance; ///< Performance configuration + PathConfig paths; ///< Path configuration + + /** + * @brief Constructor with default values + */ + AreConfig() = default; + + /** + * @brief Validate configuration values + * @return true if configuration is valid, false otherwise + */ + bool validate() const; + + /** + * @brief Print configuration to console + */ + void print() const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_CONFIG_H +``` + +### 文件:include/are/core/logger.h + +```cpp +/** + * @file logger.h + * @brief Logging system for the rendering engine + */ + +#ifndef ARE_INCLUDE_CORE_LOGGER_H +#define ARE_INCLUDE_CORE_LOGGER_H + +#include +#include + +namespace are { + +/** + * @enum LogLevel + * @brief Logging severity levels + */ +enum class LogLevel { + ARE_LOG_TRACE, + ARE_LOG_DEBUG, + ARE_LOG_INFO, + ARE_LOG_WARN, + ARE_LOG_ERROR, + ARE_LOG_CRITICAL +}; + +/** + * @class Logger + * @brief Thread-safe logging system + * + * This class provides a simple interface for logging messages with different + * severity levels. It wraps spdlog for actual logging functionality. + */ +class Logger { +public: + /** + * @brief Initialize the logging system + * @param min_level Minimum log level to display + */ + static void init(LogLevel min_level = LogLevel::ARE_LOG_INFO); + + /** + * @brief Shutdown the logging system + */ + static void shutdown(); + + /** + * @brief Log a message with file/function/line information + * @param level Log severity level + * @param file Source file name + * @param func Function name + * @param line Line number + * @param message Log message + */ + static void log(LogLevel level, const char* file, const char* func, + int line, const std::string& message); + + /** + * @brief Set minimum log level + * @param level Minimum log level to display + */ + static void set_level(LogLevel level); + +private: + static std::shared_ptr logger_impl_; ///< Internal logger implementation + static bool initialized_; ///< Initialization flag +}; + +} // namespace are + +// Logging macros +#define ARE_LOG_TRACE(msg) are::Logger::log(are::LogLevel::ARE_LOG_TRACE, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_DEBUG(msg) are::Logger::log(are::LogLevel::ARE_LOG_DEBUG, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_INFO(msg) are::Logger::log(are::LogLevel::ARE_LOG_INFO, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_WARN(msg) are::Logger::log(are::LogLevel::ARE_LOG_WARN, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_ERROR(msg) are::Logger::log(are::LogLevel::ARE_LOG_ERROR, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_CRITICAL(msg) are::Logger::log(are::LogLevel::ARE_LOG_CRITICAL, __FILE__, __func__, __LINE__, msg) + +#endif // ARE_INCLUDE_CORE_LOGGER_H +``` + +### 文件:include/are/core/profiler.h + +```cpp +/** + * @file profiler.h + * @brief Performance profiling utilities + */ + +#ifndef ARE_INCLUDE_CORE_PROFILER_H +#define ARE_INCLUDE_CORE_PROFILER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @struct ProfileResult + * @brief Result of a profiling measurement + */ +struct ProfileResult { + std::string name_; ///< Profile section name + double duration_ms_; ///< Duration in milliseconds + uint64_t call_count_; ///< Number of times called + double avg_duration_ms_; ///< Average duration per call +}; + +/** + * @class Profiler + * @brief Simple performance profiler + * + * This class provides basic timing functionality for performance analysis. + * It is only active when ARE_ENABLE_PROFILING is defined. + */ +class Profiler { +public: + /** + * @brief Initialize the profiler + */ + static void init(); + + /** + * @brief Shutdown the profiler and print results + */ + static void shutdown(); + + /** + * @brief Begin a profiling section + * @param name Section name + */ + static void begin(const std::string& name); + + /** + * @brief End a profiling section + * @param name Section name + */ + static void end(const std::string& name); + + /** + * @brief Get profiling results + * @return Map of section names to profile results + */ + static const std::unordered_map& get_results(); + + /** + * @brief Reset all profiling data + */ + static void reset(); + + /** + * @brief Print profiling results to console + */ + static void print_results(); + +private: + struct SectionData { + std::chrono::high_resolution_clock::time_point start_time_; + double total_duration_ms_ = 0.0; + uint64_t call_count_ = 0; + }; + + static std::unordered_map sections_; + static std::unordered_map results_; + static bool enabled_; +}; + +/** + * @class ScopedProfiler + * @brief RAII-style profiler for automatic timing + */ +class ScopedProfiler { +public: + /** + * @brief Constructor - begins profiling + * @param name Section name + */ + explicit ScopedProfiler(const std::string& name); + + /** + * @brief Destructor - ends profiling + */ + ~ScopedProfiler(); + +private: + std::string name_; +}; + +} // namespace are + +// Profiling macros +#ifdef ARE_ENABLE_PROFILING + #define ARE_PROFILE_BEGIN(name) are::Profiler::begin(name) + #define ARE_PROFILE_END(name) are::Profiler::end(name) + #define ARE_PROFILE_SCOPE(name) are::ScopedProfiler are_profiler_##__LINE__(name) + #define ARE_PROFILE_FUNCTION() ARE_PROFILE_SCOPE(__func__) +#else + #define ARE_PROFILE_BEGIN(name) ((void)0) + #define ARE_PROFILE_END(name) ((void)0) + #define ARE_PROFILE_SCOPE(name) ((void)0) + #define ARE_PROFILE_FUNCTION() ((void)0) +#endif + +#endif // ARE_INCLUDE_CORE_PROFILER_H +``` + +### 文件:include/are/geometry/vertex.h + +```cpp +/** + * @file vertex.h + * @brief Vertex data structure definition + */ + +#ifndef ARE_INCLUDE_GEOMETRY_VERTEX_H +#define ARE_INCLUDE_GEOMETRY_VERTEX_H + +#include + +namespace are { + +/** + * @struct Vertex + * @brief Standard vertex structure for mesh data + * + * Contains position, normal, texture coordinates, and tangent information + * for PBR rendering pipeline. + */ +struct Vertex { + Vec3 position_;///< Vertex position in object space + Vec3 normal_; ///< Vertex normal (normalized) + Vec2 texcoord_; ///< Texture coordinates (UV) + Vec3 tangent_; ///< Tangent vector for normal mapping + + Vertex() = default; + + /** + * @brief Construct vertex with position only + * @param pos Position + */ + explicit Vertex(const Vec3& pos); + + /** + * @brief Construct vertex with position and normal + * @param pos Position + * @param norm Normal + */ + Vertex(const Vec3& pos, const Vec3& norm); + + /** + * @brief Construct vertex with position, normal, and texcoord + * @param pos Position + * @param norm Normal + * @param uv Texture coordinates + */ + Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv); + + /** + * @brief Construct vertex with all attributes + * @param pos Position + * @param norm Normal + * @param uv Texture coordinates + * @param tan Tangent + */ + Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv, const Vec3& tan); + + /** + * @brief Interpolate between two vertices + * @param a First vertex + * @param b Second vertex + * @param t Interpolation factor [0, 1] + * @return Interpolated vertex + */ + static Vertex lerp(const Vertex& a, const Vertex& b, Real t); +}; + +/** + * @brief Get vertex attribute stride for OpenGL + * @return Size of Vertex structure in bytes + */ +constexpr size_t get_vertex_stride() { + return sizeof(Vertex); +} + +/** + * @brief Get offset of position attribute + * @return Offset in bytes + */ +constexpr size_t get_position_offset() { + return offsetof(Vertex, position_); +} + +/** + * @brief Get offset of normal attribute + * @return Offset in bytes + */ +constexpr size_t get_normal_offset() { + return offsetof(Vertex, normal_); +} + +/** + * @brief Get offset of texcoord attribute + * @return Offset in bytes + */ +constexpr size_t get_texcoord_offset() { + return offsetof(Vertex, texcoord_); +} + +/** + * @brief Get offset of tangent attribute + * @return Offset in bytes + */ +constexpr size_t get_tangent_offset() { + return offsetof(Vertex, tangent_); +} + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_VERTEX_H +``` + +### 文件:include/are/geometry/aabb.h + +```cpp +/** + * @file aabb.h + * @brief Axis-Aligned Bounding Box implementation + */ + +#ifndef ARE_INCLUDE_GEOMETRY_AABB_H +#define ARE_INCLUDE_GEOMETRY_AABB_H + +#include + +namespace are { + +// Forward declaration +struct Ray; + +/** + * @class AABB + * @brief Axis-Aligned Bounding Box for spatial queries + * + * Used for BVH construction and ray intersection acceleration. + */ +class AABB { +public: + Vec3 min_; ///< Minimum corner + Vec3 max_; ///< Maximum corner + + /** + * @brief Default constructor - creates invalid AABB + */ + AABB(); + + /** + * @brief Construct AABB from min and max corners + * @param min Minimum corner + * @param max Maximum corner + */ + AABB(const Vec3& min, const Vec3& max); + + /** + * @brief Construct AABB containing a single point + * @param point Point to contain + */ + explicit AABB(const Vec3& point); + + /** + * @brief Check if AABB is valid (min <= max) + * @return true if valid + */ + bool is_valid() const; + + /** + * @brief Get center of AABB + * @return Center point + */ + Vec3 center() const; + + /** + * @brief Get size (extent) of AABB + * @return Size vector + */ + Vec3 size() const; + + /** + * @brief Get surface area of AABB + * @return Surface area + */ + Real surface_area() const; + + /** + * @brief Get volume of AABB + * @return Volume + */ + Real volume() const; + + /** + * @brief Get longest axis index (0=x, 1=y, 2=z) + * @return Axis index + */ + int longest_axis() const; + + /** + * @brief Expand AABB to include a point + * @param point Point to include + */ + void expand(const Vec3& point); + + /** + * @brief Expand AABB to include another AABB + * @param other AABB to include + */ + void expand(const AABB& other); + + /** + * @brief Check if AABB contains a point + * @param point Point to check + * @return true if point is inside AABB + */ + bool contains(const Vec3& point) const; + + /** + * @brief Check if AABB intersects another AABB + * @param other AABB to check + * @return true if AABBs intersect + */ + bool intersects(const AABB& other) const; + + /** + * @brief Ray-AABB intersection test + * @param ray Ray to test + * @param t_min Minimum t value (output) + * @param t_max Maximum t value (output) + * @return true if ray intersects AABB + */ + bool intersect_ray(const Ray& ray, Real& t_min, Real& t_max) const; + + /** + * @brief Merge two AABBs + * @param a First AABB + * @param b Second AABB + * @return Merged AABB containing both + */ + static AABB merge(const AABB& a, const AABB& b); + + /** + * @brief Create invalid AABB (for initialization) + * @return Invalid AABB + */ + static AABB invalid(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_AABB_H +``` + +### 文件:include/are/geometry/triangle.h + +```cpp +/** + * @file triangle.h + * @brief Triangle primitive definition + */ + +#ifndef ARE_INCLUDE_GEOMETRY_TRIANGLE_H +#define ARE_INCLUDE_GEOMETRY_TRIANGLE_H + +#include +#include +#include + +namespace are { + +// Forward declaration +struct Ray; +struct HitRecord; + +/** + * @struct Triangle + * @brief Triangle primitive for ray tracing + * + * Stores three vertices and provides intersection testing. + */ +struct Triangle { + Vertex v0_; ///< First vertex + Vertex v1_; ///< Second vertex + Vertex v2_; ///< Third vertex + MaterialHandle material_; ///< Material handle + + /** + * @brief Default constructor + */ + Triangle(); + + /** + * @brief Construct triangle from three vertices + * @param v0 First vertex + * @param v1 Second vertex + * @param v2 Third vertex + * @param material Material handle + */ + Triangle(const Vertex& v0, const Vertex& v1, const Vertex& v2, + MaterialHandle material = are_invalid_handle); + + /** + * @brief Get triangle centroid + * @return Centroid position + */ + Vec3 centroid() const; + + /** + * @brief Get triangle normal (geometric normal) + * @return Normal vector (normalized) + */ + Vec3 normal() const; + + /** + * @brief Get triangle area + * @return Area + */ + Real area() const; + + /** + * @brief Compute axis-aligned bounding box + * @return AABB containing the triangle + */ + AABB compute_aabb() const; + + /** + * @brief Ray-triangle intersection test (Möller-Trumbore algorithm) + * @param ray Ray to test + * @param hit Output hit record + * @return true if intersection occurred + */ + bool intersect(const Ray& ray, HitRecord& hit) const; + + /** + * @brief Fast ray-triangle intersection test (no hit record) + * @param ray Ray to test + * @param t_max Maximum t value + * @return true if intersection occurred + */ + bool intersect_fast(const Ray& ray, Real t_max) const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_TRIANGLE_H +``` + +### 文件:include/are/geometry/transform.h + +```cpp +/** + * @file transform.h + * @brief Transformation matrix utilities + */ + +#ifndef ARE_INCLUDE_GEOMETRY_TRANSFORM_H +#define ARE_INCLUDE_GEOMETRY_TRANSFORM_H + +#include + +namespace are { + +/** + * @class Transform + * @brief 3D transformation (position, rotation, scale) + * + * Provides convenient interface for building transformation matrices. + */ +class Transform { +public: + /** + * @brief Default constructor (identity transform) + */ + Transform(); + + /** + * @brief Construct from position, rotation, and scale + * @param position Translation + * @param rotation Rotation (Euler angles in radians) + * @param scale Scale factors + */ + Transform(const Vec3& position, const Vec3& rotation, const Vec3& scale); + + // Setters + void set_position(const Vec3& position); + void set_rotation(const Vec3& rotation); + void set_scale(const Vec3& scale); + void set_scale(Real uniform_scale); + + // Getters + const Vec3& get_position() const { return position_; } + const Vec3& get_rotation() const { return rotation_; } + const Vec3& get_scale() const { return scale_; } + + // Matrix operations + Mat4 get_matrix() const; + Mat4 get_inverse_matrix() const; + Mat3 get_normal_matrix() const; + + /** + * @brief Transform a point + * @param point Point to transform + * @return Transformed point + */ + Vec3 transform_point(const Vec3& point) const; + + /** + * @brief Transform a direction (ignores translation) + * @param direction Direction to transform + * @return Transformed direction + */ + Vec3 transform_direction(const Vec3& direction) const; + + /** + * @brief Transform a normal (uses inverse transpose) + * @param normal Normal to transform + * @return Transformed normal + */ + Vec3 transform_normal(const Vec3& normal) const; + + /** + * @brief Combine two transforms + * @param other Other transform + * @return Combined transform + */ + Transform operator*(const Transform& other) const; + + /** + * @brief Create identity transform + * @return Identity transform + */ + static Transform identity(); + + /** + * @brief Create translation transform + * @param translation Translation vector + * @return Translation transform + */ + static Transform translate(const Vec3& translation); + + /** + * @brief Create rotation transform + * @param rotation Rotation (Euler angles in radians) + * @return Rotation transform + */ + static Transform rotate(const Vec3& rotation); + + /** + * @brief Create scale transform + * @param scale Scale factors + * @return Scale transform + */ + static Transform scale(const Vec3& scale); + +private: + void mark_dirty(); + void update_matrix() const; + + Vec3 position_; ///< Translation + Vec3 rotation_; ///< Rotation (Euler angles) + Vec3 scale_; ///< Scale factors + + mutable Mat4 matrix_; ///< Cached transformation matrix + mutable Mat4 inverse_matrix_; ///< Cached inverse matrix + mutable bool dirty_; ///< Matrix needs update +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_TRANSFORM_H +``` + +### 文件:include/are/scene/camera.h + +```cpp +/** + * @file camera.h + * @brief Camera class for view and projection management + */ + +#ifndef ARE_INCLUDE_SCENE_CAMERA_H +#define ARE_INCLUDE_SCENE_CAMERA_H + +#include + +namespace are { + +/** + * @class Camera + * @brief Perspective camera for rendering + * + * Manages view and projection matrices, and provides ray generation + * for ray tracing. + */ +class Camera { +public: + /** + * @brief Default constructor + */ + Camera(); + + /** + * @brief Construct camera with position and target + * @param position Camera position + * @param target Look-at target + * @param up Up vector + */ + Camera(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0)); + + // Position and orientation setters + void set_position(const Vec3& position); + void set_target(const Vec3& target); + void set_up(const Vec3& up); + void look_at(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0)); + + // Projection setters + void set_fov(Real fov_degrees); + void set_aspect_ratio(Real aspect); + void set_near_plane(Real near); + void set_far_plane(Real far); + void set_perspective(Real fov_degrees, Real aspect, Real near, Real far); + + // Getters + const Vec3& get_position() const { return position_; } + const Vec3& get_target() const { return target_; } + const Vec3& get_up() const { return up_; } + Real get_fov() const { return fov_; } + Real get_aspect_ratio() const { return aspect_ratio_; } + Real get_near_plane() const { return near_plane_; } + Real get_far_plane() const { return far_plane_; } + + // Direction vectors + Vec3 get_forward() const; + Vec3 get_right() const; + + // Matrix getters + const Mat4& get_view_matrix() const; + const Mat4& get_projection_matrix() const; + Mat4 get_view_projection_matrix() const; + + /** + * @brief Generate ray for given pixel coordinates + * @param u Normalized x coordinate [0, 1] + * @param v Normalized y coordinate [0, 1] + * @param origin Output ray origin + * @param direction Output ray direction (normalized) + */ + void generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const; + + /** + * @brief Check if camera parameters have changed + * @return true if matrices need recalculation + */ + bool is_dirty() const { return dirty_; } + + /** + * @brief Mark camera as clean after matrix update + */ + void clear_dirty() { dirty_ = false; } + +private: + void update_view_matrix() const; + void update_projection_matrix() const; + + Vec3 position_; ///< Camera position + Vec3 target_; ///< Look-at target + Vec3 up_; ///< Up vector + + Real fov_; ///< Field of view in degrees + Real aspect_ratio_; ///< Aspect ratio (width/height) + Real near_plane_; ///< Near clipping plane + Real far_plane_; ///< Far clipping plane + + mutable Mat4 view_matrix_; ///< Cached view matrix + mutable Mat4 projection_matrix_; ///< Cached projection matrix + mutable bool view_dirty_; ///< View matrix needs update + mutable bool projection_dirty_; ///< Projection matrix needs update + bool dirty_; ///< Any parameter changed +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_CAMERA_H +``` + +### 文件:include/are/scene/material.h + +```cpp +/** + * @file material.h + * @brief PBR material definition + */ + +#ifndef ARE_INCLUDE_SCENE_MATERIAL_H +#define ARE_INCLUDE_SCENE_MATERIAL_H + +#include +#include + +namespace are { + +/** + * @class Material + * @brief Physically-based rendering material + * + * Supports standard PBR workflow with metallic-roughness model. + */ +class Material { +public: + /** + * @brief Default constructor - creates default white material + */ + Material(); + + // Albedo (base color) + void set_albedo(const Vec3& albedo); + void set_albedo_map(const std::string& path); + const Vec3& get_albedo() const { return albedo_; } + const std::string& get_albedo_map() const { return albedo_map_; } + bool has_albedo_map() const { return !albedo_map_.empty(); } + + // Metallic + void set_metallic(Real metallic); + void set_metallic_map(const std::string& path); + Real get_metallic() const { return metallic_; } + const std::string& get_metallic_map() const { return metallic_map_; } + bool has_metallic_map() const { return !metallic_map_.empty(); } + + // Roughness + void set_roughness(Real roughness); + void set_roughness_map(const std::string& path); + Real get_roughness() const { return roughness_; } + const std::string& get_roughness_map() const { return roughness_map_; } + bool has_roughness_map() const { return !roughness_map_.empty(); } + + // Normal map + void set_normal_map(const std::string& path); + const std::string& get_normal_map() const { return normal_map_; } + bool has_normal_map() const { return !normal_map_.empty(); } + + // Ambient occlusion + void set_ao_map(const std::string& path); + const std::string& get_ao_map() const { return ao_map_; } + bool has_ao_map() const { return !ao_map_.empty(); } + + // Emissive + void set_emissive(const Vec3& emissive); + void set_emissive_map(const std::string& path); + const Vec3& get_emissive() const { return emissive_; } + const std::string& get_emissive_map() const { return emissive_map_; } + bool has_emissive_map() const { return !emissive_map_.empty(); } + bool is_emissive() const; + + // Texture handles (set by TextureManager) + void set_albedo_texture_handle(TextureHandle handle) { albedo_tex_handle_ = handle; } + void set_metallic_texture_handle(TextureHandle handle) { metallic_tex_handle_ = handle; } + void set_roughness_texture_handle(TextureHandle handle) { roughness_tex_handle_ = handle; } + void set_normal_texture_handle(TextureHandle handle) { normal_tex_handle_ = handle; } + void set_ao_texture_handle(TextureHandle handle) { ao_tex_handle_ = handle; } + void set_emissive_texture_handle(TextureHandle handle) { emissive_tex_handle_ = handle; } + + TextureHandle get_albedo_texture_handle() const { return albedo_tex_handle_; } + TextureHandle get_metallic_texture_handle() const { return metallic_tex_handle_; } + TextureHandle get_roughness_texture_handle() const { return roughness_tex_handle_; } + TextureHandle get_normal_texture_handle() const { return normal_tex_handle_; } + TextureHandle get_ao_texture_handle() const { return ao_tex_handle_; } + TextureHandle get_emissive_texture_handle() const { return emissive_tex_handle_; } + +private: + // Base values + Vec3 albedo_;///< Base color (RGB) + Real metallic_; ///< Metallic factor [0, 1] + Real roughness_; ///< Roughness factor [0, 1] + Vec3 emissive_; ///< Emissive color (RGB) + + // Texture paths + std::string albedo_map_; + std::string metallic_map_; + std::string roughness_map_; + std::string normal_map_; + std::string ao_map_; + std::string emissive_map_; + + // Texture handles (GPU resources) + TextureHandle albedo_tex_handle_; + TextureHandle metallic_tex_handle_; + TextureHandle roughness_tex_handle_; + TextureHandle normal_tex_handle_; + TextureHandle ao_tex_handle_; + TextureHandle emissive_tex_handle_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MATERIAL_H +``` + +### 文件:include/are/scene/mesh.h + +```cpp +/** + * @file mesh.h + * @brief Mesh class for geometry storage + */ + +#ifndef ARE_INCLUDE_SCENE_MESH_H +#define ARE_INCLUDE_SCENE_MESH_H + +#include +#include +#include +#include + +namespace are { + +/** + * @class Mesh + * @brief Triangle mesh container + * + * Stores vertex and index data for a triangle mesh. + * Supports automatic AABB computation. + */ +class Mesh { +public: + /** + * @brief Default constructor - creates empty mesh + */ + Mesh(); + + /** + * @brief Construct mesh from vertex and index data + * @param vertices Vertex array + * @param indices Index array (triangles) + * @param material_id Material handle + */ + Mesh(const std::vector& vertices, + const std::vector& indices, + MaterialHandle material_id = are_invalid_handle); + + /** + * @brief Construct mesh from raw arrays + * @param vertices Vertex array pointer + * @param vertex_count Number of vertices + * @param indices Index array pointer + * @param index_count Number of indices + * @param material_id Material handle + */ + Mesh(const Vertex* vertices, size_t vertex_count, + const uint32_t* indices, size_t index_count, + MaterialHandle material_id = are_invalid_handle); + + // Data setters + void set_vertices(const std::vector& vertices); + void set_indices(const std::vector& indices); + void set_material(MaterialHandle material_id); + + // Data getters + const std::vector& get_vertices() const { return vertices_; } + const std::vector& get_indices() const { return indices_; } + MaterialHandle get_material() const { return material_id_; } + + // Geometry queries + size_t get_vertex_count() const { return vertices_.size(); } + size_t get_index_count() const { return indices_.size(); } + size_t get_triangle_count() const { return indices_.size() / 3; } + bool is_empty() const { return vertices_.empty() || indices_.empty(); } + + // AABB + const AABB& get_aabb() const { return aabb_; } + void compute_aabb(); + + /** + * @brief Compute tangent vectors for normal mapping + */ + void compute_tangents();/** + * @brief Get triangle vertices by index + * @param triangle_index Triangle index + * @param v0 Output first vertex + * @param v1 Output second vertex + * @param v2 Output third vertex + * @return true if triangle exists + */ + bool get_triangle(size_t triangle_index, Vertex& v0, Vertex& v1, Vertex& v2) const; + + // GPU resource handles (set by Renderer) + void set_vao(uint32_t vao) { vao_ = vao; } + void set_vbo(uint32_t vbo) { vbo_ = vbo; } + void set_ebo(uint32_t ebo) { ebo_ = ebo; } + uint32_t get_vao() const { return vao_; } + uint32_t get_vbo() const { return vbo_; } + uint32_t get_ebo() const { return ebo_; } + bool has_gpu_resources() const { return vao_ != 0; } + +private: + std::vector vertices_; ///< Vertex data + std::vector indices_; ///< Index data (triangles) + MaterialHandle material_id_; ///< Associated material + AABB aabb_; ///< Bounding box + + // GPU resources + uint32_t vao_; ///< Vertex Array Object + uint32_t vbo_; ///< Vertex Buffer Object + uint32_t ebo_; ///< Element Buffer Object +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MESH_H +``` + +### 文件:include/are/scene/light.h + +```cpp +/** + * @file light.h + * @brief Base light class and common light utilities + */ + +#ifndef ARE_INCLUDE_SCENE_LIGHT_H +#define ARE_INCLUDE_SCENE_LIGHT_H + +#include + +namespace are { + +/** + * @enum LightType + * @brief Types of light sources + */ +enum class LightType { + ARE_LIGHT_DIRECTIONAL, + ARE_LIGHT_POINT, + ARE_LIGHT_SPOT +}; + +/** + * @struct LightData + * @brief Packed light data for GPU transfer + * + * This structure is designed to be efficiently transferred to GPU + * via SSBO or UBO. + */ +struct LightData { + Vec4 position_type_; ///< xyz: position, w: light type + Vec4 direction_range_; ///< xyz: direction, w: range + Vec4 color_intensity_; ///< xyz: color, w: intensity + Vec4 params_; ///< Light-specific parameters +}; + +/** + * @class Light + * @brief Base class for all light types + */ +class Light { +public: + /** + * @brief Constructor + * @param type Light type + */ + explicit Light(LightType type); + + virtual ~Light() = default; + + // Common properties + void set_color(const Vec3& color); + void set_intensity(Real intensity); + void set_cast_shadows(bool cast); + + const Vec3& get_color() const { return color_; } + Real get_intensity() const { return intensity_; } + bool get_cast_shadows() const { return cast_shadows_; } + LightType get_type() const { return type_; } + + /** + * @brief Pack light data for GPU transfer + * @return Packed light data + */ + virtual LightData pack() const = 0; + + /** + * @brief Check if light affects a point + * @param point World position + * @return true if light can affect the point + */ + virtual bool affects_point(const Vec3& point) const = 0; + +protected: + LightType type_; ///< Light type + Vec3 color_; ///< Light color (RGB) + Real intensity_; ///< Light intensity + bool cast_shadows_; ///< Whether light casts shadows +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_LIGHT_H +``` + +### 文件:include/are/scene/directional_light.h + +```cpp +/** + * @file directional_light.h + * @brief Directional light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H +#define ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H + +#include + +namespace are { + +/** + * @class DirectionalLight + * @brief Directional light source (sun-like) + * + * Represents an infinitely distant light source with parallel rays. + */ +class DirectionalLight : public Light { +public: + /** + * @brief Default constructor + */ + DirectionalLight(); + + /** + * @brief Construct with direction and color + * @param direction Light direction (will be normalized) + * @param color Light color + * @param intensity Light intensity + */ + DirectionalLight(const Vec3& direction, const Vec3& color = Vec3(1.0f), + Real intensity = 1.0f); + + // Direction + void set_direction(const Vec3& direction); + const Vec3& get_direction() const { return direction_; } + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 direction_; ///< Light direction (normalized) +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H +``` + +### 文件:include/are/scene/point_light.h + +```cpp +/** + * @file point_light.h + * @brief Point light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_POINT_LIGHT_H +#define ARE_INCLUDE_SCENE_POINT_LIGHT_H + +#include + +namespace are { + +/** + * @class PointLight + * @brief Point light source + * + * Emits light equally in all directions from a single point. + */ +class PointLight : public Light { +public: + /** + * @brief Default constructor + */ + PointLight(); + + /** + * @brief Construct with position and color + * @param position Light position + * @param color Light color + * @param intensity Light intensity + * @param range Light range (attenuation distance) + */ + PointLight(const Vec3& position, const Vec3& color = Vec3(1.0f), + Real intensity = 1.0f, Real range = 10.0f); + + // Position + void set_position(const Vec3& position); + const Vec3& get_position() const { return position_; } + + // Range (attenuation) + void set_range(Real range); + Real get_range() const { return range_; } + + // Attenuation parameters + void set_attenuation(Real constant, Real linear, Real quadratic); + Real get_constant_attenuation() const { return attenuation_constant_; } + Real get_linear_attenuation() const { return attenuation_linear_; } + Real get_quadratic_attenuation() const { return attenuation_quadratic_; } + + /** + * @brief Calculate attenuation at given distance + * @param distance Distance from light + * @return Attenuation factor [0, 1] + */ + Real calculate_attenuation(Real distance) const; + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 position_; ///< Light position + Real range_; ///< Light range + Real attenuation_constant_; ///< Constant attenuation factor + Real attenuation_linear_; ///< Linear attenuation factor + Real attenuation_quadratic_; ///< Quadratic attenuation factor +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_POINT_LIGHT_H +``` + +### 文件:include/are/scene/spot_light.h + +```cpp +/** + * @file spot_light.h + * @brief Spot light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_SPOT_LIGHT_H +#define ARE_INCLUDE_SCENE_SPOT_LIGHT_H + +#include + +namespace are { + +/** + * @class SpotLight + * @brief Spot light source + * + * Emits light in a cone from a single point. + */ +class SpotLight : public Light { +public: + /** + * @brief Default constructor + */ + SpotLight(); + + /** + * @brief Construct with position, direction, and angles + * @param position Light position + * @param direction Light direction + * @param inner_angle Inner cone angle in degrees + * @param outer_angle Outer cone angle in degrees + * @param color Light color + * @param intensity Light intensity + */ + SpotLight(const Vec3& position, const Vec3& direction,Real inner_angle, Real outer_angle, + const Vec3& color = Vec3(1.0f), Real intensity = 1.0f); + + // Position and direction + void set_position(const Vec3& position); + void set_direction(const Vec3& direction); + const Vec3& get_position() const { return position_; } + const Vec3& get_direction() const { return direction_; } + + // Cone angles (in degrees) + void set_inner_angle(Real angle); + void set_outer_angle(Real angle); + Real get_inner_angle() const { return inner_angle_; } + Real get_outer_angle() const { return outer_angle_; } + + // Range + void set_range(Real range); + Real get_range() const { return range_; } + + /** + * @brief Calculate spotlight intensity at given direction + * @param to_point Direction from light to point (normalized) + * @return Spotlight factor [0, 1] + */ + Real calculate_spot_factor(const Vec3& to_point) const; + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 position_; ///< Light position + Vec3 direction_; ///< Light direction (normalized) + Real inner_angle_; ///< Inner cone angle (degrees) + Real outer_angle_; ///< Outer cone angle (degrees) + Real range_; ///< Light range + Real cos_inner_; ///< Cosine of inner angle (cache + Real cos_outer_; ///< Cosine of outer angle (cached) +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_SPOT_LIGHT_H +``` + +### 文件:include/are/scene/scene_manager.h + +```cpp +/** + * @file scene_manager.h + * @brief Scene data management + */ + +#ifndef ARE_INCLUDE_SCENE_SCENE_MANAGER_H +#define ARE_INCLUDE_SCENE_SCENE_MANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +/** + * @class SceneManager + * @brief Manages all scene objects (meshes, materials, lights) + * + * Provides handle-based access to scene resources and tracks + * scene modifications for BVH rebuilding. + */ +class SceneManager { +public: + /** + * @brief Constructor + */ + SceneManager(); + + /** + * @brief Destructor + */ + ~SceneManager(); + + // Mesh management + MeshHandle add_mesh(const Mesh& mesh); + void remove_mesh(MeshHandle handle); + void update_mesh(MeshHandle handle, const Mesh& mesh); + Mesh* get_mesh(MeshHandle handle); + const Mesh* get_mesh(MeshHandle handle) const; + const std::vector& get_all_meshes() const { return meshes_; } + + // Material management + MaterialHandle add_material(const Material& material); + void remove_material(MaterialHandle handle); + void update_material(MaterialHandle handle, const Material& material); + Material* get_material(MaterialHandle handle); + const Material* get_material(MaterialHandle handle) const; + const std::vector& get_all_materials() const { return materials_; } + + // Light management + LightHandle add_light(const std::shared_ptr& light); + void remove_light(LightHandle handle); + std::shared_ptr get_light(LightHandle handle); + const std::vector>& get_all_lights() const { return lights_; } + + // Scene queries + size_t get_mesh_count() const { return meshes_.size(); } + size_t get_material_count() const { return materials_.size(); } + size_t get_light_count() const { return lights_.size(); } + size_t get_total_triangle_count() const; + + // Scene state + bool is_dirty() const { return dirty_; } + void mark_dirty() { dirty_ = true; } + void clear_dirty() { dirty_ = false; } + + /** + * @brief Clear all scene data + */ + void clear(); + + /** + * @brief Validate all handles and remove invalid entries + */ + void compact(); + +private: + std::vector meshes_; ///< Mesh storage + std::vector materials_; ///< Material storage + std::vector> lights_; ///< Light storage + + std::unordered_map mesh_handle_map_; + std::unordered_map material_handle_map_; + std::unordered_map light_handle_map_; + + MeshHandle next_mesh_handle_; + MaterialHandle next_material_handle_; + LightHandle next_light_handle_; + + bool dirty_; ///< Scene modified flag +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_SCENE_MANAGER_H +``` + +### 文件:include/are/renderer/renderer.h + +```cpp +/** + * @file renderer.h + * @brief Main renderer interface + */ + +#ifndef ARE_INCLUDE_RENDERER_RENDERER_H +#define ARE_INCLUDE_RENDERER_RENDERER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +// Forward declarations +class Window; +class SceneManager; +class Rasterizer; +class RayTracer; +class TextureManager; + +/** + * @class Renderer + * @brief Main rendering interface for Aurora Rendering Engine + * + * This class provides the primary API for rendering scenes using + * hybrid rasterization and ray tracing techniques. + */ +class Renderer { +public: + /** + * @brief Constructor + * @param config Rendering configuration + */ + explicit Renderer(const AreConfig& config); + + /** + * @brief Destructor + */ + ~Renderer(); + + // Configuration + void set_config(const AreConfig& config); + const AreConfig& get_config() const; + + // Camera management + void set_camera(const Camera& camera); + Camera& get_camera(); + const Camera& get_camera() const; + + // Scene management + MeshHandle add_mesh(const Mesh& mesh); + MaterialHandle add_material(const Material& material); + LightHandle add_light(const std::shared_ptr& light); + + void remove_mesh(MeshHandle handle); + void remove_material(MaterialHandle handle); + void remove_light(LightHandle handle); + + void update_mesh(MeshHandle handle, const Mesh& mesh); + void update_material(MaterialHandle handle, const Material& material); + + void clear_scene(); + + // Ray tracing backend control + void set_ray_tracing_backend(RayTracingBackend backend); + RayTracingBackend get_ray_tracing_backend() const; + + // Rendering + void begin_frame(); + void render(); + void end_frame(); + void present(); + + // Frame capture + void capture_frame_ldr(uint8_t** pixels, int* width, int* height); + void capture_frame_hdr(float** pixels, int* width, int* height); + void save_frame(const std::string& filename, const uint8_t* pixels, + int width, int height); + + // Window control + bool should_close() const; + void set_should_close(bool should_close); + + // Statistics + const RenderStats& get_stats() const; + void reset_stats(); + + // Debug visualization + void set_gbuffer_visualization_mode(GBufferVisualizationMode mode); + GBufferVisualizationMode get_gbuffer_visualization_mode() const; + +private: + void initialize_subsystems(); + void shutdown_subsystems(); + void rebuild_bvh_if_needed(); + void check_scene_dirty(); + + AreConfig config_; ///< Current configuration + + std::unique_ptr window_; ///< Window management + std::unique_ptr scene_manager_; ///< Scene data management + std::unique_ptr rasterizer_; ///< Rasterization pipeline + std::unique_ptr raytracer_; ///< Ray tracing pipeline + std::unique_ptr texture_manager_; ///< Texture management + + Camera camera_; ///< Active camera + RenderStats stats_; ///< Rendering statistics + + bool scene_dirty_; ///< Scene needs BVH rebuild + bool initialized_; ///< Initialization flag +}; + +} // namespace are + +#endif // ARE_INCLUDE_RENDERER_RENDERER_H +``` + +### 文件:include/are/renderer/render_stats.h + +```cpp +/** + * @file render_stats.h + * @brief Rendering statistics tracking + */ + +#ifndef ARE_INCLUDE_RENDERER_RENDER_STATS_H +#define ARE_INCLUDE_RENDERER_RENDER_STATS_H + +#include +#include + +namespace are { + +/** + * @struct RenderStats + * @brief Statistics for rendering performance analysis + */ +struct RenderStats { + // Frame timing + double frame_time_ms_; ///< Total frame time in milliseconds + double rasterization_time_ms_; ///< Rasterization time + double ray_tracing_time_ms_; ///< Ray tracing time + double bvh_build_time_ms_; ///< BVH construction time + double present_time_ms_; ///< Present/swap time + + // Scene statistics + uint32_t mesh_count_; ///< Number of meshes + uint32_t triangle_count_; ///< Total triangle count + uint32_t light_count_; ///< Number of lights + uint32_t material_count_; ///< Number of materials + + // Ray tracing statistics + uint64_t primary_rays_; ///< Number of primary rays + uint64_t secondary_rays_; ///< Number of secondary rays + uint64_t shadow_rays_; ///< Number of shadow rays + uint64_t bvh_traversals_; ///< BVH traversal count + uint64_t triangle_tests_; ///< Triangle intersection tests + + // Memory statistics + size_t vertex_memory_bytes_; ///< Vertex buffer memory + size_t index_memory_bytes_; ///< Index buffer memory + size_t texture_memory_bytes_; ///< Texture memory + size_t bvh_memory_bytes_; ///< BVH memory + + // FPS + double fps_; ///< Frames per second + + /** + * @brief Reset all statistics to zero + */ + void reset(); + + /** + * @brief Print statistics to console + */ + void print() const; + + /** + * @brief Update FPS based on frame time + */ + void update_fps(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_RENDERER_RENDER_STATS_H +``` + +### 文件:include/are/renderer/render_context.h + +```cpp +/** + * @file render_context.h + * @brief Rendering context and state management + */ + +#ifndef ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H +#define ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H + +#include +#include + +namespace are { + +/** + * @struct RenderContext + * @brief Rendering context information + * + * Contains current rendering state and frame information. + */ +struct RenderContext { + int frame_number_; ///< Current frame number + double time_; ///< Total elapsed time in seconds + double delta_time_; ///< Time since last frame + + int viewport_width_; ///< Viewport width + int viewport_height_; ///< Viewport height + + RayTracingBackend current_backend_; ///< Current ray tracing backend + bool scene_dirty_; ///< Scene needs BVH rebuild + bool camera_moved_; ///< Camera moved this frame + + /** + * @brief Constructor + */ + RenderContext(); + + /** + * @brief Reset context + */ + void reset(); + + /** + * @brief Update frame timing + * @param current_time Current time in seconds + */ + void update_timing(double current_time); +}; + +} // namespace are + +#endif // ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H +``` + +### 文件:include/are/raytracer/ray.h + +```cpp +/** + * @file ray.h + * @brief Ray structure for ray tracing + */ + +#ifndef ARE_INCLUDE_RAYTRACER_RAY_H +#define ARE_INCLUDE_RAYTRACER_RAY_H + +#include + +namespace are { + +/** + * @struct Ray + * @brief Ray representation for ray tracing + */ +struct Ray { + Vec3 origin_; ///< Ray origin + Vec3 direction_; ///< Ray direction (normalized) + Real t_min_; ///< Minimum t value + Real t_max_; ///< Maximum t value + + /** + * @brief Default constructor + */ + Ray(); + + /** + * @brief Construct ray with origin and direction + * @param origin Ray origin + * @param direction Ray direction (will be normalized) + * @param t_min Minimum t value + * @param t_max Maximum t value + */ + Ray(const Vec3& origin, const Vec3& direction, + Real t_min = are_epsilon, Real t_max = 1e30f); + + /** + * @brief Evaluate ray at parameter t + * @param t Parameter value + * @return Point on ray + */ + Vec3 at(Real t) const; + + /** + * @brief Check if t is within valid range + * @param t Parameter value + * @return true if t is valid + */ + bool is_valid_t(Real t) const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_RAY_H +``` + +### 文件:include/are/raytracer/hit_record.h + +```cpp +/** + * @file hit_record.h + * @brief Ray-surface intersection record + */ + +#ifndef ARE_INCLUDE_RAYTRACER_HIT_RECORD_H +#define ARE_INCLUDE_RAYTRACER_HIT_RECORD_H + +#include + +namespace are { + +/** + * @struct HitRecord + * @brief Information about ray-surface intersection + */ +struct HitRecord { + Vec3 position_; ///< Hit position in world space + Vec3 normal_; ///< Surface normal at hit point + Vec2 texcoord_; ///< Texture coordinates at hit point + Vec3 tangent_; ///< Tangent vector at hit point + Real t_; ///< Ray parameter at hit point + MaterialHandle material_; ///< Material at hit point + uint32_t triangle_index_; ///< Triangle index that was hit + bool front_face_; ///< Whether ray hit front face + + /** + * @brief Default constructor + */ + HitRecord(); + + /** + * @brief Set face normal based on ray direction + * @param ray_direction Ray direction + * @param outward_normal Outward-facing normal + */ + void set_face_normal(const Vec3& ray_direction, const Vec3& outward_normal); + + /** + * @brief Check if hit record is valid + * @return true if hit occurred + */ + bool is_valid() const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_HIT_RECORD_H +``` + +### 文件:include/are/raytracer/raytracer.h + +```cpp +/** + * @file raytracer.h + * @brief Ray tracing interface + */ + +#ifndef ARE_INCLUDE_RAYTRACER_RAYTRACER_H +#define ARE_INCLUDE_RAYTRACER_RAYTRACER_H + +#include +#include + +namespace are { + +// Forward declarations +class SceneManager; +class Camera; +class GBuffer; +class BVH; + +/** + * @class RayTracer + * @brief Abstract ray tracing interface + * + * Base class for CPU and GPU ray tracing implementations. + */ +class RayTracer { +public: + /** + * @brief Constructor + * @param config Ray tracing configuration + */ + explicit RayTracer(const RayTracingConfig& config); + + /** + * @brief Virtual destructor + */ + virtual ~RayTracer() = default; + + /** + * @brief Render scene using ray tracing + * @param scene Scene manager + * @param camera Camera + * @param gbuffer G-Buffer (optional, for hybrid rendering) + * @param output Output texture ID + */ + virtual void render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) = 0; + + /** + * @brief Update BVH + * @param bvh BVH reference + */ + virtual void update_bvh(const BVH& bvh) = 0; + + /** + * @brief Set configuration + * @param config New configuration + */ + virtual void set_config(const RayTracingConfig& config); + + /** + * @brief Get configuration + * @return Current configuration + */ + const RayTracingConfig& get_config() const { return config_; } + +protected: + RayTracingConfig config_; ///< Ray tracing configuration +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_RAYTRACER_H +``` + +### 文件:include/are/raytracer/cpu_raytracer.h + +```cpp +/** + * @file cpu_raytracer.h + * @brief CPU-based ray tracing implementation + */ + +#ifndef ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H +#define ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @class CPURayTracer + * @brief CPU-based ray tracing implementation + * + * Uses multithreading for parallel ray tracing on CPU. + */ +class CPURayTracer : public RayTracer { +public: + /** + * @brief Constructor + * @param config Ray tracing configuration + */ + explicit CPURayTracer(const RayTracingConfig& config); + + /** + * @brief Destructor + */ + ~CPURayTracer() override; + + /** + * @brief Render scene using CPU ray tracing + * @param scene Scene manager + * @param camera Camera + * @param gbuffer G-Buffer (optional) + * @param output Output texture ID + */ + void render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) override; + + /** + * @brief Update BVH + * @param bvh BVH reference + */ + void update_bvh(const BVH& bvh) override; + +private: + /** + * @brief Trace a single ray + * @param ray Ray to trace + * @param depth Current recursion depth + * @return Ray color + */ + Vec3 trace_ray(const Ray& ray, int depth); + + /** + * @brief Shade hit point + * @param hit Hit record + * @param ray Incident ray + * @param depth Current recursion depth + * @return Shaded color + */ + Vec3 shade(const HitRecord& hit, const Ray& ray, int depth); + + /** + * @brief Compute direct lighting + * @param hit Hit record + * @return Direct lighting contribution + */ + Vec3 compute_direct_lighting(const HitRecord& hit); + + /** + * @brief Compute ambient occlusion + * @param hit Hit record + * @return AO factor [0, 1] + */ + Real compute_ambient_occlusion(const HitRecord& hit); + + /** + * @brief Check shadow ray + * @param origin Shadow ray origin + * @param direction Shadow ray direction + * @param max_distance Maximum distance + * @return true if in shadow + */ + bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance); + + const BVH* bvh_; ///< BVH reference + const SceneManager* scene_; ///< Scene reference + std::vector framebuffer_; ///< CPU framebuffer (HDR) + int width_; ///< Framebuffer width + int height_; ///< Framebuffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H +``` + +### 文件:include/are/raytracer/compute_raytracer.h + +```cpp +/** + * @file compute_raytracer.h + * @brief GPU compute shader ray tracing implementation + */ + +#ifndef ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H +#define ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H + +#include +#include + +namespace are { + +// Forward declarations +class ShaderProgram; + +/** + * @class ComputeRayTracer + * @brief GPU-based ray tracing using compute shaders + */ +class ComputeRayTracer : public RayTracer { +public: + /** + * @brief Constructor + * @param config Ray tracing configuration + */ + explicit ComputeRayTracer(const RayTracingConfig& config); + + /** + * @brief Destructor + */ + ~ComputeRayTracer() override; + + /** + * @brief Render scene using compute shader ray tracing + * @param scene Scene manager + * @param camera Camera + * @param gbuffer G-Buffer (optional) + * @param output Output texture ID + */ + void render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) override; + + /** + * @brief Update BVH + * @param bvh BVH reference + */ + void update_bvh(const BVH& bvh) override; + +private: + void initialize_compute_shader(const std::string& shader_dir); + void upload_scene_data(const SceneManager& scene); + void upload_bvh_data(const BVH& bvh); + void upload_camera_data(const Camera& camera); + + std::unique_ptr compute_shader_; ///< Ray tracing compute shader + + // GPU buffers (SSBOs) + uint32_t bvh_buffer_; ///< BVH nodes buffer + uint32_t triangle_buffer_; ///< Triangle data buffer + uint32_t material_buffer_; ///< Material data buffer + uint32_t light_buffer_; ///< Light data buffer + + bool buffers_initialized_; ///< Buffer initialization flag +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H +``` + +### 文件:include/are/utils/image_io.h + +```cpp +/** + * @file image_io.h + * @brief Image loading and saving utilities + */ + +#ifndef ARE_INCLUDE_UTILS_IMAGE_IO_H +#define ARE_INCLUDE_UTILS_IMAGE_IO_H + +#include +#include +#include + +namespace are { + +/** + * @enum ImageFormat + * @brief Supported image formats + */ +enum class ImageFormat { + ARE_IMAGE_FORMAT_PPM, + ARE_IMAGE_FORMAT_BMP, + ARE_IMAGE_FORMAT_PNG, + ARE_IMAGE_FORMAT_JPG +}; + +/** + * @struct ImageData + * @brief Container for image data + */ +struct ImageData { + int width_; ///< Image width + int height_; ///< Image height + int channels_; ///< Number of channels (3=RGB, 4=RGBA) + std::vector data_; ///< Pixel data (row-major) + + /** + * @brief Check if image data is valid + * @return true if valid + */ + bool is_valid() const; + + /** + * @brief Get pixel at (x, y) + * @param x X coordinate + * @param y Y coordinate + * @return Pointer to pixel data (RGB or RGBA) + */ + const uint8_t* get_pixel(int x, int y) const; + + /** + * @brief Set pixel at (x, y) + * @param x X coordinate + * @param y Y coordinate + * @param r Red channel + * @param g Green channel + * @param b Blue channel + * @param a Alpha channel (optional) + */ + void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255); +}; + +/** + * @brief Load image from file + * @param filename Image file path + * @param flip_vertically Whether to flip image vertically + * @return Image data (empty if failed) + */ +ImageData load_image(const std::string& filename, bool flip_vertically = false); + +/** + * @brief Save image to file + * @param filename Output file path + * @param data Image data + * @param format Output format (auto-detected from extension if not specified) + * @return true if save succeeded + */ +bool save_image(const std::string& filename, const ImageData& data, + ImageFormat format = ImageFormat::ARE_IMAGE_FORMAT_PNG); + +/** + * @brief Save raw pixel data to file + * @param filename Output file path + * @param pixels Pixel data pointer + * @param width Image width + * @param height Image height + * @param channels Number of channels + * @param format Output format + * @return true if save succeeded + */ +bool save_image(const std::string& filename, const uint8_t* pixels, + int width, int height, int channels, + ImageFormat format = ImageFormat::ARE_IMAGE_FORMAT_PNG); + +/** + * @brief Detect image format from file extension + * @param filename File path + * @return Detected format + */ +ImageFormat detect_format(const std::string& filename); + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_IMAGE_IO_H +``` + +### 文件:include/are/utils/math_utils.h + +```cpp +/** + * @file math_utils.h + * @brief Mathematical utility functions + */ + +#ifndef ARE_INCLUDE_UTILS_MATH_UTILS_H +#define ARE_INCLUDE_UTILS_MATH_UTILS_H + +#include +#include + +namespace are { + +/** + * @brief Clamp value to range [min, max] + * @param value Value to clamp + * @param min Minimum value + * @param max Maximum value + * @return Clamped value + */ +template +inline T clamp(T value, T min, T max) { + return std::max(min, std::min(value, max)); +} + +/** + * @brief Linear interpolation + * @param a Start value + * @param b End value + * @param t Interpolation factor [0, 1] + * @return Interpolated value + */ +template +inline T lerp(T a, T b, Real t) { + return a + (b - a) * t; +} + +/** + * @brief Convert degrees to radians + * @param degrees Angle in degrees + * @return Angle in radians + */ +inline Real degrees_to_radians(Real degrees) { + return degrees * are_pi / 180.0f; +} + +/** + * @brief Convert radians to degrees + * @param radians Angle in radians + * @return Angle in degrees + */ +inline Real radians_to_degrees(Real radians) { + return radians * 180.0f / are_pi; +} + +/** + * @brief Check if two floating point values are approximately equal + * @param a First value + * @param b Second value + * @param epsilon Tolerance + * @return true if approximately equal + */ +inline bool approx_equal(Real a, Real b, Real epsilon = are_epsilon) { + return std::abs(a - b) < epsilon; +} + +/** + * @brief Compute barycentric coordinates + * @param p Point + * @param a Triangle vertex A + * @param b Triangle vertex B + * @param c Triangle vertex C + * @param u Output barycentric coordinate u + * @param v Output barycentric coordinate v + * @param w Output barycentric coordinate w + */ +void compute_barycentric(const Vec3& p, const Vec3& a, const Vec3& b, const Vec3& c, + Real& u, Real& v, Real& w); + +/** + * @brief Reflect vector around normal + * @param incident Incident vector + * @param normal Surface normal (normalized) + * @return Reflected vector + */ +Vec3 reflect(const Vec3& incident, const Vec3& normal); + +/** + * @brief Refract vector through surface + * @param incident Incident vector (normalized) + * @param normal Surface normal (normalized) + * @param eta Ratio of refractive indices + * @param refracted Output refracted vector + * @return true if refraction occurred (false for total internal reflection) + */ +bool refract(const Vec3& incident, const Vec3& normal, Real eta, Vec3& refracted); + +/** + * @brief Compute Fresnel reflectance (Schlick approximation) + * @param cos_theta Cosine of angle between view and normal + * @param f0 Reflectance at normal incidence + * @return Fresnel reflectance + */ +Real fresnel_schlick(Real cos_theta, Real f0); + +/** + * @brief Create orthonormal basis from normal + * @param normal Normal vector (normalized) + * @param tangent Output tangent vector + * @param bitangent Output bitangent vector + */ +void create_orthonormal_basis(const Vec3& normal, Vec3& tangent, Vec3& bitangent); + +/** + * @brief Transform direction from tangent space to world space + * @param tangent_dir Direction in tangent space + * @param normal Surface normal + * @param tangent Surface tangent + * @param bitangent Surface bitangent + * @return Direction in world space + */ +Vec3 tangent_to_world(const Vec3& tangent_dir, const Vec3& normal, + const Vec3& tangent, const Vec3& bitangent); + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_MATH_UTILS_H +``` + +### 文件:include/are/utils/random.h + +```cpp +/** + * @file random.h + * @brief Random number generation utilities + */ + +#ifndef ARE_INCLUDE_UTILS_RANDOM_H +#define ARE_INCLUDE_UTILS_RANDOM_H + +#include +#include + +namespace are { + +/** + * @class RandomGenerator + * @brief Thread-safe random number generator + * + * Uses PCG (Permuted Congruential Generator) for high-quality random numbers. + */ +class RandomGenerator { +public: + /** + * @brief Constructor with optional seed + * @param seed Random seed (0 = use random device) + */ + explicit RandomGenerator(uint64_t seed = 0); + + /** + * @brief Generate random float in [0, 1) + * @return Random float + */ + Real random_float(); + + /** + * @brief Generate random float in [min, max) + * @param min Minimum value + * @param max Maximum value + * @return Random float + */ + Real random_float(Real min, Real max); + + /** + * @brief Generate random integer in [min, max] + * @param min Minimum value + * @param max Maximum value + * @return Random integer + */ + int random_int(int min, int max); + + /** + * @brief Generate random point in unit disk + * @return Random point (z = 0) + */ + Vec3 random_in_unit_disk(); + + /** + * @brief Generate random point in unit sphere + * @return Random point + */ + Vec3 random_in_unit_sphere(); + + /** + * @brief Generate random unit vector + * @return Random unit vector + */ + Vec3 random_unit_vector(); + + /** + * @brief Generate random vector in hemisphere + * @param normal Hemisphere normal + * @return Random vector in hemisphere + */ + Vec3 random_in_hemisphere(const Vec3 &normal); + + /** + * @brief Generate random cosine-weighted direction + * @param normal Surface normal + * @return Random direction (cosine-weighted) + */ + Vec3 random_cosine_direction(const Vec3 &normal); + + /** + * @brief Set seed for reproducible results + * @param seed Random seed + */ + void set_seed(uint64_t seed); + +private: + std::mt19937_64 rng_; ///< Random number generator + std::uniform_real_distribution dist_; ///< Uniform distribution [0, 1) +}; + +/** + * @brief Get thread-local random generator + * @return Reference to thread-local generator + */ +RandomGenerator &get_thread_random(); + +/** + * @brief Generate random float in [0, 1) using thread-local generator + * @return Random float + */ +inline Real random_float() { + return get_thread_random().random_float(); +} + +/** + * @brief Generate random float in [min, max) using thread-local generator + * @param min Minimum value + * @param max Maximum value + * @return Random float + */ +inline Real random_float(Real min, Real max) { + return get_thread_random().random_float(min, max); +} + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_RANDOM_H +``` + +### 文件:include/are/utils/file_utils.h + +```cpp +/** + * @file file_utils.h + * @brief File system utilities + */ + +#ifndef ARE_INCLUDE_UTILS_FILE_UTILS_H +#define ARE_INCLUDE_UTILS_FILE_UTILS_H + +#include +#include +#include + +namespace are { + +/** + * @brief Read entire file into string + * @param filepath File path + * @return File contents (empty if failed) + */ +std::string read_file_to_string(const std::string& filepath); + +/** + * @brief Read entire file into byte array + * @param filepath File path + * @return File contents (empty if failed) + */ +std::vector read_file_to_bytes(const std::string& filepath); + +/** + * @brief Write string to file + * @param filepath File path + * @param content Content to write + * @return true if write succeeded + */ +bool write_string_to_file(const std::string& filepath, const std::string& content); + +/** + * @brief Write bytes to file + * @param filepath File path + * @param data Data pointer + * @param size Data size in bytes + * @return true if write succeeded + */ +bool write_bytes_to_file(const std::string& filepath, const void* data, size_t size); + +/** + * @brief Check if file exists + * @param filepath File path + * @return true if file exists + */ +bool file_exists(const std::string& filepath); + +/** + * @brief Check if path is directory + * @param path Directory path + * @return true if directory exists + */ +bool is_directory(const std::string& path); + +/** + * @brief Create directory (including parent directories) + * @param path Directory path + * @return true if creation succeeded + */ +bool create_directory(const std::string& path); + +/** + * @brief Get file extension + * @param filepath File path + * @return Extension (lowercase, without dot) + */ +std::string get_file_extension(const std::string& filepath); + +/** + * @brief Get filename from path + * @param filepath File path + * @return Filename (without directory) + */ +std::string get_filename(const std::string& filepath); + +/** + * @brief Get directory from path + * @param filepath File path + * @return Directory path + */ +std::string get_directory(const std::string& filepath); + +/** + * @brief Join path components + * @param parts Path components + * @return Joined path + */ +std::string join_path(const std::vector& parts); + +/** + * @brief Normalize path (resolve .. and .) + * @param path Path to normalize + * @return Normalized path + */ +std::string normalize_path(const std::string& path); + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_FILE_UTILS_H +``` + +### 文件:include/are/platform/window.h + +```cpp +/** + * @file window.h + * @brief Window management using GLFW + */ + +#ifndef ARE_INCLUDE_PLATFORM_WINDOW_H +#define ARE_INCLUDE_PLATFORM_WINDOW_H + +#include +#include +#include + +// Forward declare GLFW types to avoid including GLFW in header +struct GLFWwindow; + +namespace are { + +/** + * @class Window + * @brief GLFW window wrapper + * + * Manages window creation, input handling, and OpenGL context. + */ +class Window { +public: + /** + * @brief Constructor + * @param config Window configuration + */ + explicit Window(const WindowConfig& config); + + /** + * @brief Destructor + */ + ~Window(); + + // Window control + bool should_close() const; + void set_should_close(bool should_close); + void swap_buffers(); + void poll_events(); + + // Window properties + int get_width() const; + int get_height() const; + Real get_aspect_ratio() const; + const std::string& get_title() const; + + void set_title(const std::string& title); + void set_size(int width, int height); + + // Framebuffer size (may differ from window size on high-DPI displays) + void get_framebuffer_size(int& width, int& height) const; + + // VSync control + void set_vsync(bool enabled); + bool get_vsync() const; + + // Input queries (basic support) + bool is_key_pressed(int key) const; + bool is_mouse_button_pressed(int button) const; + void get_cursor_pos(double& x, double& y) const; + + // Internal + GLFWwindow* get_native_window() const { return window_; } + +private: + void initialize_glfw(); + void create_window(); + void setup_callbacks(); + + static void framebuffer_size_callback(GLFWwindow* window, int width, int height); + static void error_callback(int error, const char* description); + + GLFWwindow* window_; ///< GLFW window handle + WindowConfig config_; ///< Window configuration + bool vsync_enabled_; ///< VSync state + + static int instance_count_; ///< Number of Window instances +}; + +} // namespace are + +#endif // ARE_INCLUDE_PLATFORM_WINDOW_H +``` + +### 文件:include/are/platform/gl_context.h + +```cpp +/** + * @file gl_context.h + * @brief OpenGL context management + */ + +#ifndef ARE_INCLUDE_PLATFORM_GL_CONTEXT_H +#define ARE_INCLUDE_PLATFORM_GL_CONTEXT_H + +#include +#include + +namespace are { + +/** + * @class GLContext + * @brief OpenGL context initialization and management + * + * Handles GLAD initialization and provides OpenGL utility functions. + */ +class GLContext { +public: + /** + * @brief Initialize OpenGL context (load function pointers) + * @return true if initialization succeeded + */ + static bool initialize(); + + /** + * @brief Check if context is initialized + * @return true if initialized + */ + static bool is_initialized(); + + /** + * @brief Get OpenGL version string + * @return Version string + */ + static std::string get_version(); + + /** + * @brief Get OpenGL renderer string + * @return Renderer string + */ + static std::string get_renderer(); + + /** + * @brief Get OpenGL vendor string + * @return Vendor string + */ + static std::string get_vendor(); + + /** + * @brief Check if OpenGL extension is supported + * @param extension Extension name + * @return true if supported + */ + static bool is_extension_supported(const std::string& extension); + + /** + * @brief Print OpenGL information to console + */ + static void print_info(); + + /** + * @brief Check for OpenGL errors + * @param file Source file + * @param line Line number + * @return true if error occurred + */ + static bool check_error(const char* file, int line); + + /** + * @brief Clear all OpenGL errors + */ + static void clear_errors(); + +private: + static bool initialized_; ///< Initialization flag +}; + +} // namespace are + +// OpenGL error checking macro +#ifdef ARE_ENABLE_DEBUG_VIS + #define ARE_GL_CHECK() are::GLContext::check_error(__FILE__, __LINE__) +#else + #define ARE_GL_CHECK() ((void)0) +#endif + +#endif // ARE_INCLUDE_PLATFORM_GL_CONTEXT_H +``` + +### 文件:include/are/acceleration/bvh_node.h + +```cpp +/** + * @file bvh_node.h + * @brief BVH node structure + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_NODE_H +#define ARE_INCLUDE_ACCELERATION_BVH_NODE_H + +#include +#include + +namespace are { + +/** + * @struct BVHNode + * @brief Node in Bounding Volume Hierarchy + * + * Uses a compact representation for efficient GPU transfer. + */ +struct BVHNode { + AABB bounds_; ///< Node bounding box + + union { + uint32_t left_child_; ///< Left child index (internal node) + uint32_t first_primitive_; ///< First primitive index (leaf node) + }; + + union { + uint32_t right_child_; ///< Right child index (internal node) + uint32_t primitive_count_; ///< Number of primitives (leaf node) + }; + + /** + * @brief Check if node is a leaf + * @return true if leaf node + */ + bool is_leaf() const { + return primitive_count_ > 0; + } + + /** + * @brief Get node surface area (for SAH) + * @return Surface area + */ + Real surface_area() const { + return bounds_.surface_area(); + } +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_NODE_H +``` + +### 文件:include/are/acceleration/bvh_builder.h + +```cpp +/** + * @file bvh_builder.h + * @brief BVH construction algorithms + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H +#define ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @enum BVHSplitMethod + * @brief BVH splitting strategies + */ +enum class BVHSplitMethod { + ARE_BVH_SPLIT_MIDDLE, ///< Split at midpoint + ARE_BVH_SPLIT_SAH ///< Surface Area Heuristic +}; + +/** + * @struct BVHBuildConfig + * @brief Configuration for BVH construction + */ +struct BVHBuildConfig { + BVHSplitMethod split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH; + int max_leaf_size_ = 4; ///< Maximum triangles per leaf + int max_depth_ = 64; ///< Maximum tree depth + bool use_multithreading_ = true; ///< Use parallel construction +}; + +/** + * @class BVHBuilder + * @brief Constructs BVH from triangle list + */ +class BVHBuilder { +public: + /** + * @brief Constructor + * @param config Build configuration + */ + explicit BVHBuilder(const BVHBuildConfig& config = BVHBuildConfig()); + + /** + * @brief Build BVH from triangles + * @param triangles Triangle list + * @param nodes Output node list + * @param primitive_indices Output primitive index list + * @return Root node index + */ + uint32_t build(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices); + + /** + * @brief Get build statistics + * @param node_count Output node count + * @param leaf_count Output leaf count + * @param max_depth Output maximum depth reached + */ + void get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const; + +private: + struct BuildEntry { + uint32_t parent_; + uint32_t start_; + uint32_t end_; + int depth_; + }; + + uint32_t build_recursive(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices, + uint32_t start, uint32_t end, int depth); + + int find_best_split_axis(const std::vector& triangles, + const std::vector& indices, + uint32_t start, uint32_t end); + + Real compute_sah_cost(const AABB& bounds, uint32_t count); + + BVHBuildConfig config_; + size_t node_count_; + size_t leaf_count_; + int max_depth_reached_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H +``` + +### 文件:include/are/acceleration/bvh.h + +```cpp +/** + * @file bvh.h + * @brief BVH interface and traversal + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_H +#define ARE_INCLUDE_ACCELERATION_BVH_H + +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +/** + * @class BVH + * @brief Bounding Volume Hierarchy for ray tracing acceleration + */ +class BVH { +public: + /** + * @brief Constructor + */ + BVH(); + + /** + * @brief Destructor + */ + ~BVH(); + + /** + * @brief Build BVH from triangle list + * @param triangles Triangle list + * @param config Build configuration + * @return true if build succeeded + */ + bool build(const std::vector& triangles, + const BVHBuildConfig& config = BVHBuildConfig()); + + /** + * @brief Traverse BVH and find closest intersection + * @param ray Ray to trace + * @param hit Output hit record + * @return true if intersection found + */ + bool intersect(const Ray& ray, HitRecord& hit) const; + + /** + * @brief Fast occlusion test (any hit) + * @param ray Ray to trace + * @param t_max Maximum t value + * @return true if any intersection found + */ + bool intersect_any(const Ray& ray, Real t_max) const; + + /** + * @brief Check if BVH is built + * @return true if built + */ + bool is_built() const { return !nodes_.empty(); } + + /** + * @brief Get BVH nodes (for GPU upload) + * @return Node array + */ + const std::vector& get_nodes() const { return nodes_; } + + /** + * @brief Get primitive indices + * @return Index array + */ + const std::vector& get_primitive_indices() const { + return primitive_indices_; + } + + /** + * @brief Get triangles + * @return Triangle array + */ + const std::vector& get_triangles() const { return triangles_; } + + /** + * @brief Get memory usage in bytes + * @return Memory usage + */ + size_t get_memory_usage() const; + + /** + * @brief Clear BVH data + */ + void clear(); + +private: + // Recursive traversal (kept for reference) + bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const; + bool intersect_any_recursive(uint32_t node_index, const Ray& ray, Real t_max) const; + + // Optimized iterative traversal + bool intersect_iterative(const Ray& ray, HitRecord& hit) const; + bool intersect_any_iterative(const Ray& ray, Real t_max) const; + + // Fast intersection helpers + inline bool intersect_aabb_fast(const AABB& bounds, const Ray& ray, + const Vec3& inv_dir, Real t_max, + Real& t_min_out, Real& t_max_out) const; + inline bool intersect_triangle_fast(const Triangle& triangle, const Ray& ray, + Real t_max, HitRecord& hit) const; + + std::vector nodes_; ///< BVH nodes + std::vector primitive_indices_; ///< Primitive index array + std::vector triangles_; ///< Triangle data + uint32_t root_index_; ///< Root node index +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_H +``` + +### 文件:include/are/rasterizer/rasterizer.h + +```cpp +/** + * @file rasterizer.h + * @brief Rasterization pipeline for G-Buffer generation + */ + +#ifndef ARE_INCLUDE_RASTERIZER_RASTERIZER_H +#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H + +#include +#include +#include + +namespace are { + +// Forward declarations +class GBuffer; +class ShaderProgram; +class SceneManager; +class Camera; +class Mesh; + +/** + * @class Rasterizer + * @brief OpenGL rasterization pipeline + * + * Renders scene geometry to G-Buffer using traditional rasterization. + */ +class Rasterizer { +public: + /** + * @brief Constructor + * @param width Framebuffer width + * @param height Framebuffer height + */ + Rasterizer(int width, int height); + + /** + * @brief Destructor + */ + ~Rasterizer(); + + /** + * @brief Resize framebuffer + * @param width New width + * @param height New height + */ + void resize(int width, int height); + + /** + * @brief Render scene to G-Buffer + * @param scene Scene manager + * @param camera Camera + */ + void render_gbuffer(const SceneManager& scene, const Camera& camera); + + /** + * @brief Get G-Buffer + * @return G-Buffer reference + */ + GBuffer& get_gbuffer(); + const GBuffer& get_gbuffer() const; + + /** + * @brief Upload mesh data to GPU + * @param mesh Mesh to upload + */ + void upload_mesh(Mesh& mesh); + + /** + * @brief Delete mesh GPU resources + * @param mesh Mesh to delete + */ + void delete_mesh(Mesh& mesh); + +private: + void initialize_shaders(const std::string& shader_dir); + void setup_mesh_buffers(Mesh& mesh); + + std::unique_ptr gbuffer_; ///< G-Buffer + std::unique_ptr gbuffer_shader_; ///< G-Buffer shader + + int width_; ///< Framebuffer width + int height_; ///< Framebuffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_RASTERIZER_H +``` + +### 文件:include/are/rasterizer/shader_program.h + +```cpp +/** + * @file shader_program.h + * @brief OpenGL shader program wrapper + */ + +#ifndef ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H +#define ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H + +#include +#include +#include + +namespace are { + +/** + * @enum ShaderType + * @brief Shader stage types + */ +enum class ShaderType { + ARE_SHADER_VERTEX, + ARE_SHADER_FRAGMENT, + ARE_SHADER_COMPUTE +}; + +/** + * @class ShaderProgram + * @brief OpenGL shader program management + */ +class ShaderProgram { +public: + /** + * @brief Constructor + */ + ShaderProgram(); + + /** + * @brief Destructor + */ + ~ShaderProgram(); + + /** + * @brief Load and compile shader from file + * @param type Shader type + * @param filepath Shader file path + * @return true if compilation succeeded + */ + bool load_shader(ShaderType type, const std::string& filepath); + + /** + * @brief Compile shader from source string + * @param type Shader type + * @param source Shader source code + * @return true if compilation succeeded + */ + bool compile_shader(ShaderType type, const std::string& source); + + /** + * @brief Link shader program + * @return true if linking succeeded + */ + bool link(); + + /** + * @brief Use this shader program + */ + void use() const; + + /** + * @brief Check if program is valid + * @return true if valid + */ + bool is_valid() const { return program_ != 0 && linked_; } + + /** + * @brief Get OpenGL program ID + * @return Program ID + */ + uint32_t get_program() const { return program_; } + + // Uniform setters + void set_uniform(const std::string& name, int value); + void set_uniform(const std::string& name, float value); + void set_uniform(const std::string& name, const Vec2& value); + void set_uniform(const std::string& name, const Vec3& value); + void set_uniform(const std::string& name, const Vec4& value); + void set_uniform(const std::string& name, const Mat3& value); + void set_uniform(const std::string& name, const Mat4& value); + + /** + * @brief Get uniform location (cached) + * @param name Uniform name + * @return Uniform location (-1 if not found) + */ + int get_uniform_location(const std::string& name); + +private: + bool check_compile_errors(uint32_t shader, ShaderType type); + bool check_link_errors(); + + uint32_t program_; ///< OpenGL program ID + uint32_t vertex_shader_; ///< Vertex shader ID + uint32_t fragment_shader_; ///< Fragment shader ID + uint32_t compute_shader_; ///< Compute shader ID + + bool linked_; ///< Link status + std::unordered_map uniform_cache_; ///< Uniform location cache +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H +``` + +### 文件:include/are/rasterizer/gbuffer.h + +```cpp +/** + * @file gbuffer.h + * @brief G-Buffer management for deferred rendering + */ + +#ifndef ARE_INCLUDE_RASTERIZER_GBUFFER_H +#define ARE_INCLUDE_RASTERIZER_GBUFFER_H + +#include +#include + +namespace are { + +/** + * @class GBuffer + * @brief G-Buffer for deferred rendering + * + * Contains multiple render targets for position, normal, albedo, etc. + */ +class GBuffer { +public: + /** + * @brief Constructor + * @param width Buffer width + * @param height Buffer height + */ + GBuffer(int width, int height); + + /** + * @brief Destructor + */ + ~GBuffer(); + + /** + * @brief Resize G-Buffer + * @param width New width + * @param height New height + */ + void resize(int width, int height); + + /** + * @brief Bind G-Buffer for rendering + */ + void bind(); + + /** + * @brief Unbind G-Buffer + */ + void unbind(); + + /** + * @brief Clear all buffers + */ + void clear(); + + /** + * @brief Bind texture for reading + * @param index Texture index (0=position, 1=normal, 2=albedo, etc.) + * @param texture_unit Texture unit to bind to + */ + void bind_texture(int index, int texture_unit); + + // Texture getters + uint32_t get_position_texture() const { return position_texture_; } + uint32_t get_normal_texture() const { return normal_texture_; } + uint32_t get_albedo_texture() const { return albedo_texture_; } + uint32_t get_material_texture() const { return material_texture_; } + uint32_t get_depth_texture() const { return depth_texture_; } + + // Dimensions + int get_width() const { return width_; } + int get_height() const { return height_; } + + /** + * @brief Read pixel data from G-Buffer + * @param index Buffer index + * @param data Output data pointer + */ + void read_pixels(int index, void* data); + +private: + void create_textures(); + void delete_textures(); + void create_framebuffer(); + + uint32_t fbo_; ///< Framebuffer object + uint32_t rbo_depth_; ///< Depth renderbuffer + + // G-Buffer textures + uint32_t position_texture_; ///< World position (RGB16F) + uint32_t normal_texture_; ///< World normal (RGB16F) + uint32_t albedo_texture_; ///< Albedo + Metallic (RGBA8) + uint32_t material_texture_; ///< Roughness + AO (RG8) + uint32_t depth_texture_; ///< Depth (R32F) + + int width_; ///< Buffer width + int height_; ///< Buffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_GBUFFER_H +``` + +### 文件:include/are/texture/texture.h + +```cpp +/** + * @file texture.h + * @brief Texture resource management + */ + +#ifndef ARE_INCLUDE_TEXTURE_TEXTURE_H +#define ARE_INCLUDE_TEXTURE_TEXTURE_H + +#include +#include + +namespace are { + +/** + * @enum TextureFormat + * @brief Texture internal formats + */ +enum class TextureFormat { + ARE_TEXTURE_R8, + ARE_TEXTURE_RG8, + ARE_TEXTURE_RGB8, + ARE_TEXTURE_RGBA8, + ARE_TEXTURE_R16F, + ARE_TEXTURE_RG16F, + ARE_TEXTURE_RGB16F, + ARE_TEXTURE_RGBA16F, + ARE_TEXTURE_R32F, + ARE_TEXTURE_RG32F, + ARE_TEXTURE_RGB32F, + ARE_TEXTURE_RGBA32F +}; + +/** + * @enum TextureFilter + * @brief Texture filtering modes + */ +enum class TextureFilter { + ARE_TEXTURE_FILTER_NEAREST, + ARE_TEXTURE_FILTER_LINEAR, + ARE_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST, + ARE_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST, + ARE_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR, + ARE_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR +}; + +/** + * @enum TextureWrap + * @brief Texture wrapping modes + */ +enum class TextureWrap { + ARE_TEXTURE_WRAP_REPEAT, + ARE_TEXTURE_WRAP_CLAMP_TO_EDGE, + ARE_TEXTURE_WRAP_CLAMP_TO_BORDER, + ARE_TEXTURE_WRAP_MIRRORED_REPEAT +}; + +/** + * @class Texture + * @brief OpenGL texture wrapper + */ +class Texture { +public: + /** + * @brief Constructor + */ + Texture(); + + /** + * @brief Destructor + */ + ~Texture(); + + /** + * @brief Load texture from file + * @param filepath Image file path + * @param format Desired texture format + * @param generate_mipmaps Generate mipmaps + * @return true if load succeeded + */ + bool load_from_file(const std::string& filepath, + TextureFormat format = TextureFormat::ARE_TEXTURE_RGBA8, + bool generate_mipmaps = true); + + /** + * @brief Create texture from raw data + * @param width Texture width + * @param height Texture height + * @param format Texture format + * @param data Pixel data + * @param generate_mipmaps Generate mipmaps + * @return true if creation succeeded + */ + bool create_from_data(int width, int height, + TextureFormat format, + const void* data, + bool generate_mipmaps = true); + + /** + * @brief Bind texture to texture unit + * @param unit Texture unit (0-31) + */ + void bind(int unit = 0) const; + + /** + * @brief Unbind texture + */ + void unbind() const; + + /** + * @brief Set texture filtering + * @param min_filter Minification filter + * @param mag_filter Magnification filter + */ + void set_filter(TextureFilter min_filter, TextureFilter mag_filter); + + /** + * @brief Set texture wrapping + * @param wrap_s S-axis wrapping + * @param wrap_t T-axis wrapping + */ + void set_wrap(TextureWrap wrap_s, TextureWrap wrap_t); + + /** + * @brief Generate mipmaps + */ + void generate_mipmaps(); + + // Getters + uint32_t get_id() const { return texture_id_; } + int get_width() const { return width_; } + int get_height() const { return height_; } + TextureFormat get_format() const { return format_; } + bool is_valid() const { return texture_id_ != 0; } + + /** + * @brief Delete texture + */ + void destroy(); + +private: + uint32_t texture_id_; ///< OpenGL texture ID + int width_; ///< Texture width + int height_; ///< Texture height + TextureFormat format_; ///< Texture format +}; + +} // namespace are + +#endif // ARE_INCLUDE_TEXTURE_TEXTURE_H +``` + +### 文件:include/are/texture/texture_manager.h + +```cpp +/** + * @file texture_manager.h + * @brief Texture resource management and caching + */ + +#ifndef ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H +#define ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H + +#include +#include +#include +#include +#include + +namespace are { + +/** + * @class TextureManager + * @brief Manages texture loading and caching + * + * Automatically handles texture deduplication and lifetime management. + */ +class TextureManager { +public: + /** + * @brief Constructor + */ + TextureManager(); + + /** + * @brief Destructor + */ + ~TextureManager(); + + /** + * @brief Load texture from file (with caching) + * @param filepath Texture file path + * @param format Desired texture format + * @param generate_mipmaps Generate mipmaps + * @return Texture handle (are_invalid_handle if failed) + */ + TextureHandle load_texture(const std::string& filepath, + TextureFormat format = TextureFormat::ARE_TEXTURE_RGBA8, + bool generate_mipmaps = true); + + /** + * @brief Create texture from raw data + * @param name Texture name (for caching) + * @param width Texture width + * @param height Texture height + * @param format Texture format + * @param data Pixel data + * @param generate_mipmaps Generate mipmaps + * @return Texture handle + */ + TextureHandle create_texture(const std::string& name, + int width, int height, + TextureFormat format, + const void* data, + bool generate_mipmaps = true); + + /** + * @brief Get texture by handle + * @param handle Texture handle + * @return Texture pointer (nullptr if not found) + */ + Texture* get_texture(TextureHandle handle); + const Texture* get_texture(TextureHandle handle) const; + + /** + * @brief Unload texture + * @param handle Texture handle + */ + void unload_texture(TextureHandle handle); + + /** + * @brief Clear all textures + */ + void clear(); + + /** + * @brief Get total texture memory usage + * @return Memory usage in bytes + */ + size_t get_memory_usage() const; + + /** + * @brief Get number of loaded textures + * @return Texture count + */ + size_t get_texture_count() const { return textures_.size(); } + +private: + std::unordered_map path_to_handle_; + std::unordered_map> textures_; + TextureHandle next_handle_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H +``` + +### 文件:include/are/texture/sampler.h + +```cpp +/** + * @file sampler.h + * @brief Texture sampling utilities + */ + +#ifndef ARE_INCLUDE_TEXTURE_SAMPLER_H +#define ARE_INCLUDE_TEXTURE_SAMPLER_H + +#include + +namespace are { + +// Forward declaration +class Texture; +class TextureWrap; + +/** + * @class Sampler + * @brief Texture sampling utilities for CPU ray tracing + * + * Provides bilinear filtering and wrapping modes for CPU-side texture access. + */ +class Sampler { +public: + /** + * @brief Sample texture at UV coordinates + * @param texture Texture to sample + * @param uv UV coordinates + * @return Sampled color (RGBA) + */ + static Vec4 sample(const Texture& texture, const Vec2& uv); + + /** + * @brief Sample texture with bilinear filtering + * @param texture Texture to sample + * @param uv UV coordinates + * @return Sampled color (RGBA) + */ + static Vec4 sample_bilinear(const Texture& texture, const Vec2& uv); + + /** + * @brief Sample texture at nearest pixel + * @param texture Texture to sample + * @param uv UV coordinates + * @return Sampled color (RGBA) + */ + static Vec4 sample_nearest(const Texture& texture, const Vec2& uv); + +private: + static Vec2 apply_wrap(const Vec2& uv, TextureWrap wrap); +}; + +} // namespace are + +#endif // ARE_INCLUDE_TEXTURE_SAMPLER_H +``` + +接下来我再给你一个项目中的.cpp文件,让你熟悉错误处理和日志输出等信息: +### 文件:src/utils/image_io.cpp +```cpp +/** + * @file image_io.cpp + * @brief Implementation of image I/O utilities + */ + +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + +namespace are { + +bool ImageData::is_valid() const { + return width_ > 0 && height_ > 0 && channels_ > 0 && !data_.empty(); +} + +const uint8_t* ImageData::get_pixel(int x, int y) const { + if (x < 0 || x >= width_ || y < 0 || y >= height_) { + return nullptr; + } + + size_t index = (y * width_ + x) * channels_; + return &data_[index]; +} + +void ImageData::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (x < 0 || x >= width_ || y < 0 || y >= height_) { + return; + } + + size_t index = (y * width_ + x) * channels_; + + data_[index + 0] = r; + data_[index + 1] = g; + data_[index + 2] = b; + + if (channels_ == 4) { + data_[index + 3] = a; + } +} + +ImageData load_image(const std::string& filename, bool flip_vertically) { + ImageData image; + + // Set flip flag + stbi_set_flip_vertically_on_load(flip_vertically ? 1 : 0); + + // Load image + int width, height, channels; + unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, 0); + + if (!data) { + ARE_LOG_ERROR("Failed to load image: " + filename + " - " + stbi_failure_reason()); + return image; + } + + // Copy data to ImageData + image.width_ = width; + image.height_ = height; + image.channels_ = channels; + + size_t data_size = width * height * channels; + image.data_.resize(data_size); + std::memcpy(image.data_.data(), data, data_size); + + // Free stb_image data + stbi_image_free(data); + + ARE_LOG_INFO("Loaded image: " + filename + " (" + + std::to_string(width) + "x" + std::to_string(height) + + ", " + std::to_string(channels) + " channels)"); + + return image; +} + +ImageFormat detect_format(const std::string& filename) { + std::string ext; + size_t dot_pos = filename.find_last_of('.'); + + if (dot_pos != std::string::npos) { + ext = filename.substr(dot_pos + 1); + + // Convert to lowercase + std::transform(ext.begin(), ext.end(), ext.begin(), + [](unsigned char c) { return std::tolower(c); }); + } + + if (ext == "ppm") return ImageFormat::ARE_IMAGE_FORMAT_PPM; + if (ext == "bmp") return ImageFormat::ARE_IMAGE_FORMAT_BMP; + if (ext == "png") return ImageFormat::ARE_IMAGE_FORMAT_PNG; + if (ext == "jpg" || ext == "jpeg") return ImageFormat::ARE_IMAGE_FORMAT_JPG; + + // Default to PNG + return ImageFormat::ARE_IMAGE_FORMAT_PNG; +} + +bool save_image(const std::string& filename, const ImageData& data, ImageFormat format) { + if (!data.is_valid()) { + ARE_LOG_ERROR("Cannot save invalid image data"); + return false; + } + + // Auto-detect format if not specified + if (format == ImageFormat::ARE_IMAGE_FORMAT_PNG) { + format = detect_format(filename); + } + + int result = 0; + + switch (format) { + case ImageFormat::ARE_IMAGE_FORMAT_PPM: { + // Write PPM manually (stb doesn't support it) + std::ofstream file(filename, std::ios::binary); + if (!file.is_open()) { + ARE_LOG_ERROR("Failed to open file for writing: " + filename); + return false; + } + + file << "P6\n" << data.width_ << " " << data.height_ << "\n255\n"; + + for (int i = 0; i < data.width_ * data.height_; ++i) { + int idx = i * data.channels_; + file.put(data.data_[idx + 0]); // R + file.put(data.data_[idx + 1]); // G + file.put(data.data_[idx + 2]); // B + } + + file.close(); + result = 1; + break; + } + + case ImageFormat::ARE_IMAGE_FORMAT_BMP: + result = stbi_write_bmp(filename.c_str(), data.width_, data.height_, + data.channels_, data.data_.data()); + break; + + case ImageFormat::ARE_IMAGE_FORMAT_PNG: + result = stbi_write_png(filename.c_str(), data.width_, data.height_, + data.channels_, data.data_.data(), + data.width_ * data.channels_); + break; + + case ImageFormat::ARE_IMAGE_FORMAT_JPG: + result = stbi_write_jpg(filename.c_str(), data.width_, data.height_, + data.channels_, data.data_.data(), 90); + break; + } + + if (result == 0) { + ARE_LOG_ERROR("Failed to save image: " + filename); + return false; + } + + ARE_LOG_INFO("Saved image: " + filename); + return true; +} + +bool save_image(const std::string& filename, const uint8_t* pixels, + int width, int height, int channels, ImageFormat format) { + ImageData data; + data.width_ = width; + data.height_ = height; + data.channels_ = channels; + + size_t data_size = width * height * channels; + data.data_.resize(data_size); + std::memcpy(data.data_.data(), pixels, data_size); + + return save_image(filename, data, format); +} + +} // namespace are +``` +哦对了,这是我们现在的CMakeLists.txt,你可以在编写完这个模块之后编写cornell_box的测试代码和独属于Phase 5的测试代码(在CPU光追效率太低的时候使用): +```CMakeLists.txt +cmake_minimum_required(VERSION 3.15) +project(AuroraRenderingEngine VERSION 0.1.0 LANGUAGES CXX C) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Set C standard for GLAD +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Build options +option(ARE_BUILD_SHARED "Build shared library" OFF) +option(ARE_BUILD_EXAMPLES "Build example programs" ON) +option(ARE_ENABLE_PROFILING "Enable performance profiling" ON) +option(ARE_ENABLE_DEBUG_VIS "Enable debug visualization" ON) + +# Set output directories +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# Compiler flags +if(MSVC) + add_compile_options(/W4) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + # Disable specific warnings that are too strict + add_compile_options(/wd4100) # unreferenced formal parameter +else() + add_compile_options(-Wall -Wextra -pedantic) + # Disable specific warnings + add_compile_options(-Wno-unused-parameter) +endif() + +# Find required packages +find_package(OpenGL REQUIRED) +find_package(glfw3 REQUIRED) +find_package(glm REQUIRED) + +# Try to find spdlog (system installation) +find_package(spdlog QUIET) + +if(NOT spdlog_FOUND) + message(STATUS "spdlog not found in system, using local version") + # Use local spdlog + set(SPDLOG_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/spdlog/include") + if(NOT EXISTS ${SPDLOG_INCLUDE_DIR}) + message(FATAL_ERROR "spdlog not found. Please install spdlog or place it in lib/spdlog/") + endif() + add_library(spdlog INTERFACE) + target_include_directories(spdlog INTERFACE ${SPDLOG_INCLUDE_DIR}) + # spdlog compile definitions + target_compile_definitions(spdlog INTERFACE SPDLOG_COMPILED_LIB) +endif() + +# Find OpenMP (optional) +find_package(OpenMP) +if(OpenMP_CXX_FOUND) + set(ARE_USE_OPENMP ON) + add_compile_definitions(ARE_USE_OPENMP) + message(STATUS "OpenMP found and enabled") +else() + message(STATUS "OpenMP not found, multithreading will use std::thread") +endif() + +# GLAD library path +set(GLAD_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/glad") +set(GLAD_SOURCE_DIR "${CMAKE_SOURCE_DIR}/lib/glad") + +if(NOT EXISTS ${GLAD_INCLUDE_DIR}) + message(FATAL_ERROR "GLAD include directory not found: ${GLAD_INCLUDE_DIR}") +endif() + +if(NOT EXISTS ${GLAD_SOURCE_DIR}) + message(FATAL_ERROR "GLAD source directory not found: ${GLAD_SOURCE_DIR}") +endif() + +# STB library path +set(STB_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/stb") + +if(NOT EXISTS ${STB_INCLUDE_DIR}) + message(FATAL_ERROR "STB include directory not found: ${STB_INCLUDE_DIR}") +endif() + +# Collect all source files from src/ +file(GLOB_RECURSE ARE_SOURCES + "${CMAKE_SOURCE_DIR}/src/*.cpp" + "${CMAKE_SOURCE_DIR}/src/*.c" +) + +# Collect all GLAD source files +file(GLOB GLAD_SOURCES + "${GLAD_SOURCE_DIR}/*.c" +) + +# Add GLAD sources to ARE sources +list(APPEND ARE_SOURCES ${GLAD_SOURCES}) + +# Collect all header files +file(GLOB_RECURSE ARE_HEADERS + "${CMAKE_SOURCE_DIR}/include/*.h" + "${CMAKE_SOURCE_DIR}/include/*.hpp" +) + +# Create library +if(ARE_BUILD_SHARED) + add_library(are SHARED ${ARE_SOURCES} ${ARE_HEADERS}) + target_compile_definitions(are PRIVATE ARE_BUILD_SHARED) + message(STATUS "Building shared library") +else() + add_library(are STATIC ${ARE_SOURCES} ${ARE_HEADERS}) + message(STATUS "Building static library") +endif() + +# Set target properties +set_target_properties(are PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + PUBLIC_HEADER "${ARE_HEADERS}" +) + +# Include directories +target_include_directories(are + PUBLIC + $ + $ + PRIVATE + ${CMAKE_SOURCE_DIR}/src + ${GLAD_INCLUDE_DIR} + ${STB_INCLUDE_DIR} +) + +# Link libraries +target_link_libraries(are + PUBLIC + OpenGL::GL + glfw + glm::glm +) + +# Link spdlog +if(spdlog_FOUND) + target_link_libraries(are PUBLIC spdlog::spdlog) +else() + target_link_libraries(are PUBLIC spdlog) + target_include_directories(are PRIVATE ${SPDLOG_INCLUDE_DIR}) +endif() + +# Link OpenMP if available +if(OpenMP_CXX_FOUND) + target_link_libraries(are PUBLIC OpenMP::OpenMP_CXX) +endif() + +# Platform-specific libraries +if(UNIX AND NOT APPLE) + target_link_libraries(are PUBLIC dl pthread) +endif() + +# Compile definitions +if(ARE_ENABLE_PROFILING) + target_compile_definitions(are PUBLIC ARE_ENABLE_PROFILING) + message(STATUS "Profiling enabled") +endif() + +if(ARE_ENABLE_DEBUG_VIS) + target_compile_definitions(are PUBLIC ARE_ENABLE_DEBUG_VIS) + message(STATUS "Debug visualization enabled") +endif() + +# Build examples +if(ARE_BUILD_EXAMPLES) + # Define helper function for creating examples + function(add_are_example EXAMPLE_NAME) + add_executable(${EXAMPLE_NAME} ${ARGN}) + target_link_libraries(${EXAMPLE_NAME} PRIVATE are) + set_target_properties(${EXAMPLE_NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + endfunction() + + # Add example subdirectories + add_subdirectory(examples/00_phase1_test) + add_subdirectory(examples/01_phase2_test) + add_subdirectory(examples/02_visual_test) + add_subdirectory(examples/02_phase3_test) + add_subdirectory(examples/03_phase4_test) + + message(STATUS "Examples will be built") +endif() + +# Installation rules +install(TARGETS are + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/are +) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/are + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" +) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/shaders + DESTINATION share/are/shaders +) + +# Print configuration summary +message(STATUS "") +message(STATUS "========================================") +message(STATUS "Aurora Rendering Engine Configuration") +message(STATUS "========================================") +message(STATUS " Version: ${PROJECT_VERSION}") +message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS " Library type: ${ARE_BUILD_SHARED}") +message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}") +message(STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS "") +message(STATUS "Features:") +message(STATUS " OpenMP support: ${ARE_USE_OPENMP}") +message(STATUS " Build examples: ${ARE_BUILD_EXAMPLES}") +message(STATUS " Enable profiling: ${ARE_ENABLE_PROFILING}") +message(STATUS " Enable debug vis: ${ARE_ENABLE_DEBUG_VIS}") +message(STATUS "") +message(STATUS "Dependencies:") +message(STATUS " OpenGL: ${OPENGL_LIBRARIES}") +message(STATUS " GLFW: Found") +message(STATUS " GLM: Found") +if(spdlog_FOUND) + message(STATUS " spdlog: Found (system)") +else() + message(STATUS " spdlog: Found (local)") +endif() +message(STATUS " GLAD: ${GLAD_INCLUDE_DIR}") +message(STATUS " STB: ${STB_INCLUDE_DIR}") +message(STATUS "") +message(STATUS "Output directories:") +message(STATUS " Executables: ${CMAKE_BINARY_DIR}/bin") +message(STATUS " Libraries: ${CMAKE_BINARY_DIR}/lib") +message(STATUS "========================================") +message(STATUS "") +``` + +目前你只需要实现Phase 5所要求的文件即可,其他大部分模块已经实现。 diff --git a/examples/01_phase2_test/CMakeLists.txt b/examples/01_phase2_test/CMakeLists.txt new file mode 100644 index 0000000..eb2f04a --- /dev/null +++ b/examples/01_phase2_test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Phase 2 verification example + +add_are_example(phase2_test + main.cpp +) + +# Copy to bin directory for easy execution +set_target_properties(phase2_test PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) diff --git a/examples/01_phase2_test/main.cpp b/examples/01_phase2_test/main.cpp new file mode 100644 index 0000000..a0b82f0 --- /dev/null +++ b/examples/01_phase2_test/main.cpp @@ -0,0 +1,326 @@ +/** + * @file main.cpp + * @brief Phase 2 verification program + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace are; + +// Test result tracking +struct TestResult { + std::string name; + bool passed; + std::string message; +}; + +std::vector test_results; + +void report_test(const std::string& name, bool passed, const std::string& message = "") { + test_results.push_back({name, passed, message}); + if (passed) { + ARE_LOG_INFO("✓ " + name); + } else { + ARE_LOG_ERROR("✗ " + name + ": " + message); + } +} + +// Test 1: Vertex operations +void test_vertex() { + Vertex v1(Vec3(1, 2, 3)); + Vertex v2(Vec3(4, 5, 6), Vec3(0, 1, 0)); + Vertex v3 = Vertex::lerp(v1, v2, 0.5f); + + bool passed = glm::length(v3.position_ - Vec3(2.5f, 3.5f, 4.5f)) < are_epsilon; + report_test("Vertex interpolation", passed); +} + +// Test 2: AABB operations +void test_aabb() { + AABB aabb1(Vec3(-1, -1, -1), Vec3(1, 1, 1)); + AABB aabb2(Vec3(0, 0, 0), Vec3(2, 2, 2)); + + bool test1 = aabb1.is_valid(); + bool test2 = aabb1.contains(Vec3(0, 0, 0)); + bool test3 = aabb1.intersects(aabb2); + bool test4 = aabb1.longest_axis() == 0; // All axes equal + + AABB merged = AABB::merge(aabb1, aabb2); + bool test5 = merged.contains(Vec3(-1, -1, -1)) && merged.contains(Vec3(2, 2, 2)); + + report_test("AABB validity", test1); + report_test("AABB contains point", test2); + report_test("AABB intersection", test3); + report_test("AABB merge", test5); +} + +// Test 3: Triangle operations +void test_triangle() { + Vertex v0(Vec3(0, 0, 0), Vec3(0, 0, 1)); + Vertex v1(Vec3(1, 0, 0), Vec3(0, 0, 1)); + Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1)); + + Triangle tri(v0, v1, v2); + + Vec3 centroid = tri.centroid(); + bool test1 = glm::length(centroid - Vec3(1.0f/3.0f, 1.0f/3.0f, 0.0f)) < are_epsilon; + + Vec3 normal = tri.normal(); + bool test2 = glm::length(normal - Vec3(0, 0, 1)) < are_epsilon; + + Real area = tri.area(); + bool test3 = std::abs(area - 0.5f) < are_epsilon; + + AABB aabb = tri.compute_aabb(); + bool test4 = aabb.contains(Vec3(0, 0, 0)) && aabb.contains(Vec3(1, 0, 0)); + + report_test("Triangle centroid", test1); + report_test("Triangle normal", test2); + report_test("Triangle area", test3); + report_test("Triangle AABB", test4); +} + +// Test 4: Ray-Triangle intersection +void test_ray_triangle_intersection() { + Vertex v0(Vec3(0, 0, 0), Vec3(0, 0, 1)); + Vertex v1(Vec3(1, 0, 0), Vec3(0, 0, 1)); + Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1)); + + Triangle tri(v0, v1, v2); + + // Ray hitting the triangle + Ray ray1(Vec3(0.25f, 0.25f, -1.0f), Vec3(0, 0, 1)); + HitRecord hit1; + bool test1 = tri.intersect(ray1, hit1); + + // Ray missing the triangle + Ray ray2(Vec3(2, 2, -1), Vec3(0, 0, 1)); + HitRecord hit2; + bool test2 = !tri.intersect(ray2, hit2); + + report_test("Ray-Triangle hit", test1); + report_test("Ray-Triangle miss", test2); +} + +// Test 5: Transform operations +void test_transform() { + Transform t1 = Transform::translate(Vec3(1, 2, 3)); + Transform t2 = Transform::rotate(Vec3(0, are_pi / 2, 0)); + Transform t3 = Transform::scale(Vec3(2, 2, 2)); + + Vec3 point = Vec3(1, 0, 0); + Vec3 transformed = t1.transform_point(point); + bool test1 = glm::length(transformed - Vec3(2, 2, 3)) < are_epsilon; + + Vec3 scaled = t3.transform_point(point); + bool test2 = glm::length(scaled - Vec3(2, 0, 0)) < are_epsilon; + + report_test("Transform translation", test1); + report_test("Transform scale", test2); +} + +// Test 6: Camera operations +void test_camera() { + Camera camera(Vec3(0, 0, 5), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, 16.0f / 9.0f, 0.1f, 100.0f); + + Vec3 forward = camera.get_forward(); + bool test1 = glm::length(forward - Vec3(0, 0, -1)) < are_epsilon; + + Vec3 origin, direction; + camera.generate_ray(0.5f, 0.5f, origin, direction); + bool test2 = glm::length(origin - Vec3(0, 0, 5)) < are_epsilon; + bool test3 = glm::length(direction - Vec3(0, 0, -1)) < 0.1f; // Approximate + + report_test("Camera forward vector", test1); + report_test("Camera ray generation origin", test2); + report_test("Camera ray generation direction", test3); +} + +// Test 7: Mesh operations +void test_mesh() { + std::vector vertices = { + Vertex(Vec3(0, 0, 0), Vec3(0, 0, 1)), + Vertex(Vec3(1, 0, 0), Vec3(0, 0, 1)), + Vertex(Vec3(0, 1, 0), Vec3(0, 0, 1)) + }; + + std::vector indices = {0, 1, 2}; + + Mesh mesh(vertices, indices); + + bool test1 = mesh.get_vertex_count() == 3; + bool test2 = mesh.get_triangle_count() == 1; + bool test3 = mesh.get_aabb().is_valid(); + + Vertex v0, v1, v2; + bool test4 = mesh.get_triangle(0, v0, v1, v2); + + report_test("Mesh vertex count", test1); + report_test("Mesh triangle count", test2); + report_test("Mesh AABB", test3); + report_test("Mesh get triangle", test4); +} + +// Test 8: Material operations +void test_material() { + Material mat; + mat.set_albedo(Vec3(0.8f, 0.2f, 0.1f)); + mat.set_metallic(0.5f); + mat.set_roughness(0.3f); + mat.set_emissive(Vec3(1.0f, 0.5f, 0.0f)); + + bool test1 = glm::length(mat.get_albedo() - Vec3(0.8f, 0.2f, 0.1f)) < are_epsilon; + bool test2 = std::abs(mat.get_metallic() - 0.5f) < are_epsilon; + bool test3 = mat.is_emissive(); + + mat.set_albedo_map("textures/albedo.png"); + bool test4 = mat.has_albedo_map(); + + report_test("Material albedo", test1); + report_test("Material metallic", test2); + report_test("Material emissive", test3); + report_test("Material texture map", test4); +} + +// Test 9: Light operations +void test_lights() { + // Directional light + DirectionalLight dir_light(Vec3(0, -1, 0), Vec3(1, 1, 1), 1.0f); + bool test1 = dir_light.affects_point(Vec3(100, 100, 100)); + + // Point light + PointLight point_light(Vec3(0, 0, 0), Vec3(1, 1, 1), 1.0f, 10.0f); + bool test2 = point_light.affects_point(Vec3(5, 0, 0)); + bool test3 = !point_light.affects_point(Vec3(20, 0, 0)); + + // Spot light + SpotLight spot_light(Vec3(0, 0, 0), Vec3(0, 0, -1), 30.0f, 45.0f); + bool test4 = spot_light.affects_point(Vec3(0, 0, -5)); + + report_test("Directional light affects all points", test1); + report_test("Point light range (inside)", test2); + report_test("Point light range (outside)", test3); + report_test("Spot light cone", test4); +} + +// Test 10: SceneManager operations +void test_scene_manager() { + SceneManager scene; + + // Add mesh + std::vector vertices = { + Vertex(Vec3(0, 0, 0)), + Vertex(Vec3(1, 0, 0)), + Vertex(Vec3(0, 1, 0)) + }; + std::vector indices = {0, 1, 2}; + Mesh mesh(vertices, indices); + + MeshHandle mesh_handle = scene.add_mesh(mesh); + bool test1 = mesh_handle != are_invalid_handle; + bool test2 = scene.get_mesh_count() == 1; + + // Add material + Material mat; + MaterialHandle mat_handle = scene.add_material(mat); + bool test3 = mat_handle != are_invalid_handle; + bool test4 = scene.get_material_count() == 1; + + // Add light + auto light = std::make_shared(); + LightHandle light_handle = scene.add_light(light); + bool test5 = light_handle != are_invalid_handle; + bool test6 = scene.get_light_count() == 1; + + // Test dirty flag + bool test7 = scene.is_dirty(); + scene.clear_dirty(); + bool test8 = !scene.is_dirty(); + + // Remove mesh + scene.remove_mesh(mesh_handle); + bool test9 = scene.get_mesh_count() == 0; + + report_test("SceneManager add mesh", test1); + report_test("SceneManager mesh count", test2); + report_test("SceneManager add material", test3); + report_test("SceneManager material count", test4); + report_test("SceneManager add light", test5); + report_test("SceneManager light count", test6); + report_test("SceneManager dirty flag (set)", test7); + report_test("SceneManager dirty flag (clear)", test8); + report_test("SceneManager remove mesh", test9); +} + +int main() { + // Initialize logger + Logger::init(LogLevel::ARE_LOG_INFO); + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Phase 2 Verification Program"); + ARE_LOG_INFO("========================================"); + + // Run all tests + test_vertex(); + test_aabb(); + test_triangle(); + test_ray_triangle_intersection(); + test_transform(); + test_camera(); + test_mesh(); + test_material(); + test_lights(); + test_scene_manager(); + + // Print summary + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Test Summary"); + ARE_LOG_INFO("========================================"); + + int passed = 0; + int failed = 0; + + for (const auto& result : test_results) { + if (result.passed) { + ++passed; + } else { + ++failed; + } + } + + ARE_LOG_INFO("Total tests: " + std::to_string(test_results.size())); + ARE_LOG_INFO("Passed: " + std::to_string(passed)); + ARE_LOG_INFO("Failed: " + std::to_string(failed)); + + if (failed == 0) { + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("✓ All Phase 2 tests passed!"); + ARE_LOG_INFO("========================================"); + } else { + ARE_LOG_ERROR("========================================"); + ARE_LOG_ERROR("✗ Some tests failed. Please review."); + ARE_LOG_ERROR("========================================"); + } + + Logger::shutdown(); + + return failed == 0 ? 0 : 1; +} diff --git a/examples/02_phase3_test/CMakeLists.txt b/examples/02_phase3_test/CMakeLists.txt new file mode 100644 index 0000000..775859e --- /dev/null +++ b/examples/02_phase3_test/CMakeLists.txt @@ -0,0 +1,18 @@ +# Phase 3 verification example + +add_are_example(phase3_test + main.cpp +) + +# Copy to bin directory for easy execution +set_target_properties(phase3_test PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) + +# Copy shaders to build directory +add_custom_command(TARGET phase3_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/shaders + ${CMAKE_BINARY_DIR}/bin/shaders + COMMENT "Copying shaders to build directory" +) diff --git a/examples/02_phase3_test/main.cpp b/examples/02_phase3_test/main.cpp new file mode 100644 index 0000000..9ed82ad --- /dev/null +++ b/examples/02_phase3_test/main.cpp @@ -0,0 +1,478 @@ +/** + * @file main.cpp + * @brief Phase 3 verification program - G-Buffer rendering test + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../lib/glad/glad/glad.h" +#include + +using namespace are; + +/** + * @brief Create a simple cube mesh + */ +Mesh create_cube_mesh() { + std::vector vertices = { + // Front face + Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 0)), + Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 0)), + Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 1)), + Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 1)), + + // Back face + Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 0)), + Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 0)), + Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 1)), + Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 1)), + + // Top face + Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(0, 0)), + Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(1, 0)), + Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(1, 1)), + Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(0, 1)), + + // Bottom face + Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(0, 0)), + Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(1, 0)), + Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(1, 1)), + Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(0, 1)), + + // Right face + Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 0)), + Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 0)), + Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 1)), + Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 1)), + + // Left face + Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 0)), + Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 0)), + Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 1)), + Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 1)) + }; + + std::vector indices = { + // Front + 0, 1, 2, 2, 3, 0, + // Back + 4, 5, 6, 6, 7, 4, + // Top + 8, 9, 10, 10, 11, 8, + // Bottom + 12, 13, 14, 14, 15, 12, + // Right + 16, 17, 18, 18, 19, 16, + // Left + 20, 21, 22, 22, 23, 20 + }; + + return Mesh(vertices, indices); +} + +/** + * @brief Create a simple triangle mesh (positioned in front of cube) + */ +Mesh create_triangle_mesh() { + std::vector vertices = { + Vertex(Vec3(-0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0, 0)), // Z = 1.0 + Vertex(Vec3( 0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(1, 0)), // Z = 1.0 + Vertex(Vec3( 0.0f, 0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0.5f, 1)) // Z = 1.0 + }; + + std::vector indices = {0, 1, 2}; + + return Mesh(vertices, indices); +} + +/** + * @brief Fullscreen quad shader for G-Buffer visualization + */ +const char *fullscreen_vert_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 *visualize_frag_source = R"( +#version 430 core +in vec2 v_texcoord; +out vec4 frag_color; + +uniform sampler2D u_texture; +uniform int u_mode; // 0=position, 1=normal, 2=albedo, 3=material + +void main() { + vec4 value = texture(u_texture, v_texcoord); + + if (u_mode == 0) { + // Position: normalize to [0,1] range for visualization + frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0); + } else if (u_mode == 1) { + // Normal: normalize to [0,1] range + frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0); + } else if (u_mode == 2) { + // Albedo: direct output + frag_color = vec4(value.rgb, 1.0); + } else if (u_mode == 3) { + // Material: roughness in R, AO in G + frag_color = vec4(value.r, value.g, 0.0, 1.0); + } else { + frag_color = value; + } +} +)"; + +/** + * @brief Create fullscreen quad VAO + */ +uint32_t create_fullscreen_quad() { + 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, 1.0f + }; + + uint32_t indices[] = { 0, 1, 2, 2, 3, 0 }; + + uint32_t vao, vbo, ebo; + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenBuffers(1, &ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); + + glBindVertexArray(0); + + return vao; +} + +int main() { + // Initialize logger + Logger::init(LogLevel::ARE_LOG_DEBUG); + Profiler::init(); + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Phase 3 Verification Program"); + ARE_LOG_INFO("G-Buffer Rendering Test"); + ARE_LOG_INFO("========================================"); + + // Create window configuration + WindowConfig window_config; + window_config.width = 800; + window_config.height = 600; + window_config.title = "Phase 3 - G-Buffer Test"; + window_config.vsync = true; + + // Create window + Window window(window_config); + + // Initialize OpenGL + if (!GLContext::initialize()) { + ARE_LOG_CRITICAL("Failed to initialize OpenGL context"); + return -1; + } + + GLContext::print_info(); + + // Get shader directory (relative to executable) + std::string shader_dir = "shaders/"; + + // Create rasterizer + int fb_width, fb_height; + window.get_framebuffer_size(fb_width, fb_height); + Rasterizer rasterizer(fb_width, fb_height); + + // Initialize shaders + // First, create G-Buffer shader manually for testing + ShaderProgram gbuffer_shader; + + // Try to load from file first + bool shader_loaded = false; + if (file_exists(shader_dir + "gbuffer/gbuffer.vert") && file_exists(shader_dir + "gbuffer/gbuffer.frag")) { + if (gbuffer_shader.load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert") && gbuffer_shader.load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag") && gbuffer_shader.link()) { + shader_loaded = true; + ARE_LOG_INFO("Loaded G-Buffer shaders from files"); + } + } + + // Fallback to embedded shaders + if (!shader_loaded) { + ARE_LOG_WARN("Shader files not found, using embedded shaders"); + + const char *gbuffer_vert = R"( +#version 430 core +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec3 a_normal; +layout(location = 2) in vec2 a_texcoord; +layout(location = 3) in vec3 a_tangent; + +uniform mat4 u_model; +uniform mat4 u_view; +uniform mat4 u_projection; +uniform mat3 u_normal_matrix; + +out vec3 v_world_position; +out vec3 v_world_normal; +out vec2 v_texcoord; +out vec3 v_world_tangent; + +void main() { + vec4 world_pos = u_model * vec4(a_position, 1.0); + v_world_position = world_pos.xyz; + v_world_normal = normalize(u_normal_matrix * a_normal); + v_world_tangent = normalize(u_normal_matrix * a_tangent); + v_texcoord = a_texcoord; + gl_Position = u_projection * u_view * world_pos; +} +)"; + + const char *gbuffer_frag = R"( +#version 430 core +in vec3 v_world_position; +in vec3 v_world_normal; +in vec2 v_texcoord; +in vec3 v_world_tangent; + +uniform vec3 u_albedo; +uniform float u_metallic; +uniform float u_roughness; + +layout(location = 0) out vec3 g_position; +layout(location = 1) out vec3 g_normal; +layout(location = 2) out vec4 g_albedo_metallic; +layout(location = 3) out vec2 g_roughness_ao; + +void main() { + g_position = v_world_position; + g_normal = normalize(v_world_normal); + g_albedo_metallic = vec4(u_albedo, u_metallic); + g_roughness_ao = vec2(u_roughness, 1.0); +} +)"; + + if (!gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, gbuffer_vert) || !gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, gbuffer_frag) || !gbuffer_shader.link()) { + ARE_LOG_CRITICAL("Failed to compile embedded G-Buffer shaders"); + return -1; + } + } + + // Create visualization shader + ShaderProgram vis_shader; + if (!vis_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, fullscreen_vert_source) || !vis_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, visualize_frag_source) || !vis_shader.link()) { + ARE_LOG_CRITICAL("Failed to compile visualization shaders"); + return -1; + } + + // Create fullscreen quad + uint32_t fullscreen_quad_vao = create_fullscreen_quad(); + + // Create scene + SceneManager scene; + + // Create materials + Material red_material; + red_material.set_albedo(Vec3(0.8f, 0.2f, 0.2f)); + red_material.set_metallic(0.0f); + red_material.set_roughness(0.5f); + MaterialHandle red_mat_handle = scene.add_material(red_material); + + Material green_material; + green_material.set_albedo(Vec3(0.2f, 0.8f, 0.2f)); + green_material.set_metallic(0.5f); + green_material.set_roughness(0.3f); + MaterialHandle green_mat_handle = scene.add_material(green_material); + + // Create meshes + Mesh cube = create_cube_mesh(); + cube.set_material(red_mat_handle); + cube.compute_tangents(); + + Mesh triangle = create_triangle_mesh(); + triangle.set_material(green_mat_handle); + triangle.compute_tangents(); + + // Upload meshes to GPU + rasterizer.upload_mesh(cube); + rasterizer.upload_mesh(triangle); + + // Add meshes to scene + scene.add_mesh(cube); + scene.add_mesh(triangle); + + // Create camera + Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, static_cast(fb_width) / fb_height, 0.1f, 100.0f); + + // Visualization mode (0=position, 1=normal, 2=albedo, 3=material) + int vis_mode = 2; // Start with albedo + + ARE_LOG_INFO("Controls:"); + ARE_LOG_INFO(" 1 - View Position buffer"); + ARE_LOG_INFO(" 2 - View Normal buffer"); + ARE_LOG_INFO(" 3 - View Albedo buffer"); + ARE_LOG_INFO(" 4 - View Material buffer (Roughness/AO)"); + ARE_LOG_INFO(" ESC - Exit"); + + // Main loop + float time = 0.0f; + while (!window.should_close()) { + ARE_PROFILE_SCOPE("Frame"); + + // Poll events + window.poll_events(); + + // Handle input + if (window.is_key_pressed(256)) { // ESC + window.set_should_close(true); + } + if (window.is_key_pressed(49)) + vis_mode = 0; // 1 - Position + if (window.is_key_pressed(50)) + vis_mode = 1; // 2 - Normal + if (window.is_key_pressed(51)) + vis_mode = 2; // 3 - Albedo + if (window.is_key_pressed(52)) + vis_mode = 3; // 4 - Material + + // Update camera position (orbit around origin) + time += 0.016f; + float cam_x = std::sin(time * 0.5f) * 3.0f; + float cam_z = std::cos(time * 0.5f) * 3.0f; + camera.set_position(Vec3(cam_x, 1.5f, cam_z)); + camera.set_target(Vec3(0, 0, 0)); + + // Render to G-Buffer + { + ARE_PROFILE_SCOPE("Render G-Buffer"); + + // Manually render since we're using our own shader + GBuffer &gbuffer = rasterizer.get_gbuffer(); + gbuffer.bind(); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + gbuffer_shader.use(); + gbuffer_shader.set_uniform("u_view", camera.get_view_matrix()); + gbuffer_shader.set_uniform("u_projection", camera.get_projection_matrix()); + + // Render all meshes + const auto &meshes = scene.get_all_meshes(); + const auto &materials = scene.get_all_materials(); + + for (const auto &mesh : meshes) { + if (!mesh.has_gpu_resources()) + continue; + + Mat4 model = Mat4(1.0f); + gbuffer_shader.set_uniform("u_model", model); + + Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model))); + gbuffer_shader.set_uniform("u_normal_matrix", normal_matrix); + + MaterialHandle mat_handle = mesh.get_material(); + if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) { + const Material &mat = materials[mat_handle - 1]; + gbuffer_shader.set_uniform("u_albedo", mat.get_albedo()); + gbuffer_shader.set_uniform("u_metallic", mat.get_metallic()); + gbuffer_shader.set_uniform("u_roughness", mat.get_roughness()); + } else { + gbuffer_shader.set_uniform("u_albedo", Vec3(0.8f)); + gbuffer_shader.set_uniform("u_metallic", 0.0f); + gbuffer_shader.set_uniform("u_roughness", 0.5f); + } + + glBindVertexArray(mesh.get_vao()); + glDrawElements(GL_TRIANGLES, + static_cast(mesh.get_index_count()), + GL_UNSIGNED_INT, + nullptr); + } + + glBindVertexArray(0); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + gbuffer.unbind(); + } + + // Visualize G-Buffer + { + ARE_PROFILE_SCOPE("Visualize G-Buffer"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, fb_width, fb_height); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + vis_shader.use(); + vis_shader.set_uniform("u_mode", vis_mode); + vis_shader.set_uniform("u_texture", 0); + + // Bind appropriate G-Buffer texture + GBuffer &gbuffer = rasterizer.get_gbuffer(); + gbuffer.bind_texture(vis_mode, 0); + + glBindVertexArray(fullscreen_quad_vao); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); + glBindVertexArray(0); + } + + // Swap buffers + window.swap_buffers(); + } + + // Cleanup + glDeleteVertexArrays(1, &fullscreen_quad_vao); + + // Print profiling results + Profiler::print_results(); + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Phase 3 test completed successfully!"); + ARE_LOG_INFO("========================================"); + + Profiler::shutdown(); + Logger::shutdown(); + + return 0; +} diff --git a/examples/02_visual_test/CMakeLists.txt b/examples/02_visual_test/CMakeLists.txt new file mode 100644 index 0000000..36b99be --- /dev/null +++ b/examples/02_visual_test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Phase 2 visual verification example + +add_are_example(visual_test + main.cpp +) + +# Copy to bin directory +set_target_properties(visual_test PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) diff --git a/examples/02_visual_test/main.cpp b/examples/02_visual_test/main.cpp new file mode 100644 index 0000000..0ed0014 --- /dev/null +++ b/examples/02_visual_test/main.cpp @@ -0,0 +1,329 @@ +/** + * @file main.cpp + * @brief Visual verification using software rasterization + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "../lib/stb/stb_image_write.h" + +#include +#include +#include + +using namespace are; + +// Simple framebuffer +struct Framebuffer { + int width; + int height; + std::vector pixels; // RGB format + + Framebuffer(int w, int h) : width(w), height(h) { + pixels.resize(w * h * 3, 0); + } + + void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) { + if (x < 0 || x >= width || y < 0 || y >= height) + return; + int index = (y * width + x) * 3; + pixels[index + 0] = r; + pixels[index + 1] = g; + pixels[index + 2] = b; + } + + void set_pixel(int x, int y, const Vec3 &color) { + uint8_t r = static_cast(std::min(color.x * 255.0f, 255.0f)); + uint8_t g = static_cast(std::min(color.y * 255.0f, 255.0f)); + uint8_t b = static_cast(std::min(color.z * 255.0f, 255.0f)); + set_pixel(x, y, r, g, b); + } + + bool save(const std::string &filename) { + return stbi_write_png(filename.c_str(), width, height, 3, + pixels.data(), width * 3) + != 0; + } +}; + +// Simple shading function +Vec3 shade_hit(const HitRecord &hit, const Vec3 &light_dir) { + // Lambertian shading + float ndotl = std::max(0.0f, glm::dot(hit.normal_, light_dir)); + + // Base color based on normal (for visualization) + Vec3 base_color = (hit.normal_ + Vec3(1.0f)) * 0.5f; + + // Apply lighting + Vec3 ambient = base_color * 0.2f; + Vec3 diffuse = base_color * ndotl * 0.8f; + + return ambient + diffuse; +} + +// Render a single triangle +void render_triangle(Framebuffer &fb, const Triangle &tri, Camera &camera) { + Vec3 light_dir = glm::normalize(Vec3(0.5f, 1.0f, 0.5f)); + + for (int y = 0; y < fb.height; ++y) { + for (int x = 0; x < fb.width; ++x) { + // Generate ray + float u = (x + 0.5f) / fb.width; + float v = (y + 0.5f) / fb.height; + + Vec3 origin, direction; + camera.generate_ray(u, v, origin, direction); + Ray ray(origin, direction); + + // Test intersection + HitRecord hit; + if (tri.intersect(ray, hit)) { + Vec3 color = shade_hit(hit, light_dir); + fb.set_pixel(x, y, color); + } else { + // Background gradient + Vec3 bg_color = Vec3(0.5f, 0.7f, 1.0f) * (1.0f - v) + Vec3(1.0f, 1.0f, 1.0f) * v; + fb.set_pixel(x, y, bg_color); + } + } + } +} + +// Render multiple triangles (mesh) +void render_mesh(Framebuffer &fb, const Mesh &mesh, Camera &camera) { + Vec3 light_dir = glm::normalize(Vec3(0.5f, 1.0f, 0.5f)); + + for (int y = 0; y < fb.height; ++y) { + for (int x = 0; x < fb.width; ++x) { + // Generate ray + float u = (x + 0.5f) / fb.width; + float v = (y + 0.5f) / fb.height; + + Vec3 origin, direction; + camera.generate_ray(u, v, origin, direction); + Ray ray(origin, direction); + + // Test intersection with all triangles + HitRecord closest_hit; + closest_hit.t_ = ray.t_max_; + bool hit_any = false; + + for (size_t i = 0; i < mesh.get_triangle_count(); ++i) { + Vertex v0, v1, v2; + if (mesh.get_triangle(i, v0, v1, v2)) { + Triangle tri(v0, v1, v2); + HitRecord hit; + if (tri.intersect(ray, hit) && hit.t_ < closest_hit.t_) { + closest_hit = hit; + hit_any = true; + } + } + } + + if (hit_any) { + Vec3 color = shade_hit(closest_hit, light_dir); + fb.set_pixel(x, y, color); + } else { + // Background gradient + Vec3 bg_color = Vec3(0.5f, 0.7f, 1.0f) * (1.0f - v) + Vec3(1.0f, 1.0f, 1.0f) * v; + fb.set_pixel(x, y, bg_color); + } + } + } +} + +int main() { + Logger::init(LogLevel::ARE_LOG_INFO); + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Phase 2 Visual Verification"); + ARE_LOG_INFO("========================================"); + + const int width = 800; + const int height = 600; + + // Test 1: Single triangle + { + ARE_LOG_INFO("Rendering single triangle..."); + + Framebuffer fb(width, height); + + // Create triangle + Vertex v0(Vec3(-1, -1, 0), Vec3(0, 0, 1)); + Vertex v1(Vec3(1, -1, 0), Vec3(0, 0, 1)); + Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1)); + Triangle tri(v0, v1, v2); + + // Setup camera + Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f); + + // Render + render_triangle(fb, tri, camera); + + // Save + if (fb.save("output_triangle.png")) { + ARE_LOG_INFO("✓ Saved: output_triangle.png"); + } else { + ARE_LOG_ERROR("✗ Failed to save output_triangle.png"); + } + } + + // Test 2: Colored triangle (using normals) + { + ARE_LOG_INFO("Rendering colored triangle..."); + + Framebuffer fb(width, height); + + // Create triangle with different normals for each vertex + Vertex v0(Vec3(-1, -1, 0), Vec3(1, 0, 0)); // Red + Vertex v1(Vec3(1, -1, 0), Vec3(0, 1, 0)); // Green + Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1)); // Blue + Triangle tri(v0, v1, v2); + + Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f); + + render_triangle(fb, tri, camera); + + if (fb.save("output_colored_triangle.png")) { + ARE_LOG_INFO("✓ Saved: output_colored_triangle.png"); + } else { + ARE_LOG_ERROR("✗ Failed to save output_colored_triangle.png"); + } + } + + // Test 3: Cube (mesh with multiple triangles) + { + ARE_LOG_INFO("Rendering cube..."); + + Framebuffer fb(width, height); + + // Create cube vertices + std::vector vertices = { + // Front face + Vertex(Vec3(-1, -1, 1), Vec3(0, 0, 1)), + Vertex(Vec3(1, -1, 1), Vec3(0, 0, 1)), + Vertex(Vec3(1, 1, 1), Vec3(0, 0, 1)), + Vertex(Vec3(-1, 1, 1), Vec3(0, 0, 1)), + // Back face + Vertex(Vec3(-1, -1, -1), Vec3(0, 0, -1)), + Vertex(Vec3(1, -1, -1), Vec3(0, 0, -1)), + Vertex(Vec3(1, 1, -1), Vec3(0, 0, -1)), + Vertex(Vec3(-1, 1, -1), Vec3(0, 0, -1)), + }; + + // Create cube indices + std::vector indices = { + // Front + 0, 1, 2, 2, 3, 0, + // Right + 1, 5, 6, 6, 2, 1, + // Back + 5, 4, 7, 7, 6, 5, + // Left + 4, 0, 3, 3, 7, 4, + // Top + 3, 2, 6, 6, 7, 3, + // Bottom + 4, 5, 1, 1, 0, 4 + }; + + Mesh cube(vertices, indices); + + // Setup camera (slightly angled view) + Camera camera(Vec3(3, 2, 4), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f); + + // Render + render_mesh(fb, cube, camera); + + if (fb.save("output_cube.png")) { + ARE_LOG_INFO("✓ Saved: output_cube.png"); + } else { + ARE_LOG_ERROR("✗ Failed to save output_cube.png"); + } + } + + // Test 4: Cornell Box (corrected) + { + ARE_LOG_INFO("Rendering Cornell Box..."); + + Framebuffer fb(width, height); + + std::vector vertices; + std::vector indices; + + // Helper function to add a quad + auto add_quad = [&](const Vec3 &v0, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, const Vec3 &normal) { + unsigned int base = vertices.size(); + vertices.push_back(Vertex(v0, normal)); + vertices.push_back(Vertex(v1, normal)); + vertices.push_back(Vertex(v2, normal)); + vertices.push_back(Vertex(v3, normal)); + indices.insert(indices.end(), { base + 0, base + 1, base + 2, base + 2, base + 3, base + 0 }); + }; + + // Floor (white) + add_quad( + Vec3(-2, -2, 2), Vec3(2, -2, 2), + Vec3(2, -2, -2), Vec3(-2, -2, -2), + Vec3(0, 1, 0)); + + // Ceiling (white) + add_quad( + Vec3(-2, 2, -2), Vec3(2, 2, -2), + Vec3(2, 2, 2), Vec3(-2, 2, 2), + Vec3(0, -1, 0)); + + // Back wall (white) + add_quad( + Vec3(-2, -2, -2), Vec3(2, -2, -2), + Vec3(2, 2, -2), Vec3(-2, 2, -2), + Vec3(0, 0, 1)); + + // Left wall (red) + add_quad( + Vec3(-2, -2, 2), Vec3(-2, -2, -2), + Vec3(-2, 2, -2), Vec3(-2, 2, 2), + Vec3(1, 0, 0)); + + // Right wall (green) + add_quad( + Vec3(2, -2, -2), Vec3(2, -2, 2), + Vec3(2, 2, 2), Vec3(2, 2, -2), + Vec3(-1, 0, 0)); + + Mesh cornell_box(vertices, indices); + + Camera camera(Vec3(0, 0, 5), Vec3(0, 0, 0)); + camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f); + + render_mesh(fb, cornell_box, camera); + + if (fb.save("output_cornell_box.png")) { + ARE_LOG_INFO("✓ Saved: output_cornell_box.png"); + } + } + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("✓ All images generated successfully!"); + ARE_LOG_INFO("Check the following files:"); + ARE_LOG_INFO(" - output_triangle.png"); + ARE_LOG_INFO(" - output_colored_triangle.png"); + ARE_LOG_INFO(" - output_cube.png"); + ARE_LOG_INFO(" - output_cornell_box.png"); + ARE_LOG_INFO("========================================"); + + Logger::shutdown(); + return 0; +} diff --git a/examples/03_phase4_test/CMakeLists.txt b/examples/03_phase4_test/CMakeLists.txt new file mode 100644 index 0000000..cf7772b --- /dev/null +++ b/examples/03_phase4_test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Phase 4 verification example + +add_are_example(phase4_test + main.cpp +) + +# Copy to bin directory for easy execution +set_target_properties(phase4_test PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +) diff --git a/examples/03_phase4_test/main.cpp b/examples/03_phase4_test/main.cpp new file mode 100644 index 0000000..04ce684 --- /dev/null +++ b/examples/03_phase4_test/main.cpp @@ -0,0 +1,410 @@ +/** + * @file main.cpp + * @brief Phase 4 verification program - BVH construction and traversal test + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace are; + +// Test result tracking +struct TestResult { + std::string name; + bool passed; + std::string message; +}; + +std::vector test_results; + +void report_test(const std::string &name, bool passed, const std::string &message = "") { + test_results.push_back({ name, passed, message }); + if (passed) { + ARE_LOG_INFO("✓ " + name); + } else { + ARE_LOG_ERROR("✗ " + name + ": " + message); + } +} + +/** + * @brief Create a simple scene with a few triangles + */ +std::vector create_simple_scene() { + std::vector triangles; + + // Ground plane (2 triangles) + Vertex v0(Vec3(-5, 0, -5), Vec3(0, 1, 0)); + Vertex v1(Vec3(5, 0, -5), Vec3(0, 1, 0)); + Vertex v2(Vec3(5, 0, 5), Vec3(0, 1, 0)); + Vertex v3(Vec3(-5, 0, 5), Vec3(0, 1, 0)); + + triangles.emplace_back(v0, v1, v2); + triangles.emplace_back(v0, v2, v3); + + // Cube (12 triangles) + Vec3 cube_min(-1, 1, -1); + Vec3 cube_max(1, 3, 1); + + // Front face + triangles.emplace_back( + Vertex(Vec3(cube_min.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)), + Vertex(Vec3(cube_max.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)), + Vertex(Vec3(cube_max.x, cube_max.y, cube_max.z), Vec3(0, 0, 1))); + triangles.emplace_back( + Vertex(Vec3(cube_min.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)), + Vertex(Vec3(cube_max.x, cube_max.y, cube_max.z), Vec3(0, 0, 1)), + Vertex(Vec3(cube_min.x, cube_max.y, cube_max.z), Vec3(0, 0, 1))); + + // Add more faces... (simplified for brevity) + + return triangles; +} + +/** + * @brief Create a complex scene with many triangles + */ +std::vector create_complex_scene(int num_triangles) { + std::vector triangles; + triangles.reserve(num_triangles); + + std::mt19937 rng(42); + std::uniform_real_distribution dist(-10.0f, 10.0f); + + for (int i = 0; i < num_triangles; ++i) { + Vec3 p0(dist(rng), dist(rng), dist(rng)); + Vec3 p1 = p0 + Vec3(dist(rng) * 0.5f, dist(rng) * 0.5f, dist(rng) * 0.5f); + Vec3 p2 = p0 + Vec3(dist(rng) * 0.5f, dist(rng) * 0.5f, dist(rng) * 0.5f); + + Vec3 normal = glm::normalize(glm::cross(p1 - p0, p2 - p0)); + + triangles.emplace_back( + Vertex(p0, normal), + Vertex(p1, normal), + Vertex(p2, normal)); + } + + return triangles; +} + +/** + * @brief Test 1: BVH construction + */ +void test_bvh_construction() { + auto triangles = create_simple_scene(); + + BVH bvh; + BVHBuildConfig config; + config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_MIDDLE; + config.max_leaf_size_ = 4; + + bool success = bvh.build(triangles, config); + + report_test("BVH construction (simple scene)", success); + report_test("BVH is built", bvh.is_built()); + report_test("BVH has nodes", !bvh.get_nodes().empty()); +} + +/** + * @brief Test 2: BVH construction with SAH + */ +void test_bvh_construction_sah() { + auto triangles = create_simple_scene(); + + BVH bvh; + BVHBuildConfig config; + config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH; + config.max_leaf_size_ = 2; + + bool success = bvh.build(triangles, config); + + report_test("BVH construction with SAH", success); +} + +/** + * @brief Test 3: Ray-BVH intersection + */ +void test_ray_bvh_intersection() { + auto triangles = create_simple_scene(); + + BVH bvh; + bvh.build(triangles); + + // Ray hitting the ground plane + Ray ray1(Vec3(0, 5, 0), Vec3(0, -1, 0)); + HitRecord hit1; + bool test1 = bvh.intersect(ray1, hit1); + + // Ray missing everything + Ray ray2(Vec3(100, 5, 100), Vec3(0, -1, 0)); + HitRecord hit2; + bool test2 = !bvh.intersect(ray2, hit2); + + report_test("Ray-BVH intersection (hit)", test1); + report_test("Ray-BVH intersection (miss)", test2); +} + +/** + * @brief Test 4: BVH occlusion test + */ +void test_bvh_occlusion() { + auto triangles = create_simple_scene(); + + BVH bvh; + bvh.build(triangles); + + // Ray with occlusion + Ray ray1(Vec3(0, 5, 0), Vec3(0, -1, 0)); + bool test1 = bvh.intersect_any(ray1, 10.0f); + + // Ray without occlusion + Ray ray2(Vec3(100, 5, 100), Vec3(0, -1, 0)); + bool test2 = !bvh.intersect_any(ray2, 10.0f); + + report_test("BVH occlusion test (occluded)", test1); + report_test("BVH occlusion test (not occluded)", test2); +} + +/** + * @brief Test 5: BVH performance with complex scene + */ +void test_bvh_performance() { + const int num_triangles = 10000; + auto triangles = create_complex_scene(num_triangles); + + ARE_LOG_INFO("Building BVH for " + std::to_string(num_triangles) + " triangles..."); + + BVH bvh; + BVHBuildConfig config; + config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH; + + auto start_build = std::chrono::high_resolution_clock::now(); + bool success = bvh.build(triangles, config); + auto end_build = std::chrono::high_resolution_clock::now(); + + double build_time = std::chrono::duration(end_build - start_build).count(); + + ARE_LOG_INFO("BVH build time: " + std::to_string(build_time) + " ms"); + + // Test ray tracing performance + const int num_rays = 10000; + std::mt19937 rng(42); + std::uniform_real_distribution dist(-10.0f, 10.0f); + + int hit_count = 0; + + auto start_trace = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < num_rays; ++i) { + Vec3 origin(dist(rng), dist(rng), dist(rng)); + Vec3 direction = glm::normalize(Vec3(dist(rng), dist(rng), dist(rng))); + + Ray ray(origin, direction); + HitRecord hit; + + if (bvh.intersect(ray, hit)) { + hit_count++; + } + } + + auto end_trace = std::chrono::high_resolution_clock::now(); + double trace_time = std::chrono::duration(end_trace - start_trace).count(); + + ARE_LOG_INFO("Ray tracing time: " + std::to_string(trace_time) + " ms for " + std::to_string(num_rays) + " rays"); + ARE_LOG_INFO("Hit rate: " + std::to_string(hit_count) + "/" + std::to_string(num_rays) + " (" + std::to_string(100.0 * hit_count / num_rays) + "%)"); + ARE_LOG_INFO("Average time per ray: " + std::to_string(trace_time / num_rays) + " ms"); + + report_test("BVH performance test", success && build_time < 5000.0); // Should build in < 5 seconds +} + +/** + * @brief Test 6: BVH memory usage + */ +void test_bvh_memory() { + auto triangles = create_complex_scene(1000); + + BVH bvh; + bvh.build(triangles); + + size_t memory = bvh.get_memory_usage(); + ARE_LOG_INFO("BVH memory usage: " + std::to_string(memory / 1024) + " KB"); + + report_test("BVH memory usage", memory > 0); +} + +/** + * @brief Test 7: BVH clear and rebuild + */ +void test_bvh_clear_rebuild() { + auto triangles = create_simple_scene(); + + BVH bvh; + bvh.build(triangles); + + bool test1 = bvh.is_built(); + + bvh.clear(); + bool test2 = !bvh.is_built(); + + bvh.build(triangles); + bool test3 = bvh.is_built(); + + report_test("BVH clear and rebuild (initial build)", test1); + report_test("BVH clear and rebuild (after clear)", test2); + report_test("BVH clear and rebuild (rebuild)", test3); +} + +/** + * @brief Test 8: BVH with empty scene + */ +void test_bvh_empty_scene() { + std::vector empty_triangles; + + BVH bvh; + bool success = bvh.build(empty_triangles); + + report_test("BVH with empty scene", !success); +} + +/** + * @brief Test 9: BVH node structure + */ +void test_bvh_node_structure() { + auto triangles = create_simple_scene(); + + BVH bvh; + bvh.build(triangles); + + const auto &nodes = bvh.get_nodes(); + + bool test1 = !nodes.empty(); + + // Check root node + bool test2 = nodes[0].bounds_.is_valid(); + + // Count leaf and internal nodes + int leaf_count = 0; + int internal_count = 0; + + for (const auto &node : nodes) { + if (node.is_leaf()) { + leaf_count++; + } else { + internal_count++; + } + } + + bool test3 = leaf_count > 0; + bool test4 = internal_count >= 0; + + ARE_LOG_INFO("BVH structure: " + std::to_string(nodes.size()) + " nodes (" + std::to_string(leaf_count) + " leaves, " + std::to_string(internal_count) + " internal)"); + + report_test("BVH node structure (has nodes)", test1); + report_test("BVH node structure (valid root)", test2); + report_test("BVH node structure (has leaves)", test3); + report_test("BVH node structure (node counts)", test4); +} + +/** + * @brief Test 10: BVH traversal correctness + */ +void test_bvh_traversal_correctness() { + // Create a simple scene with known geometry + std::vector triangles; + + // Single triangle at origin + Vertex v0(Vec3(-1, 0, -1), Vec3(0, 1, 0)); + Vertex v1(Vec3(1, 0, -1), Vec3(0, 1, 0)); + Vertex v2(Vec3(0, 0, 1), Vec3(0, 1, 0)); + + triangles.emplace_back(v0, v1, v2); + + BVH bvh; + bvh.build(triangles); + + // Ray hitting the triangle from above + Ray ray(Vec3(0, 5, 0), Vec3(0, -1, 0)); + HitRecord hit; + + bool intersected = bvh.intersect(ray, hit); + + bool test1 = intersected; + bool test2 = hit.t_ > 0.0f && hit.t_ < 10.0f; + bool test3 = glm::length(hit.normal_ - Vec3(0, 1, 0)) < 0.01f; + + report_test("BVH traversal correctness (intersection)", test1); + report_test("BVH traversal correctness (t value)", test2); + report_test("BVH traversal correctness (normal)", test3); +} + +int main() { + // Initialize logger and profiler + Logger::init(LogLevel::ARE_LOG_INFO); + Profiler::init(); + + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Phase 4 Verification Program"); + ARE_LOG_INFO("BVH Construction and Traversal Test"); + ARE_LOG_INFO("========================================"); + + // Run all tests + test_bvh_construction(); + test_bvh_construction_sah(); + test_ray_bvh_intersection(); + test_bvh_occlusion(); + test_bvh_performance(); + test_bvh_memory(); + test_bvh_clear_rebuild(); + test_bvh_empty_scene(); + test_bvh_node_structure(); + test_bvh_traversal_correctness(); + + // Print summary + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("Test Summary"); + ARE_LOG_INFO("========================================"); + + int passed = 0; + int failed = 0; + + for (const auto &result : test_results) { + if (result.passed) { + ++passed; + } else { + ++failed; + } + } + + ARE_LOG_INFO("Total tests: " + std::to_string(test_results.size())); + ARE_LOG_INFO("Passed: " + std::to_string(passed)); + ARE_LOG_INFO("Failed: " + std::to_string(failed)); + + if (failed == 0) { + ARE_LOG_INFO("========================================"); + ARE_LOG_INFO("✓ All Phase 4 tests passed!"); + ARE_LOG_INFO("========================================"); + } else { + ARE_LOG_ERROR("========================================"); + ARE_LOG_ERROR("✗ Some tests failed. Please review."); + ARE_LOG_ERROR("========================================"); + } + + // Print profiling results + Profiler::print_results(); + + Profiler::shutdown(); + Logger::shutdown(); + + return failed == 0 ? 0 : 1; +} diff --git a/files.md b/files.md index 926b982..aca15ab 100644 --- a/files.md +++ b/files.md @@ -79,24 +79,1855 @@ enum class GBufferVisualizationMode { ### 文件:include/are/core/logger.h ```cpp +/** + * @file logger.h + * @brief Logging system for the rendering engine + */ + +#ifndef ARE_INCLUDE_CORE_LOGGER_H +#define ARE_INCLUDE_CORE_LOGGER_H + +#include +#include + +namespace are { + +/** + * @enum LogLevel + * @brief Logging severity levels + */ +enum class LogLevel { + ARE_LOG_TRACE, + ARE_LOG_DEBUG, + ARE_LOG_INFO, + ARE_LOG_WARN, + ARE_LOG_ERROR, + ARE_LOG_CRITICAL +}; + +/** + * @class Logger + * @brief Thread-safe logging system + * + * This class provides a simple interface for logging messages with different + * severity levels. It wraps spdlog for actual logging functionality. + */ +class Logger { +public: + /** + * @brief Initialize the logging system + * @param min_level Minimum log level to display + */ + static void init(LogLevel min_level = LogLevel::ARE_LOG_INFO); + + /** + * @brief Shutdown the logging system + */ + static void shutdown(); + + /** + * @brief Log a message with file/function/line information + * @param level Log severity level + * @param file Source file name + * @param func Function name + * @param line Line number + * @param message Log message + */ + static void log(LogLevel level, const char* file, const char* func, + int line, const std::string& message); + + /** + * @brief Set minimum log level + * @param level Minimum log level to display + */ + static void set_level(LogLevel level); + +private: + static std::shared_ptr logger_impl_; ///< Internal logger implementation + static bool initialized_; ///< Initialization flag +}; + +} // namespace are + +// Logging macros +#define ARE_LOG_TRACE(msg) are::Logger::log(are::LogLevel::ARE_LOG_TRACE, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_DEBUG(msg) are::Logger::log(are::LogLevel::ARE_LOG_DEBUG, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_INFO(msg) are::Logger::log(are::LogLevel::ARE_LOG_INFO, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_WARN(msg) are::Logger::log(are::LogLevel::ARE_LOG_WARN, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_ERROR(msg) are::Logger::log(are::LogLevel::ARE_LOG_ERROR, __FILE__, __func__, __LINE__, msg) +#define ARE_LOG_CRITICAL(msg) are::Logger::log(are::LogLevel::ARE_LOG_CRITICAL, __FILE__, __func__, __LINE__, msg) + +#endif // ARE_INCLUDE_CORE_LOGGER_H ``` ### 文件:include/are/core/config.h ```cpp +/** + * @file config.h + * @brief Configuration system for the rendering engine + */ + +#ifndef ARE_INCLUDE_CORE_CONFIG_H +#define ARE_INCLUDE_CORE_CONFIG_H + +#include +#include + +namespace are { + +/** + * @struct WindowConfig + * @brief Configuration for window creation + */ +struct WindowConfig { + int width = 1280; ///< Window width in pixels + int height = 720; ///< Window height in pixels + std::string title = "Aurora Rendering Engine"; ///< Window title + bool resizable = true; ///< Whether window is resizable + bool vsync = true; ///< Enable vertical synchronization + int samples = 1; ///< MSAA samples (1 = disabled) +}; + +/** + * @struct RayTracingConfig + * @brief Configuration for ray tracing + */ +struct RayTracingConfig { + RayTracingBackend backend = RayTracingBackend::ARE_RT_BACKEND_COMPUTE_SHADER; + int spp = 64; ///< Samples per pixel + int max_depth = 8; ///< Maximum ray bounce depth + bool enable_gi = true; ///< Enable global illumination + bool enable_ao = true; ///< Enable ambient occlusion + bool enable_soft_shadows = false; ///< Enable soft shadows + int ao_samples = 16; ///< AO sample count + Real ao_radius = 1.0f; ///< AO sampling radius +}; + +/** + * @struct RenderConfig + * @brief General rendering configuration + */ +struct RenderConfig { + ToneMappingOperator tonemap_op = ToneMappingOperator::ARE_TONEMAP_ACES; + Real exposure = 1.0f; ///< Exposure value for tone mapping + bool use_hdr = true; ///< Use HDR rendering pipeline + GBufferVisualizationMode gbuffer_vis_mode = GBufferVisualizationMode::ARE_GBUFFER_VIS_NONE; +}; + +/** + * @struct PerformanceConfig + * @brief Performance-related configuration + */ +struct PerformanceConfig { + int num_threads = 0; ///< Number of threads (0 = auto-detect) + bool enable_bvh_multithreading = true; ///< Use multithreading for BVH construction + bool enable_profiling = false; ///< Enable performance profiling +}; + +/** + * @struct PathConfig + * @brief File path configuration + */ +struct PathConfig { + std::string shader_dir = "shaders/"; ///< Directory containing shader files + std::string texture_dir = "textures/"; ///< Default texture directory + std::string output_dir = "output/"; ///< Default output directory +}; + +/** + * @class AreConfig + * @brief Main configuration class for Aurora Rendering Engine + */ +class AreConfig { +public: + WindowConfig window; ///< Window configuration + RayTracingConfig ray_tracing; ///< Ray tracing configuration + RenderConfig render; ///< Rendering configuration + PerformanceConfig performance; ///< Performance configuration + PathConfig paths; ///< Path configuration + + /** + * @brief Constructor with default values + */ + AreConfig() = default; + + /** + * @brief Validate configuration values + * @return true if configuration is valid, false otherwise + */ + bool validate() const; + + /** + * @brief Print configuration to console + */ + void print() const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_CORE_CONFIG_H ``` ### 文件:include/are/utils/math_utils.h ```cpp +/** + * @file math_utils.h + * @brief Mathematical utility functions + */ + +#ifndef ARE_INCLUDE_UTILS_MATH_UTILS_H +#define ARE_INCLUDE_UTILS_MATH_UTILS_H + +#include +#include + +namespace are { + +/** + * @brief Clamp value to range [min, max] + * @param value Value to clamp + * @param min Minimum value + * @param max Maximum value + * @return Clamped value + */ +template +inline T clamp(T value, T min, T max) { + return std::max(min, std::min(value, max)); +} + +/** + * @brief Linear interpolation + * @param a Start value + * @param b End value + * @param t Interpolation factor [0, 1] + * @return Interpolated value + */ +template +inline T lerp(T a, T b, Real t) { + return a + (b - a) * t; +} + +/** + * @brief Convert degrees to radians + * @param degrees Angle in degrees + * @return Angle in radians + */ +inline Real degrees_to_radians(Real degrees) { + return degrees * are_pi / 180.0f; +} + +/** + * @brief Convert radians to degrees + * @param radians Angle in radians + * @return Angle in degrees + */ +inline Real radians_to_degrees(Real radians) { + return radians * 180.0f / are_pi; +} + +/** + * @brief Check if two floating point values are approximately equal + * @param a First value + * @param b Second value + * @param epsilon Tolerance + * @return true if approximately equal + */ +inline bool approx_equal(Real a, Real b, Real epsilon = are_epsilon) { + return std::abs(a - b) < epsilon; +} + +/** + * @brief Compute barycentric coordinates + * @param p Point + * @param a Triangle vertex A + * @param b Triangle vertex B + * @param c Triangle vertex C + * @param u Output barycentric coordinate u + * @param v Output barycentric coordinate v + * @param w Output barycentric coordinate w + */ +void compute_barycentric(const Vec3& p, const Vec3& a, const Vec3& b, const Vec3& c, + Real& u, Real& v, Real& w); + +/** + * @brief Reflect vector around normal + * @param incident Incident vector + * @param normal Surface normal (normalized) + * @return Reflected vector + */ +Vec3 reflect(const Vec3& incident, const Vec3& normal); + +/** + * @brief Refract vector through surface + * @param incident Incident vector (normalized) + * @param normal Surface normal (normalized) + * @param eta Ratio of refractive indices + * @param refracted Output refracted vector + * @return true if refraction occurred (false for total internal reflection) + */ +bool refract(const Vec3& incident, const Vec3& normal, Real eta, Vec3& refracted); + +/** + * @brief Compute Fresnel reflectance (Schlick approximation) + * @param cos_theta Cosine of angle between view and normal + * @param f0 Reflectance at normal incidence + * @return Fresnel reflectance + */ +Real fresnel_schlick(Real cos_theta, Real f0); + +/** + * @brief Create orthonormal basis from normal + * @param normal Normal vector (normalized) + * @param tangent Output tangent vector + * @param bitangent Output bitangent vector + */ +void create_orthonormal_basis(const Vec3& normal, Vec3& tangent, Vec3& bitangent); + +/** + * @brief Transform direction from tangent space to world space + * @param tangent_dir Direction in tangent space + * @param normal Surface normal + * @param tangent Surface tangent + * @param bitangent Surface bitangent + * @return Direction in world space + */ +Vec3 tangent_to_world(const Vec3& tangent_dir, const Vec3& normal, + const Vec3& tangent, const Vec3& bitangent); + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_MATH_UTILS_H ``` ### 文件:include/are/core/profiler.h ```cpp +/** + * @file profiler.h + * @brief Performance profiling utilities + */ + +#ifndef ARE_INCLUDE_CORE_PROFILER_H +#define ARE_INCLUDE_CORE_PROFILER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @struct ProfileResult + * @brief Result of a profiling measurement + */ +struct ProfileResult { + std::string name_; ///< Profile section name + double duration_ms_; ///< Duration in milliseconds + uint64_t call_count_; ///< Number of times called + double avg_duration_ms_; ///< Average duration per call +}; + +/** + * @class Profiler + * @brief Simple performance profiler + * + * This class provides basic timing functionality for performance analysis. + * It is only active when ARE_ENABLE_PROFILING is defined. + */ +class Profiler { +public: + /** + * @brief Initialize the profiler + */ + static void init(); + + /** + * @brief Shutdown the profiler and print results + */ + static void shutdown(); + + /** + * @brief Begin a profiling section + * @param name Section name + */ + static void begin(const std::string& name); + + /** + * @brief End a profiling section + * @param name Section name + */ + static void end(const std::string& name); + + /** + * @brief Get profiling results + * @return Map of section names to profile results + */ + static const std::unordered_map& get_results(); + + /** + * @brief Reset all profiling data + */ + static void reset(); + + /** + * @brief Print profiling results to console + */ + static void print_results(); + +private: + struct SectionData { + std::chrono::high_resolution_clock::time_point start_time_; + double total_duration_ms_ = 0.0; + uint64_t call_count_ = 0; + }; + + static std::unordered_map sections_; + static std::unordered_map results_; + static bool enabled_; +}; + +/** + * @class ScopedProfiler + * @brief RAII-style profiler for automatic timing + */ +class ScopedProfiler { +public: + /** + * @brief Constructor - begins profiling + * @param name Section name + */ + explicit ScopedProfiler(const std::string& name); + + /** + * @brief Destructor - ends profiling + */ + ~ScopedProfiler(); + +private: + std::string name_; +}; + +} // namespace are + +// Profiling macros +#ifdef ARE_ENABLE_PROFILING + #define ARE_PROFILE_BEGIN(name) are::Profiler::begin(name) + #define ARE_PROFILE_END(name) are::Profiler::end(name) + #define ARE_PROFILE_SCOPE(name) are::ScopedProfiler are_profiler_##__LINE__(name) + #define ARE_PROFILE_FUNCTION() ARE_PROFILE_SCOPE(__func__) +#else + #define ARE_PROFILE_BEGIN(name) ((void)0) + #define ARE_PROFILE_END(name) ((void)0) + #define ARE_PROFILE_SCOPE(name) ((void)0) + #define ARE_PROFILE_FUNCTION() ((void)0) +#endif + +#endif // ARE_INCLUDE_CORE_PROFILER_H ``` ### 文件:src/core/logger.cpp ```cpp +/** + * @file logger.cpp + * @brief Implementation of logging system + */ + +#include +#include +#include +#include +#include +#include + +namespace are { + +std::shared_ptr Logger::logger_impl_ = nullptr; +bool Logger::initialized_ = false; + +void Logger::init(LogLevel min_level) { + if (initialized_) { + return; + } + + try { + // Create console sink with color support + auto console_sink = std::make_shared(); + console_sink->set_pattern("[%H:%M:%S.%e] [%^%l%$] %v"); + + // Create logger with console sink + auto logger = std::make_shared("are", console_sink); + + // Set log level + switch (min_level) { + case LogLevel::ARE_LOG_TRACE: + logger->set_level(spdlog::level::trace); + break; + case LogLevel::ARE_LOG_DEBUG: + logger->set_level(spdlog::level::debug); + break; + case LogLevel::ARE_LOG_INFO: + logger->set_level(spdlog::level::info); + break; + case LogLevel::ARE_LOG_WARN: + logger->set_level(spdlog::level::warn); + break; + case LogLevel::ARE_LOG_ERROR: + logger->set_level(spdlog::level::err); + break; + case LogLevel::ARE_LOG_CRITICAL: + logger->set_level(spdlog::level::critical); + break; + } + + // Flush on error or higher + logger->flush_on(spdlog::level::err); + + // Set as default logger + spdlog::set_default_logger(logger); + logger_impl_ = logger; + initialized_ = true; + + } catch (const std::exception& e) { + fprintf(stderr, "[ARE] Failed to initialize logger: %s\n", e.what()); + } +} + +void Logger::shutdown() { + if (!initialized_) { + return; + } + + try { + spdlog::shutdown(); + logger_impl_.reset(); + initialized_ = false; + } catch (const std::exception& e) { + fprintf(stderr, "[ARE] Error during logger shutdown: %s\n", e.what()); + } +} + +void Logger::log(LogLevel level, const char* file, const char* func, + int line, const std::string& message) { + if (!initialized_) { + init(); + } + + // Extract filename from full path + const char* filename = file; + const char* last_slash = nullptr; + + for (const char* p = file; *p; ++p) { + if (*p == '/' || *p == '\\') { + last_slash = p; + } + } + + if (last_slash) { + filename = last_slash + 1; + } + + // Format message with location information + std::string formatted = message + " (" + filename + ":" + std::to_string(line) + ")"; + + try { + auto logger = std::static_pointer_cast(logger_impl_); + + switch (level) { + case LogLevel::ARE_LOG_TRACE: + logger->trace(formatted); + break; + case LogLevel::ARE_LOG_DEBUG: + logger->debug(formatted); + break; + case LogLevel::ARE_LOG_INFO: + logger->info(formatted); + break; + case LogLevel::ARE_LOG_WARN: + logger->warn(formatted); + break; + case LogLevel::ARE_LOG_ERROR: + logger->error(formatted); + break; + case LogLevel::ARE_LOG_CRITICAL: + logger->critical(formatted); + break; + } + } catch (const std::exception& e) { + fprintf(stderr, "[ARE] Logging error: %s\n", e.what()); + } +} + +void Logger::set_level(LogLevel level) { + if (!initialized_) { + return; + } + + try { + auto logger = std::static_pointer_cast(logger_impl_); + + switch (level) { + case LogLevel::ARE_LOG_TRACE: + logger->set_level(spdlog::level::trace); + break; + case LogLevel::ARE_LOG_DEBUG: + logger->set_level(spdlog::level::debug); + break; + case LogLevel::ARE_LOG_INFO: + logger->set_level(spdlog::level::info); + break; + case LogLevel::ARE_LOG_WARN: + logger->set_level(spdlog::level::warn); + break; + case LogLevel::ARE_LOG_ERROR: + logger->set_level(spdlog::level::err); + break; + case LogLevel::ARE_LOG_CRITICAL: + logger->set_level(spdlog::level::critical); + break; + } + } catch (const std::exception& e) { + fprintf(stderr, "[ARE] Error setting log level: %s\n", e.what()); + } +} + +} // namespace are ``` ### 文件:CMakeLists.txt ```CMakeLists.txt +cmake_minimum_required(VERSION 3.15) +project(AuroraRenderingEngine VERSION 0.1.0 LANGUAGES CXX C) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Set C standard for GLAD +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Build options +option(ARE_BUILD_SHARED "Build shared library" OFF) +option(ARE_BUILD_EXAMPLES "Build example programs" ON) +option(ARE_ENABLE_PROFILING "Enable performance profiling" ON) +option(ARE_ENABLE_DEBUG_VIS "Enable debug visualization" ON) + +# Set output directories +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# Compiler flags +if(MSVC) + add_compile_options(/W4) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + # Disable specific warnings that are too strict + add_compile_options(/wd4100) # unreferenced formal parameter +else() + add_compile_options(-Wall -Wextra -pedantic) + # Disable specific warnings + add_compile_options(-Wno-unused-parameter) +endif() + +# Find required packages +find_package(OpenGL REQUIRED) +find_package(glfw3 REQUIRED) +find_package(glm REQUIRED) + +# Try to find spdlog (system installation) +find_package(spdlog QUIET) + +if(NOT spdlog_FOUND) + message(STATUS "spdlog not found in system, using local version") + # Use local spdlog + set(SPDLOG_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/spdlog/include") + if(NOT EXISTS ${SPDLOG_INCLUDE_DIR}) + message(FATAL_ERROR "spdlog not found. Please install spdlog or place it in lib/spdlog/") + endif() + add_library(spdlog INTERFACE) + target_include_directories(spdlog INTERFACE ${SPDLOG_INCLUDE_DIR}) + # spdlog compile definitions + target_compile_definitions(spdlog INTERFACE SPDLOG_COMPILED_LIB) +endif() + +# Find OpenMP (optional) +find_package(OpenMP) +if(OpenMP_CXX_FOUND) + set(ARE_USE_OPENMP ON) + add_compile_definitions(ARE_USE_OPENMP) + message(STATUS "OpenMP found and enabled") +else() + message(STATUS "OpenMP not found, multithreading will use std::thread") +endif() + +# GLAD library path +set(GLAD_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/glad") +set(GLAD_SOURCE_DIR "${CMAKE_SOURCE_DIR}/lib/glad") + +if(NOT EXISTS ${GLAD_INCLUDE_DIR}) + message(FATAL_ERROR "GLAD include directory not found: ${GLAD_INCLUDE_DIR}") +endif() + +if(NOT EXISTS ${GLAD_SOURCE_DIR}) + message(FATAL_ERROR "GLAD source directory not found: ${GLAD_SOURCE_DIR}") +endif() + +# STB library path +set(STB_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/stb") + +if(NOT EXISTS ${STB_INCLUDE_DIR}) + message(FATAL_ERROR "STB include directory not found: ${STB_INCLUDE_DIR}") +endif() + +# Collect all source files from src/ +file(GLOB_RECURSE ARE_SOURCES + "${CMAKE_SOURCE_DIR}/src/*.cpp" + "${CMAKE_SOURCE_DIR}/src/*.c" +) + +# Collect all GLAD source files +file(GLOB GLAD_SOURCES + "${GLAD_SOURCE_DIR}/*.c" +) + +# Add GLAD sources to ARE sources +list(APPEND ARE_SOURCES ${GLAD_SOURCES}) + +# Collect all header files +file(GLOB_RECURSE ARE_HEADERS + "${CMAKE_SOURCE_DIR}/include/*.h" + "${CMAKE_SOURCE_DIR}/include/*.hpp" +) + +# Create library +if(ARE_BUILD_SHARED) + add_library(are SHARED ${ARE_SOURCES} ${ARE_HEADERS}) + target_compile_definitions(are PRIVATE ARE_BUILD_SHARED) + message(STATUS "Building shared library") +else() + add_library(are STATIC ${ARE_SOURCES} ${ARE_HEADERS}) + message(STATUS "Building static library") +endif() + +# Set target properties +set_target_properties(are PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + PUBLIC_HEADER "${ARE_HEADERS}" +) + +# Include directories +target_include_directories(are + PUBLIC + $ + $ + PRIVATE + ${CMAKE_SOURCE_DIR}/src + ${GLAD_INCLUDE_DIR} + ${STB_INCLUDE_DIR} +) + +# Link libraries +target_link_libraries(are + PUBLIC + OpenGL::GL + glfw + glm::glm +) + +# Link spdlog +if(spdlog_FOUND) + target_link_libraries(are PUBLIC spdlog::spdlog) +else() + target_link_libraries(are PUBLIC spdlog) + target_include_directories(are PRIVATE ${SPDLOG_INCLUDE_DIR}) +endif() + +# Link OpenMP if available +if(OpenMP_CXX_FOUND) + target_link_libraries(are PUBLIC OpenMP::OpenMP_CXX) +endif() + +# Platform-specific libraries +if(UNIX AND NOT APPLE) + target_link_libraries(are PUBLIC dl pthread) +endif() + +# Compile definitions +if(ARE_ENABLE_PROFILING) + target_compile_definitions(are PUBLIC ARE_ENABLE_PROFILING) + message(STATUS "Profiling enabled") +endif() + +if(ARE_ENABLE_DEBUG_VIS) + target_compile_definitions(are PUBLIC ARE_ENABLE_DEBUG_VIS) + message(STATUS "Debug visualization enabled") +endif() + +# Build examples +if(ARE_BUILD_EXAMPLES) + # Define helper function for creating examples + function(add_are_example EXAMPLE_NAME) + add_executable(${EXAMPLE_NAME} ${ARGN}) + target_link_libraries(${EXAMPLE_NAME} PRIVATE are) + set_target_properties(${EXAMPLE_NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + endfunction() + + # Add example subdirectories + add_subdirectory(examples/00_phase1_test) + + message(STATUS "Examples will be built") +endif() + +# Installation rules +install(TARGETS are + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/are +) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/are + DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" +) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/shaders + DESTINATION share/are/shaders +) + +# Print configuration summary +message(STATUS "") +message(STATUS "========================================") +message(STATUS "Aurora Rendering Engine Configuration") +message(STATUS "========================================") +message(STATUS " Version: ${PROJECT_VERSION}") +message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS " Library type: ${ARE_BUILD_SHARED}") +message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}") +message(STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS "") +message(STATUS "Features:") +message(STATUS " OpenMP support: ${ARE_USE_OPENMP}") +message(STATUS " Build examples: ${ARE_BUILD_EXAMPLES}") +message(STATUS " Enable profiling: ${ARE_ENABLE_PROFILING}") +message(STATUS " Enable debug vis: ${ARE_ENABLE_DEBUG_VIS}") +message(STATUS "") +message(STATUS "Dependencies:") +message(STATUS " OpenGL: ${OPENGL_LIBRARIES}") +message(STATUS " GLFW: Found") +message(STATUS " GLM: Found") +if(spdlog_FOUND) + message(STATUS " spdlog: Found (system)") +else() + message(STATUS " spdlog: Found (local)") +endif() +message(STATUS " GLAD: ${GLAD_INCLUDE_DIR}") +message(STATUS " STB: ${STB_INCLUDE_DIR}") +message(STATUS "") +message(STATUS "Output directories:") +message(STATUS " Executables: ${CMAKE_BINARY_DIR}/bin") +message(STATUS " Libraries: ${CMAKE_BINARY_DIR}/lib") +message(STATUS "========================================") +message(STATUS "") ``` + +接下来,其实我已经实现了所有头文件,下面是Phase 2的头文件: +### 文件:vertex.h +```cpp +/** + * @file vertex.h + * @brief Vertex data structure definition + */ + +#ifndef ARE_INCLUDE_GEOMETRY_VERTEX_H +#define ARE_INCLUDE_GEOMETRY_VERTEX_H + +#include + +namespace are { + +/** + * @struct Vertex + * @brief Standard vertex structure for mesh data + * + * Contains position, normal, texture coordinates, and tangent information + * for PBR rendering pipeline. + */ +struct Vertex { + Vec3 position_;///< Vertex position in object space + Vec3 normal_; ///< Vertex normal (normalized) + Vec2 texcoord_; ///< Texture coordinates (UV) + Vec3 tangent_; ///< Tangent vector for normal mapping + + Vertex() = default; + + /** + * @brief Construct vertex with position only + * @param pos Position + */ + explicit Vertex(const Vec3& pos); + + /** + * @brief Construct vertex with position and normal + * @param pos Position + * @param norm Normal + */ + Vertex(const Vec3& pos, const Vec3& norm); + + /** + * @brief Construct vertex with position, normal, and texcoord + * @param pos Position + * @param norm Normal + * @param uv Texture coordinates + */ + Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv); + + /** + * @brief Construct vertex with all attributes + * @param pos Position + * @param norm Normal + * @param uv Texture coordinates + * @param tan Tangent + */ + Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv, const Vec3& tan); + + /** + * @brief Interpolate between two vertices + * @param a First vertex + * @param b Second vertex + * @param t Interpolation factor [0, 1] + * @return Interpolated vertex + */ + static Vertex lerp(const Vertex& a, const Vertex& b, Real t); +}; + +/** + * @brief Get vertex attribute stride for OpenGL + * @return Size of Vertex structure in bytes + */ +constexpr size_t get_vertex_stride() { + return sizeof(Vertex); +} + +/** + * @brief Get offset of position attribute + * @return Offset in bytes + */ +constexpr size_t get_position_offset() { + return offsetof(Vertex, position_); +} + +/** + * @brief Get offset of normal attribute + * @return Offset in bytes + */ +constexpr size_t get_normal_offset() { + return offsetof(Vertex, normal_); +} + +/** + * @brief Get offset of texcoord attribute + * @return Offset in bytes + */ +constexpr size_t get_texcoord_offset() { + return offsetof(Vertex, texcoord_); +} + +/** + * @brief Get offset of tangent attribute + * @return Offset in bytes + */ +constexpr size_t get_tangent_offset() { + return offsetof(Vertex, tangent_); +} + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_VERTEX_H +``` + +### 文件:triangle.h +```cpp +/** + * @file triangle.h + * @brief Triangle primitive definition + */ + +#ifndef ARE_INCLUDE_GEOMETRY_TRIANGLE_H +#define ARE_INCLUDE_GEOMETRY_TRIANGLE_H + +#include +#include +#include + +namespace are { + +// Forward declaration +struct Ray; +struct HitRecord; + +/** + * @struct Triangle + * @brief Triangle primitive for ray tracing + * + * Stores three vertices and provides intersection testing. + */ +struct Triangle { + Vertex v0_; ///< First vertex + Vertex v1_; ///< Second vertex + Vertex v2_; ///< Third vertex + MaterialHandle material_; ///< Material handle + + /** + * @brief Default constructor + */ + Triangle(); + + /** + * @brief Construct triangle from three vertices + * @param v0 First vertex + * @param v1 Second vertex + * @param v2 Third vertex + * @param material Material handle + */ + Triangle(const Vertex& v0, const Vertex& v1, const Vertex& v2, + MaterialHandle material = are_invalid_handle); + + /** + * @brief Get triangle centroid + * @return Centroid position + */ + Vec3 centroid() const; + + /** + * @brief Get triangle normal (geometric normal) + * @return Normal vector (normalized) + */ + Vec3 normal() const; + + /** + * @brief Get triangle area + * @return Area + */ + Real area() const; + + /** + * @brief Compute axis-aligned bounding box + * @return AABB containing the triangle + */ + AABB compute_aabb() const; + + /** + * @brief Ray-triangle intersection test (Möller-Trumbore algorithm) + * @param ray Ray to test + * @param hit Output hit record + * @return true if intersection occurred + */ + bool intersect(const Ray& ray, HitRecord& hit) const; + + /** + * @brief Fast ray-triangle intersection test (no hit record) + * @param ray Ray to test + * @param t_max Maximum t value + * @return true if intersection occurred + */ + bool intersect_fast(const Ray& ray, Real t_max) const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_TRIANGLE_H +``` + +### 文件:aabb.h +```cpp +/** + * @file aabb.h + * @brief Axis-Aligned Bounding Box implementation + */ + +#ifndef ARE_INCLUDE_GEOMETRY_AABB_H +#define ARE_INCLUDE_GEOMETRY_AABB_H + +#include + +namespace are { + +// Forward declaration +struct Ray; + +/** + * @class AABB + * @brief Axis-Aligned Bounding Box for spatial queries + * + * Used for BVH construction and ray intersection acceleration. + */ +class AABB { +public: + Vec3 min_; ///< Minimum corner + Vec3 max_; ///< Maximum corner + + /** + * @brief Default constructor - creates invalid AABB + */ + AABB(); + + /** + * @brief Construct AABB from min and max corners + * @param min Minimum corner + * @param max Maximum corner + */ + AABB(const Vec3& min, const Vec3& max); + + /** + * @brief Construct AABB containing a single point + * @param point Point to contain + */ + explicit AABB(const Vec3& point); + + /** + * @brief Check if AABB is valid (min <= max) + * @return true if valid + */ + bool is_valid() const; + + /** + * @brief Get center of AABB + * @return Center point + */ + Vec3 center() const; + + /** + * @brief Get size (extent) of AABB + * @return Size vector + */ + Vec3 size() const; + + /** + * @brief Get surface area of AABB + * @return Surface area + */ + Real surface_area() const; + + /** + * @brief Get volume of AABB + * @return Volume + */ + Real volume() const; + + /** + * @brief Get longest axis index (0=x, 1=y, 2=z) + * @return Axis index + */ + int longest_axis() const; + + /** + * @brief Expand AABB to include a point + * @param point Point to include + */ + void expand(const Vec3& point); + + /** + * @brief Expand AABB to include another AABB + * @param other AABB to include + */ + void expand(const AABB& other); + + /** + * @brief Check if AABB contains a point + * @param point Point to check + * @return true if point is inside AABB + */ + bool contains(const Vec3& point) const; + + /** + * @brief Check if AABB intersects another AABB + * @param other AABB to check + * @return true if AABBs intersect + */ + bool intersects(const AABB& other) const; + + /** + * @brief Ray-AABB intersection test + * @param ray Ray to test + * @param t_min Minimum t value (output) + * @param t_max Maximum t value (output) + * @return true if ray intersects AABB + */ + bool intersect_ray(const Ray& ray, Real& t_min, Real& t_max) const; + + /** + * @brief Merge two AABBs + * @param a First AABB + * @param b Second AABB + * @return Merged AABB containing both + */ + static AABB merge(const AABB& a, const AABB& b); + + /** + * @brief Create invalid AABB (for initialization) + * @return Invalid AABB + */ + static AABB invalid(); +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_AABB_H +``` + +### 文件:transform.h +```cpp +/** + * @file transform.h + * @brief Transformation matrix utilities + */ + +#ifndef ARE_INCLUDE_GEOMETRY_TRANSFORM_H +#define ARE_INCLUDE_GEOMETRY_TRANSFORM_H + +#include + +namespace are { + +/** + * @class Transform + * @brief 3D transformation (position, rotation, scale) + * + * Provides convenient interface for building transformation matrices. + */ +class Transform { +public: + /** + * @brief Default constructor (identity transform) + */ + Transform(); + + /** + * @brief Construct from position, rotation, and scale + * @param position Translation + * @param rotation Rotation (Euler angles in radians) + * @param scale Scale factors + */ + Transform(const Vec3& position, const Vec3& rotation, const Vec3& scale); + + // Setters + void set_position(const Vec3& position); + void set_rotation(const Vec3& rotation); + void set_scale(const Vec3& scale); + void set_scale(Real uniform_scale); + + // Getters + const Vec3& get_position() const { return position_; } + const Vec3& get_rotation() const { return rotation_; } + const Vec3& get_scale() const { return scale_; } + + // Matrix operations + Mat4 get_matrix() const; + Mat4 get_inverse_matrix() const; + Mat3 get_normal_matrix() const; + + /** + * @brief Transform a point + * @param point Point to transform + * @return Transformed point + */ + Vec3 transform_point(const Vec3& point) const; + + /** + * @brief Transform a direction (ignores translation) + * @param direction Direction to transform + * @return Transformed direction + */ + Vec3 transform_direction(const Vec3& direction) const; + + /** + * @brief Transform a normal (uses inverse transpose) + * @param normal Normal to transform + * @return Transformed normal + */ + Vec3 transform_normal(const Vec3& normal) const; + + /** + * @brief Combine two transforms + * @param other Other transform + * @return Combined transform + */ + Transform operator*(const Transform& other) const; + + /** + * @brief Create identity transform + * @return Identity transform + */ + static Transform identity(); + + /** + * @brief Create translation transform + * @param translation Translation vector + * @return Translation transform + */ + static Transform translate(const Vec3& translation); + + /** + * @brief Create rotation transform + * @param rotation Rotation (Euler angles in radians) + * @return Rotation transform + */ + static Transform rotate(const Vec3& rotation); + + /** + * @brief Create scale transform + * @param scale Scale factors + * @return Scale transform + */ + static Transform scale(const Vec3& scale); + +private: + void mark_dirty(); + void update_matrix() const; + + Vec3 position_; ///< Translation + Vec3 rotation_; ///< Rotation (Euler angles) + Vec3 scale_; ///< Scale factors + + mutable Mat4 matrix_; ///< Cached transformation matrix + mutable Mat4 inverse_matrix_; ///< Cached inverse matrix + mutable bool dirty_; ///< Matrix needs update +}; + +} // namespace are + +#endif // ARE_INCLUDE_GEOMETRY_TRANSFORM_H +``` + +### 文件:camera.h +```cpp +/** + * @file camera.h + * @brief Camera class for view and projection management + */ + +#ifndef ARE_INCLUDE_SCENE_CAMERA_H +#define ARE_INCLUDE_SCENE_CAMERA_H + +#include + +namespace are { + +/** + * @class Camera + * @brief Perspective camera for rendering + * + * Manages view and projection matrices, and provides ray generation + * for ray tracing. + */ +class Camera { +public: + /** + * @brief Default constructor + */ + Camera(); + + /** + * @brief Construct camera with position and target + * @param position Camera position + * @param target Look-at target + * @param up Up vector + */ + Camera(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0)); + + // Position and orientation setters + void set_position(const Vec3& position); + void set_target(const Vec3& target); + void set_up(const Vec3& up); + void look_at(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0)); + + // Projection setters + void set_fov(Real fov_degrees); + void set_aspect_ratio(Real aspect); + void set_near_plane(Real near); + void set_far_plane(Real far); + void set_perspective(Real fov_degrees, Real aspect, Real near, Real far); + + // Getters + const Vec3& get_position() const { return position_; } + const Vec3& get_target() const { return target_; } + const Vec3& get_up() const { return up_; } + Real get_fov() const { return fov_; } + Real get_aspect_ratio() const { return aspect_ratio_; } + Real get_near_plane() const { return near_plane_; } + Real get_far_plane() const { return far_plane_; } + + // Direction vectors + Vec3 get_forward() const; + Vec3 get_right() const; + + // Matrix getters + const Mat4& get_view_matrix() const; + const Mat4& get_projection_matrix() const; + Mat4 get_view_projection_matrix() const; + + /** + * @brief Generate ray for given pixel coordinates + * @param u Normalized x coordinate [0, 1] + * @param v Normalized y coordinate [0, 1] + * @param origin Output ray origin + * @param direction Output ray direction (normalized) + */ + void generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const; + + /** + * @brief Check if camera parameters have changed + * @return true if matrices need recalculation + */ + bool is_dirty() const { return dirty_; } + + /** + * @brief Mark camera as clean after matrix update + */ + void clear_dirty() { dirty_ = false; } + +private: + void update_view_matrix() const; + void update_projection_matrix() const; + + Vec3 position_; ///< Camera position + Vec3 target_; ///< Look-at target + Vec3 up_; ///< Up vector + + Real fov_; ///< Field of view in degrees + Real aspect_ratio_; ///< Aspect ratio (width/height) + Real near_plane_; ///< Near clipping plane + Real far_plane_; ///< Far clipping plane + + mutable Mat4 view_matrix_; ///< Cached view matrix + mutable Mat4 projection_matrix_; ///< Cached projection matrix + mutable bool view_dirty_; ///< View matrix needs update + mutable bool projection_dirty_; ///< Projection matrix needs update + bool dirty_; ///< Any parameter changed +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_CAMERA_H +``` + +### 文件:mesh.h +```cpp +/** + * @file mesh.h + * @brief Mesh class for geometry storage + */ + +#ifndef ARE_INCLUDE_SCENE_MESH_H +#define ARE_INCLUDE_SCENE_MESH_H + +#include +#include +#include +#include + +namespace are { + +/** + * @class Mesh + * @brief Triangle mesh container + * + * Stores vertex and index data for a triangle mesh. + * Supports automatic AABB computation. + */ +class Mesh { +public: + /** + * @brief Default constructor - creates empty mesh + */ + Mesh(); + + /** + * @brief Construct mesh from vertex and index data + * @param vertices Vertex array + * @param indices Index array (triangles) + * @param material_id Material handle + */ + Mesh(const std::vector& vertices, + const std::vector& indices, + MaterialHandle material_id = are_invalid_handle); + + /** + * @brief Construct mesh from raw arrays + * @param vertices Vertex array pointer + * @param vertex_count Number of vertices + * @param indices Index array pointer + * @param index_count Number of indices + * @param material_id Material handle + */ + Mesh(const Vertex* vertices, size_t vertex_count, + const uint32_t* indices, size_t index_count, + MaterialHandle material_id = are_invalid_handle); + + // Data setters + void set_vertices(const std::vector& vertices); + void set_indices(const std::vector& indices); + void set_material(MaterialHandle material_id); + + // Data getters + const std::vector& get_vertices() const { return vertices_; } + const std::vector& get_indices() const { return indices_; } + MaterialHandle get_material() const { return material_id_; } + + // Geometry queries + size_t get_vertex_count() const { return vertices_.size(); } + size_t get_index_count() const { return indices_.size(); } + size_t get_triangle_count() const { return indices_.size() / 3; } + bool is_empty() const { return vertices_.empty() || indices_.empty(); } + + // AABB + const AABB& get_aabb() const { return aabb_; } + void compute_aabb(); + + /** + * @brief Compute tangent vectors for normal mapping + */ + void compute_tangents();/** + * @brief Get triangle vertices by index + * @param triangle_index Triangle index + * @param v0 Output first vertex + * @param v1 Output second vertex + * @param v2 Output third vertex + * @return true if triangle exists + */ + bool get_triangle(size_t triangle_index, Vertex& v0, Vertex& v1, Vertex& v2) const; + + // GPU resource handles (set by Renderer) + void set_vao(uint32_t vao) { vao_ = vao; } + void set_vbo(uint32_t vbo) { vbo_ = vbo; } + void set_ebo(uint32_t ebo) { ebo_ = ebo; } + uint32_t get_vao() const { return vao_; } + uint32_t get_vbo() const { return vbo_; } + uint32_t get_ebo() const { return ebo_; } + bool has_gpu_resources() const { return vao_ != 0; } + +private: + std::vector vertices_; ///< Vertex data + std::vector indices_; ///< Index data (triangles) + MaterialHandle material_id_; ///< Associated material + AABB aabb_; ///< Bounding box + + // GPU resources + uint32_t vao_; ///< Vertex Array Object + uint32_t vbo_; ///< Vertex Buffer Object + uint32_t ebo_; ///< Element Buffer Object +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MESH_H +``` + +### 文件:material.h +```cpp +/** + * @file material.h + * @brief PBR material definition + */ + +#ifndef ARE_INCLUDE_SCENE_MATERIAL_H +#define ARE_INCLUDE_SCENE_MATERIAL_H + +#include +#include + +namespace are { + +/** + * @class Material + * @brief Physically-based rendering material + * + * Supports standard PBR workflow with metallic-roughness model. + */ +class Material { +public: + /** + * @brief Default constructor - creates default white material + */ + Material(); + + // Albedo (base color) + void set_albedo(const Vec3& albedo); + void set_albedo_map(const std::string& path); + const Vec3& get_albedo() const { return albedo_; } + const std::string& get_albedo_map() const { return albedo_map_; } + bool has_albedo_map() const { return !albedo_map_.empty(); } + + // Metallic + void set_metallic(Real metallic); + void set_metallic_map(const std::string& path); + Real get_metallic() const { return metallic_; } + const std::string& get_metallic_map() const { return metallic_map_; } + bool has_metallic_map() const { return !metallic_map_.empty(); } + + // Roughness + void set_roughness(Real roughness); + void set_roughness_map(const std::string& path); + Real get_roughness() const { return roughness_; } + const std::string& get_roughness_map() const { return roughness_map_; } + bool has_roughness_map() const { return !roughness_map_.empty(); } + + // Normal map + void set_normal_map(const std::string& path); + const std::string& get_normal_map() const { return normal_map_; } + bool has_normal_map() const { return !normal_map_.empty(); } + + // Ambient occlusion + void set_ao_map(const std::string& path); + const std::string& get_ao_map() const { return ao_map_; } + bool has_ao_map() const { return !ao_map_.empty(); } + + // Emissive + void set_emissive(const Vec3& emissive); + void set_emissive_map(const std::string& path); + const Vec3& get_emissive() const { return emissive_; } + const std::string& get_emissive_map() const { return emissive_map_; } + bool has_emissive_map() const { return !emissive_map_.empty(); } + bool is_emissive() const; + + // Texture handles (set by TextureManager) + void set_albedo_texture_handle(TextureHandle handle) { albedo_tex_handle_ = handle; } + void set_metallic_texture_handle(TextureHandle handle) { metallic_tex_handle_ = handle; } + void set_roughness_texture_handle(TextureHandle handle) { roughness_tex_handle_ = handle; } + void set_normal_texture_handle(TextureHandle handle) { normal_tex_handle_ = handle; } + void set_ao_texture_handle(TextureHandle handle) { ao_tex_handle_ = handle; } + void set_emissive_texture_handle(TextureHandle handle) { emissive_tex_handle_ = handle; } + + TextureHandle get_albedo_texture_handle() const { return albedo_tex_handle_; } + TextureHandle get_metallic_texture_handle() const { return metallic_tex_handle_; } + TextureHandle get_roughness_texture_handle() const { return roughness_tex_handle_; } + TextureHandle get_normal_texture_handle() const { return normal_tex_handle_; } + TextureHandle get_ao_texture_handle() const { return ao_tex_handle_; } + TextureHandle get_emissive_texture_handle() const { return emissive_tex_handle_; } + +private: + // Base values + Vec3 albedo_;///< Base color (RGB) + Real metallic_; ///< Metallic factor [0, 1] + Real roughness_; ///< Roughness factor [0, 1] + Vec3 emissive_; ///< Emissive color (RGB) + + // Texture paths + std::string albedo_map_; + std::string metallic_map_; + std::string roughness_map_; + std::string normal_map_; + std::string ao_map_; + std::string emissive_map_; + + // Texture handles (GPU resources) + TextureHandle albedo_tex_handle_; + TextureHandle metallic_tex_handle_; + TextureHandle roughness_tex_handle_; + TextureHandle normal_tex_handle_; + TextureHandle ao_tex_handle_; + TextureHandle emissive_tex_handle_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_MATERIAL_H +``` + +### 文件:light.h +```cpp +/** + * @file light.h + * @brief Base light class and common light utilities + */ + +#ifndef ARE_INCLUDE_SCENE_LIGHT_H +#define ARE_INCLUDE_SCENE_LIGHT_H + +#include + +namespace are { + +/** + * @enum LightType + * @brief Types of light sources + */ +enum class LightType { + ARE_LIGHT_DIRECTIONAL, + ARE_LIGHT_POINT, + ARE_LIGHT_SPOT +}; + +/** + * @struct LightData + * @brief Packed light data for GPU transfer + * + * This structure is designed to be efficiently transferred to GPU + * via SSBO or UBO. + */ +struct LightData { + Vec4 position_type_; ///< xyz: position, w: light type + Vec4 direction_range_; ///< xyz: direction, w: range + Vec4 color_intensity_; ///< xyz: color, w: intensity + Vec4 params_; ///< Light-specific parameters +}; + +/** + * @class Light + * @brief Base class for all light types + */ +class Light { +public: + /** + * @brief Constructor + * @param type Light type + */ + explicit Light(LightType type); + + virtual ~Light() = default; + + // Common properties + void set_color(const Vec3& color); + void set_intensity(Real intensity); + void set_cast_shadows(bool cast); + + const Vec3& get_color() const { return color_; } + Real get_intensity() const { return intensity_; } + bool get_cast_shadows() const { return cast_shadows_; } + LightType get_type() const { return type_; } + + /** + * @brief Pack light data for GPU transfer + * @return Packed light data + */ + virtual LightData pack() const = 0; + + /** + * @brief Check if light affects a point + * @param point World position + * @return true if light can affect the point + */ + virtual bool affects_point(const Vec3& point) const = 0; + +protected: + LightType type_; ///< Light type + Vec3 color_; ///< Light color (RGB) + Real intensity_; ///< Light intensity + bool cast_shadows_; ///< Whether light casts shadows +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_LIGHT_H +``` + +### 文件:scene_manager.h +```cpp +/** + * @file scene_manager.h + * @brief Scene data management + */ + +#ifndef ARE_INCLUDE_SCENE_SCENE_MANAGER_H +#define ARE_INCLUDE_SCENE_SCENE_MANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +/** + * @class SceneManager + * @brief Manages all scene objects (meshes, materials, lights) + * + * Provides handle-based access to scene resources and tracks + * scene modifications for BVH rebuilding. + */ +class SceneManager { +public: + /** + * @brief Constructor + */ + SceneManager(); + + /** + * @brief Destructor + */ + ~SceneManager(); + + // Mesh management + MeshHandle add_mesh(const Mesh& mesh); + void remove_mesh(MeshHandle handle); + void update_mesh(MeshHandle handle, const Mesh& mesh); + Mesh* get_mesh(MeshHandle handle); + const Mesh* get_mesh(MeshHandle handle) const; + const std::vector& get_all_meshes() const { return meshes_; } + + // Material management + MaterialHandle add_material(const Material& material); + void remove_material(MaterialHandle handle); + void update_material(MaterialHandle handle, const Material& material); + Material* get_material(MaterialHandle handle); + const Material* get_material(MaterialHandle handle) const; + const std::vector& get_all_materials() const { return materials_; } + + // Light management + LightHandle add_light(const std::shared_ptr& light); + void remove_light(LightHandle handle); + std::shared_ptr get_light(LightHandle handle); + const std::vector>& get_all_lights() const { return lights_; } + + // Scene queries + size_t get_mesh_count() const { return meshes_.size(); } + size_t get_material_count() const { return materials_.size(); } + size_t get_light_count() const { return lights_.size(); } + size_t get_total_triangle_count() const; + + // Scene state + bool is_dirty() const { return dirty_; } + void mark_dirty() { dirty_ = true; } + void clear_dirty() { dirty_ = false; } + + /** + * @brief Clear all scene data + */ + void clear(); + + /** + * @brief Validate all handles and remove invalid entries + */ + void compact(); + +private: + std::vector meshes_; ///< Mesh storage + std::vector materials_; ///< Material storage + std::vector> lights_; ///< Light storage + + std::unordered_map mesh_handle_map_; + std::unordered_map material_handle_map_; + std::unordered_map light_handle_map_; + + MeshHandle next_mesh_handle_; + MaterialHandle next_material_handle_; + LightHandle next_light_handle_; + + bool dirty_; ///< Scene modified flag +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_SCENE_MANAGER_H +``` +关于异常部分,你直接ARE_LOG_CRITICAL然后返回或者throw std::runtime_error即可;GLM已经有了自定义别名,且已经启用GLM扩展。 +关于命名空间的话,就像你刚才看到的一样,代码包裹在are命名空间中。 +还有需要我提供的东西吗?没有的话,你可以开始分步骤实现Phase 2。 diff --git a/include/are/acceleration/bvh.h b/include/are/acceleration/bvh.h index 2352402..e1d2f27 100644 --- a/include/are/acceleration/bvh.h +++ b/include/are/acceleration/bvh.h @@ -95,8 +95,20 @@ public: void clear(); private: + // Recursive traversal (kept for reference) bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const; bool intersect_any_recursive(uint32_t node_index, const Ray& ray, Real t_max) const; + + // Optimized iterative traversal + bool intersect_iterative(const Ray& ray, HitRecord& hit) const; + bool intersect_any_iterative(const Ray& ray, Real t_max) const; + + // Fast intersection helpers + inline bool intersect_aabb_fast(const AABB& bounds, const Ray& ray, + const Vec3& inv_dir, Real t_max, + Real& t_min_out, Real& t_max_out) const; + inline bool intersect_triangle_fast(const Triangle& triangle, const Ray& ray, + Real t_max, HitRecord& hit) const; std::vector nodes_; ///< BVH nodes std::vector primitive_indices_; ///< Primitive index array diff --git a/include/are/renderer/gbuffer.h b/include/are/rasterizer/gbuffer.h similarity index 100% rename from include/are/renderer/gbuffer.h rename to include/are/rasterizer/gbuffer.h diff --git a/include/are/raytracer/compute_raytracer.h b/include/are/raytracer/compute_raytracer.h index d273406..a74f97e 100644 --- a/include/are/raytracer/compute_raytracer.h +++ b/include/are/raytracer/compute_raytracer.h @@ -7,13 +7,11 @@ #define ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H #include +#include #include namespace are { -// Forward declarations -class ShaderProgram; - /** * @class ComputeRayTracer * @brief GPU-based ray tracing using compute shaders diff --git a/include/are/renderer/renderer.h b/include/are/renderer/renderer.h index 77bd5eb..435108f 100644 --- a/include/are/renderer/renderer.h +++ b/include/are/renderer/renderer.h @@ -24,6 +24,7 @@ class SceneManager; class Rasterizer; class RayTracer; class TextureManager; +class BVH; /** * @class Renderer @@ -109,6 +110,7 @@ private: std::unique_ptr rasterizer_; ///< Rasterization pipeline std::unique_ptr raytracer_; ///< Ray tracing pipeline std::unique_ptr texture_manager_; ///< Texture management + std::unique_ptr bvh_; ///< BVH acceleration structure Camera camera_; ///< Active camera RenderStats stats_; ///< Rendering statistics diff --git a/include/are/texture/sampler.h b/include/are/texture/sampler.h index c34fcbe..4b7d993 100644 --- a/include/are/texture/sampler.h +++ b/include/are/texture/sampler.h @@ -7,13 +7,10 @@ #define ARE_INCLUDE_TEXTURE_SAMPLER_H #include +#include namespace are { -// Forward declaration -class Texture; -class TextureWrap; - /** * @class Sampler * @brief Texture sampling utilities for CPU ray tracing diff --git a/new_conversation.md b/new_conversation.md index 04a7bfa..431443d 100644 --- a/new_conversation.md +++ b/new_conversation.md @@ -850,5 +850,5 @@ renderer.render(); - [ ] 光源能够打包数据 - [ ] 场景管理器能够管理对象 -对于目前的进度,我们已经实现了Phase 1,正在实现Phase 2,请你帮我们进行Phase 2的实现。 -但是因为我还没有给你我们已有的代码,因此你需要哪些头文件的代码?请在下一轮向我询问,然后我们会开始Phase 2的开发。 +对于目前的进度,我们已经实现到了Phase 4,正在实现Phase 5,请你帮我们进行Phase 5以及后续代码的实现。 +但是因为我还没有给你我们已有的代码,因此你需要哪些头文件的代码?请在下一轮向我询问,然后我们会开始Phase 5的开发。 diff --git a/phase3.md b/phase3.md new file mode 100644 index 0000000..d013a07 --- /dev/null +++ b/phase3.md @@ -0,0 +1,605 @@ +### 文件:include/are/platform/window.h +```cpp +/** + * @file window.h + * @brief Window management using GLFW + */ + +#ifndef ARE_INCLUDE_PLATFORM_WINDOW_H +#define ARE_INCLUDE_PLATFORM_WINDOW_H + +#include +#include +#include + +// Forward declare GLFW types to avoid including GLFW in header +struct GLFWwindow; + +namespace are { + +/** + * @class Window + * @brief GLFW window wrapper + * + * Manages window creation, input handling, and OpenGL context. + */ +class Window { +public: + /** + * @brief Constructor + * @param config Window configuration + */ + explicit Window(const WindowConfig& config); + + /** + * @brief Destructor + */ + ~Window(); + + // Window control + bool should_close() const; + void set_should_close(bool should_close); + void swap_buffers(); + void poll_events(); + + // Window properties + int get_width() const; + int get_height() const; + Real get_aspect_ratio() const; + const std::string& get_title() const; + + void set_title(const std::string& title); + void set_size(int width, int height); + + // Framebuffer size (may differ from window size on high-DPI displays) + void get_framebuffer_size(int& width, int& height) const; + + // VSync control + void set_vsync(bool enabled); + bool get_vsync() const; + + // Input queries (basic support) + bool is_key_pressed(int key) const; + bool is_mouse_button_pressed(int button) const; + void get_cursor_pos(double& x, double& y) const; + + // Internal + GLFWwindow* get_native_window() const { return window_; } + +private: + void initialize_glfw(); + void create_window(); + void setup_callbacks(); + + static void framebuffer_size_callback(GLFWwindow* window, int width, int height); + static void error_callback(int error, const char* description); + + GLFWwindow* window_; ///< GLFW window handle + WindowConfig config_; ///< Window configuration + bool vsync_enabled_; ///< VSync state + + static int instance_count_; ///< Number of Window instances +}; + +} // namespace are + +#endif // ARE_INCLUDE_PLATFORM_WINDOW_H +``` + +### 文件:include/are/platform/gl_context.h +```cpp +/** + * @file gl_context.h + * @brief OpenGL context management + */ + +#ifndef ARE_INCLUDE_PLATFORM_GL_CONTEXT_H +#define ARE_INCLUDE_PLATFORM_GL_CONTEXT_H + +#include +#include + +namespace are { + +/** + * @class GLContext + * @brief OpenGL context initialization and management + * + * Handles GLAD initialization and provides OpenGL utility functions. + */ +class GLContext { +public: + /** + * @brief Initialize OpenGL context (load function pointers) + * @return true if initialization succeeded + */ + static bool initialize(); + + /** + * @brief Check if context is initialized + * @return true if initialized + */ + static bool is_initialized(); + + /** + * @brief Get OpenGL version string + * @return Version string + */ + static std::string get_version(); + + /** + * @brief Get OpenGL renderer string + * @return Renderer string + */ + static std::string get_renderer(); + + /** + * @brief Get OpenGL vendor string + * @return Vendor string + */ + static std::string get_vendor(); + + /** + * @brief Check if OpenGL extension is supported + * @param extension Extension name + * @return true if supported + */ + static bool is_extension_supported(const std::string& extension); + + /** + * @brief Print OpenGL information to console + */ + static void print_info(); + + /** + * @brief Check for OpenGL errors + * @param file Source file + * @param line Line number + * @return true if error occurred + */ + static bool check_error(const char* file, int line); + + /** + * @brief Clear all OpenGL errors + */ + static void clear_errors(); + +private: + static bool initialized_; ///< Initialization flag +}; + +} // namespace are + +// OpenGL error checking macro +#ifdef ARE_ENABLE_DEBUG_VIS + #define ARE_GL_CHECK() are::GLContext::check_error(__FILE__, __LINE__) +#else + #define ARE_GL_CHECK() ((void)0) +#endif + +#endif // ARE_INCLUDE_PLATFORM_GL_CONTEXT_H +``` + +### 文件:include/are/utils/file_utils.h +```cpp +/** + * @file file_utils.h + * @brief File system utilities + */ + +#ifndef ARE_INCLUDE_UTILS_FILE_UTILS_H +#define ARE_INCLUDE_UTILS_FILE_UTILS_H + +#include +#include +#include + +namespace are { + +/** + * @brief Read entire file into string + * @param filepath File path + * @return File contents (empty if failed) + */ +std::string read_file_to_string(const std::string& filepath); + +/** + * @brief Read entire file into byte array + * @param filepath File path + * @return File contents (empty if failed) + */ +std::vector read_file_to_bytes(const std::string& filepath); + +/** + * @brief Write string to file + * @param filepath File path + * @param content Content to write + * @return true if write succeeded + */ +bool write_string_to_file(const std::string& filepath, const std::string& content); + +/** + * @brief Write bytes to file + * @param filepath File path + * @param data Data pointer + * @param size Data size in bytes + * @return true if write succeeded + */ +bool write_bytes_to_file(const std::string& filepath, const void* data, size_t size); + +/** + * @brief Check if file exists + * @param filepath File path + * @return true if file exists + */ +bool file_exists(const std::string& filepath); + +/** + * @brief Check if path is directory + * @param path Directory path + * @return true if directory exists + */ +bool is_directory(const std::string& path); + +/** + * @brief Create directory (including parent directories) + * @param path Directory path + * @return true if creation succeeded + */ +bool create_directory(const std::string& path); + +/** + * @brief Get file extension + * @param filepath File path + * @return Extension (lowercase, without dot) + */ +std::string get_file_extension(const std::string& filepath); + +/** + * @brief Get filename from path + * @param filepath File path + * @return Filename (without directory) + */ +std::string get_filename(const std::string& filepath); + +/** + * @brief Get directory from path + * @param filepath File path + * @return Directory path + */ +std::string get_directory(const std::string& filepath); + +/** + * @brief Join path components + * @param parts Path components + * @return Joined path + */ +std::string join_path(const std::vector& parts); + +/** + * @brief Normalize path (resolve .. and .) + * @param path Path to normalize + * @return Normalized path + */ +std::string normalize_path(const std::string& path); + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_FILE_UTILS_H +``` + +这是你所要求的三个头文件,如果还需要更多头文件的代码,请随时向我提出。同时,平台层着三个函数的代码我也已经实现,你只需要专心考虑渲染管线即可。此外,渲染管线的头文件我也实现了,你只需要负责实现渲染管线的shader以及代码实现即可。 +### 文件:include/are/rasterizer/shader_program.h +```cpp +/** + * @file shader_program.h + * @brief OpenGL shader program wrapper + */ + +#ifndef ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H +#define ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H + +#include +#include +#include + +namespace are { + +/** + * @enum ShaderType + * @brief Shader stage types + */ +enum class ShaderType { + ARE_SHADER_VERTEX, + ARE_SHADER_FRAGMENT, + ARE_SHADER_COMPUTE +}; + +/** + * @class ShaderProgram + * @brief OpenGL shader program management + */ +class ShaderProgram { +public: + /** + * @brief Constructor + */ + ShaderProgram(); + + /** + * @brief Destructor + */ + ~ShaderProgram(); + + /** + * @brief Load and compile shader from file + * @param type Shader type + * @param filepath Shader file path + * @return true if compilation succeeded + */ + bool load_shader(ShaderType type, const std::string& filepath); + + /** + * @brief Compile shader from source string + * @param type Shader type + * @param source Shader source code + * @return true if compilation succeeded + */ + bool compile_shader(ShaderType type, const std::string& source); + + /** + * @brief Link shader program + * @return true if linking succeeded + */ + bool link(); + + /** + * @brief Use this shader program + */ + void use() const; + + /** + * @brief Check if program is valid + * @return true if valid + */ + bool is_valid() const { return program_ != 0 && linked_; } + + /** + * @brief Get OpenGL program ID + * @return Program ID + */ + uint32_t get_program() const { return program_; } + + // Uniform setters + void set_uniform(const std::string& name, int value); + void set_uniform(const std::string& name, float value); + void set_uniform(const std::string& name, const Vec2& value); + void set_uniform(const std::string& name, const Vec3& value); + void set_uniform(const std::string& name, const Vec4& value); + void set_uniform(const std::string& name, const Mat3& value); + void set_uniform(const std::string& name, const Mat4& value); + + /** + * @brief Get uniform location (cached) + * @param name Uniform name + * @return Uniform location (-1 if not found) + */ + int get_uniform_location(const std::string& name); + +private: + bool check_compile_errors(uint32_t shader, ShaderType type); + bool check_link_errors(); + + uint32_t program_; ///< OpenGL program ID + uint32_t vertex_shader_; ///< Vertex shader ID + uint32_t fragment_shader_; ///< Fragment shader ID + uint32_t compute_shader_; ///< Compute shader ID + + bool linked_; ///< Link status + std::unordered_map uniform_cache_; ///< Uniform location cache +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H +``` + +### 文件:include/are/rasterizer/gbuffer.h +```cpp +/** + * @file gbuffer.h + * @brief G-Buffer management for deferred rendering + */ + +#ifndef ARE_INCLUDE_RASTERIZER_GBUFFER_H +#define ARE_INCLUDE_RASTERIZER_GBUFFER_H + +#include +#include + +namespace are { + +/** + * @class GBuffer + * @brief G-Buffer for deferred rendering + * + * Contains multiple render targets for position, normal, albedo, etc. + */ +class GBuffer { +public: + /** + * @brief Constructor + * @param width Buffer width + * @param height Buffer height + */ + GBuffer(int width, int height); + + /** + * @brief Destructor + */ + ~GBuffer(); + + /** + * @brief Resize G-Buffer + * @param width New width + * @param height New height + */ + void resize(int width, int height); + + /** + * @brief Bind G-Buffer for rendering + */ + void bind(); + + /** + * @brief Unbind G-Buffer + */ + void unbind(); + + /** + * @brief Clear all buffers + */ + void clear(); + + /** + * @brief Bind texture for reading + * @param index Texture index (0=position, 1=normal, 2=albedo, etc.) + * @param texture_unit Texture unit to bind to + */ + void bind_texture(int index, int texture_unit); + + // Texture getters + uint32_t get_position_texture() const { return position_texture_; } + uint32_t get_normal_texture() const { return normal_texture_; } + uint32_t get_albedo_texture() const { return albedo_texture_; } + uint32_t get_material_texture() const { return material_texture_; } + uint32_t get_depth_texture() const { return depth_texture_; } + + // Dimensions + int get_width() const { return width_; } + int get_height() const { return height_; } + + /** + * @brief Read pixel data from G-Buffer + * @param index Buffer index + * @param data Output data pointer + */ + void read_pixels(int index, void* data); + +private: + void create_textures(); + void delete_textures(); + void create_framebuffer(); + + uint32_t fbo_; ///< Framebuffer object + uint32_t rbo_depth_; ///< Depth renderbuffer + + // G-Buffer textures + uint32_t position_texture_; ///< World position (RGB16F) + uint32_t normal_texture_; ///< World normal (RGB16F) + uint32_t albedo_texture_; ///< Albedo + Metallic (RGBA8) + uint32_t material_texture_; ///< Roughness + AO (RG8) + uint32_t depth_texture_; ///< Depth (R32F) + + int width_; ///< Buffer width + int height_; ///< Buffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_GBUFFER_H +``` + +### 文件:include/are/rasterizer/rasterizer.h +```cpp +/** + * @file rasterizer.h + * @brief Rasterization pipeline for G-Buffer generation + */ + +#ifndef ARE_INCLUDE_RASTERIZER_RASTERIZER_H +#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H + +#include +#include +#include + +namespace are { + +// Forward declarations +class GBuffer; +class ShaderProgram; +class SceneManager; +class Camera; +class Mesh; + +/** + * @class Rasterizer + * @brief OpenGL rasterization pipeline + * + * Renders scene geometry to G-Buffer using traditional rasterization. + */ +class Rasterizer { +public: + /** + * @brief Constructor + * @param width Framebuffer width + * @param height Framebuffer height + */ + Rasterizer(int width, int height); + + /** + * @brief Destructor + */ + ~Rasterizer(); + + /** + * @brief Resize framebuffer + * @param width New width + * @param height New height + */ + void resize(int width, int height); + + /** + * @brief Render scene to G-Buffer + * @param scene Scene manager + * @param camera Camera + */ + void render_gbuffer(const SceneManager& scene, const Camera& camera); + + /** + * @brief Get G-Buffer + * @return G-Buffer reference + */ + GBuffer& get_gbuffer(); + const GBuffer& get_gbuffer() const; + + /** + * @brief Upload mesh data to GPU + * @param mesh Mesh to upload + */ + void upload_mesh(Mesh& mesh); + + /** + * @brief Delete mesh GPU resources + * @param mesh Mesh to delete + */ + void delete_mesh(Mesh& mesh); + +private: + void initialize_shaders(const std::string& shader_dir); + void setup_mesh_buffers(Mesh& mesh); + + std::unique_ptr gbuffer_; ///< G-Buffer + std::unique_ptr gbuffer_shader_; ///< G-Buffer shader + + int width_; ///< Framebuffer width + int height_; ///< Framebuffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RASTERIZER_RASTERIZER_H +``` + +如果没有还需要我给出的内容的话,你就可以开始实现了。 diff --git a/phase4.md b/phase4.md new file mode 100644 index 0000000..1d5ae67 --- /dev/null +++ b/phase4.md @@ -0,0 +1,270 @@ +很好!让我们来实现Phase 4吧! +如下是我已经实现的Phase 4头文件: +### 文件:include/are/acceleration/bvh_node.h +```cpp +/** + * @file bvh_node.h + * @brief BVH node structure + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_NODE_H +#define ARE_INCLUDE_ACCELERATION_BVH_NODE_H + +#include +#include + +namespace are { + +/** + * @struct BVHNode + * @brief Node in Bounding Volume Hierarchy + * + * Uses a compact representation for efficient GPU transfer. + */ +struct BVHNode { + AABB bounds_; ///< Node bounding box + + union { + uint32_t left_child_; ///< Left child index (internal node) + uint32_t first_primitive_; ///< First primitive index (leaf node) + }; + + union { + uint32_t right_child_; ///< Right child index (internal node) + uint32_t primitive_count_; ///< Number of primitives (leaf node) + }; + + /** + * @brief Check if node is a leaf + * @return true if leaf node + */ + bool is_leaf() const { + return primitive_count_ > 0; + } + + /** + * @brief Get node surface area (for SAH) + * @return Surface area + */ + Real surface_area() const { + return bounds_.surface_area(); + } +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_NODE_H +``` + +### 文件:include/are/acceleration/bvh_builder.h +```cpp +/** + * @file bvh_builder.h + * @brief BVH construction algorithms + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H +#define ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @enum BVHSplitMethod + * @brief BVH splitting strategies + */ +enum class BVHSplitMethod { + ARE_BVH_SPLIT_MIDDLE, ///< Split at midpoint + ARE_BVH_SPLIT_SAH ///< Surface Area Heuristic +}; + +/** + * @struct BVHBuildConfig + * @brief Configuration for BVH construction + */ +struct BVHBuildConfig { + BVHSplitMethod split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH; + int max_leaf_size_ = 4; ///< Maximum triangles per leaf + int max_depth_ = 64; ///< Maximum tree depth + bool use_multithreading_ = true; ///< Use parallel construction +}; + +/** + * @class BVHBuilder + * @brief Constructs BVH from triangle list + */ +class BVHBuilder { +public: + /** + * @brief Constructor + * @param config Build configuration + */ + explicit BVHBuilder(const BVHBuildConfig& config = BVHBuildConfig()); + + /** + * @brief Build BVH from triangles + * @param triangles Triangle list + * @param nodes Output node list + * @param primitive_indices Output primitive index list + * @return Root node index + */ + uint32_t build(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices); + + /** + * @brief Get build statistics + * @param node_count Output node count + * @param leaf_count Output leaf count + * @param max_depth Output maximum depth reached + */ + void get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const; + +private: + struct BuildEntry { + uint32_t parent_; + uint32_t start_; + uint32_t end_; + int depth_; + }; + + uint32_t build_recursive(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices, + uint32_t start, uint32_t end, int depth); + + int find_best_split_axis(const std::vector& triangles, + const std::vector& indices, + uint32_t start, uint32_t end); + + Real compute_sah_cost(const AABB& bounds, uint32_t count); + + BVHBuildConfig config_; + size_t node_count_; + size_t leaf_count_; + int max_depth_reached_; +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H +``` + +### 文件:include/are/acceleration/bvh.h +```cpp +/** + * @file bvh.h + * @brief BVH interface and traversal + */ + +#ifndef ARE_INCLUDE_ACCELERATION_BVH_H +#define ARE_INCLUDE_ACCELERATION_BVH_H + +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +/** + * @class BVH + * @brief Bounding Volume Hierarchy for ray tracing acceleration + */ +class BVH { +public: + /** + * @brief Constructor + */ + BVH(); + + /** + * @brief Destructor + */ + ~BVH(); + + /** + * @brief Build BVH from triangle list + * @param triangles Triangle list + * @param config Build configuration + * @return true if build succeeded + */ + bool build(const std::vector& triangles, + const BVHBuildConfig& config = BVHBuildConfig()); + + /** + * @brief Traverse BVH and find closest intersection + * @param ray Ray to trace + * @param hit Output hit record + * @return true if intersection found + */ + bool intersect(const Ray& ray, HitRecord& hit) const; + + /** + * @brief Fast occlusion test (any hit) + * @param ray Ray to trace + * @param t_max Maximum t value + * @return true if any intersection found + */ + bool intersect_any(const Ray& ray, Real t_max) const; + + /** + * @brief Check if BVH is built + * @return true if built + */ + bool is_built() const { return !nodes_.empty(); } + + /** + * @brief Get BVH nodes (for GPU upload) + * @return Node array + */ + const std::vector& get_nodes() const { return nodes_; } + + /** + * @brief Get primitive indices + * @return Index array + */ + const std::vector& get_primitive_indices() const { + return primitive_indices_; + } + + /** + * @brief Get triangles + * @return Triangle array + */ + const std::vector& get_triangles() const { return triangles_; } + + /** + * @brief Get memory usage in bytes + * @return Memory usage + */ + size_t get_memory_usage() const; + + /** + * @brief Clear BVH data + */ + void clear(); + +private: + bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const; + bool intersect_any_recursive(uint32_t node_index, const Ray& ray, Real t_max) const; + + std::vector nodes_; ///< BVH nodes + std::vector primitive_indices_; ///< Primitive index array + std::vector triangles_; ///< Triangle data + uint32_t root_index_; ///< Root node index +}; + +} // namespace are + +#endif // ARE_INCLUDE_ACCELERATION_BVH_H +``` + +如果有依赖或者需要给你的头文件或实现文件我还没有给你,欢迎提出! diff --git a/phase5.md b/phase5.md new file mode 100644 index 0000000..f79c9bc --- /dev/null +++ b/phase5.md @@ -0,0 +1,310 @@ +很好!现在让我们开始Phase 5的实现吧!如下是Phase 5我已经写好的头文件与依赖文件(注:ray.h/cpp&hit_record.h/cpp在前面的代码中你已经实现了,现在只需要再检查一遍之前的实现是否正确即可): +### 文件:include/are/raytracer/cpu_raytracer.h +```cpp +/** + * @file cpu_raytracer.h + * @brief CPU-based ray tracing implementation + */ + +#ifndef ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H +#define ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H + +#include +#include +#include +#include + +namespace are { + +/** + * @class CPURayTracer + * @brief CPU-based ray tracing implementation + * + * Uses multithreading for parallel ray tracing on CPU. + */ +class CPURayTracer : public RayTracer { +public: + /** + * @brief Constructor + * @param config Ray tracing configuration + */ + explicit CPURayTracer(const RayTracingConfig& config); + + /** + * @brief Destructor + */ + ~CPURayTracer() override; + + /** + * @brief Render scene using CPU ray tracing + * @param scene Scene manager + * @param camera Camera + * @param gbuffer G-Buffer (optional) + * @param output Output texture ID + */ + void render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) override; + + /** + * @brief Update BVH + * @param bvh BVH reference + */ + void update_bvh(const BVH& bvh) override; + +private: + /** + * @brief Trace a single ray + * @param ray Ray to trace + * @param depth Current recursion depth + * @return Ray color + */ + Vec3 trace_ray(const Ray& ray, int depth); + + /** + * @brief Shade hit point + * @param hit Hit record + * @param ray Incident ray + * @param depth Current recursion depth + * @return Shaded color + */ + Vec3 shade(const HitRecord& hit, const Ray& ray, int depth); + + /** + * @brief Compute direct lighting + * @param hit Hit record + * @return Direct lighting contribution + */ + Vec3 compute_direct_lighting(const HitRecord& hit); + + /** + * @brief Compute ambient occlusion + * @param hit Hit record + * @return AO factor [0, 1] + */ + Real compute_ambient_occlusion(const HitRecord& hit); + + /** + * @brief Check shadow ray + * @param origin Shadow ray origin + * @param direction Shadow ray direction + * @param max_distance Maximum distance + * @return true if in shadow + */ + bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance); + + const BVH* bvh_; ///< BVH reference + const SceneManager* scene_; ///< Scene reference + std::vector framebuffer_; ///< CPU framebuffer (HDR) + int width_; ///< Framebuffer width + int height_; ///< Framebuffer height +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H +``` + +### 文件:include/are/raytracer/raytracer.h +```cpp +/** + * @file raytracer.h + * @brief Ray tracing interface + */ + +#ifndef ARE_INCLUDE_RAYTRACER_RAYTRACER_H +#define ARE_INCLUDE_RAYTRACER_RAYTRACER_H + +#include +#include + +namespace are { + +// Forward declarations +class SceneManager; +class Camera; +class GBuffer; +class BVH; + +/** + * @class RayTracer + * @brief Abstract ray tracing interface + * + * Base class for CPU and GPU ray tracing implementations. + */ +class RayTracer { +public: + /** + * @brief Constructor + * @param config Ray tracing configuration + */ + explicit RayTracer(const RayTracingConfig& config); + + /** + * @brief Virtual destructor + */ + virtual ~RayTracer() = default; + + /** + * @brief Render scene using ray tracing + * @param scene Scene manager + * @param camera Camera + * @param gbuffer G-Buffer (optional, for hybrid rendering) + * @param output Output texture ID + */ + virtual void render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) = 0; + + /** + * @brief Update BVH + * @param bvh BVH reference + */ + virtual void update_bvh(const BVH& bvh) = 0; + + /** + * @brief Set configuration + * @param config New configuration + */ + virtual void set_config(const RayTracingConfig& config); + + /** + * @brief Get configuration + * @return Current configuration + */ + const RayTracingConfig& get_config() const { return config_; } + +protected: + RayTracingConfig config_; ///< Ray tracing configuration +}; + +} // namespace are +#endif // ARE_INCLUDE_RAYTRACER_RAYTRACER_H +``` +此外,你可能需要随机数模块。 +### 文件:include/utils/random.h +```cpp +/** + * @file random.h + * @brief Random number generation utilities + */ + +#ifndef ARE_INCLUDE_UTILS_RANDOM_H +#define ARE_INCLUDE_UTILS_RANDOM_H + +#include +#include + +namespace are { + +/** + * @class RandomGenerator + * @brief Thread-safe random number generator + * + * Uses PCG (Permuted Congruential Generator) for high-quality random numbers. + */ +class RandomGenerator { +public: + /** + * @brief Constructor with optional seed + * @param seed Random seed (0 = use random device) + */ + explicit RandomGenerator(uint64_t seed = 0); + + /** + * @brief Generate random float in [0, 1) + * @return Random float + */ + Real random_float(); + + /** + * @brief Generate random float in [min, max) + * @param min Minimum value + * @param max Maximum value + * @return Random float + */ + Real random_float(Real min, Real max); + + /** + * @brief Generate random integer in [min, max] + * @param min Minimum value + * @param max Maximum value + * @return Random integer + */ + int random_int(int min, int max); + + /** + * @brief Generate random point in unit disk + * @return Random point (z = 0) + */ + Vec3 random_in_unit_disk(); + + /** + * @brief Generate random point in unit sphere + * @return Random point + */ + Vec3 random_in_unit_sphere(); + + /** + * @brief Generate random unit vector + * @return Random unit vector + */ + Vec3 random_unit_vector(); + + /** + * @brief Generate random vector in hemisphere + * @param normal Hemisphere normal + * @return Random vector in hemisphere + */ + Vec3 random_in_hemisphere(const Vec3 &normal); + + /** + * @brief Generate random cosine-weighted direction + * @param normal Surface normal + * @return Random direction (cosine-weighted) + */ + Vec3 random_cosine_direction(const Vec3 &normal); + + /** + * @brief Set seed for reproducible results + * @param seed Random seed + */ + void set_seed(uint64_t seed); + +private: + std::mt19937_64 rng_; ///< Random number generator + std::uniform_real_distribution dist_; ///< Uniform distribution [0, 1) +}; + +/** + * @brief Get thread-local random generator + * @return Reference to thread-local generator + */ +RandomGenerator &get_thread_random(); + +/** + * @brief Generate random float in [0, 1) using thread-local generator + * @return Random float + */ +inline Real random_float() { + return get_thread_random().random_float(); +} + +/** + * @brief Generate random float in [min, max) using thread-local generator + * @param min Minimum value + * @param max Maximum value + * @return Random float + */ +inline Real random_float(Real min, Real max) { + return get_thread_random().random_float(min, max); +} + +} // namespace are + +#endif // ARE_INCLUDE_UTILS_RANDOM_H +``` + +同样地,如果有依赖或者需要给你的头文件/实现文件还没有给你,欢迎提出;如果已有的数学或者随机数模块需要补充,请分别给出函数声明与实现。 diff --git a/request2.md b/request2.md new file mode 100644 index 0000000..7c7cedf --- /dev/null +++ b/request2.md @@ -0,0 +1,322 @@ +你对代码的观察非常敏锐!我对你的实现计划表示认同,下面是你要求到的和可能用到的头文件: +### 文件:include/are/raytracer/ray.h +```cpp +/** + * @file ray.h + * @brief Ray structure for ray tracing + */ + +#ifndef ARE_INCLUDE_RAYTRACER_RAY_H +#define ARE_INCLUDE_RAYTRACER_RAY_H + +#include + +namespace are { + +/** + * @struct Ray + * @brief Ray representation for ray tracing + */ +struct Ray { + Vec3 origin_; ///< Ray origin + Vec3 direction_; ///< Ray direction (normalized) + Real t_min_; ///< Minimum t value + Real t_max_; ///< Maximum t value + + /** + * @brief Default constructor + */ + Ray(); + + /** + * @brief Construct ray with origin and direction + * @param origin Ray origin + * @param direction Ray direction (will be normalized) + * @param t_min Minimum t value + * @param t_max Maximum t value + */ + Ray(const Vec3& origin, const Vec3& direction, + Real t_min = are_epsilon, Real t_max = 1e30f); + + /** + * @brief Evaluate ray at parameter t + * @param t Parameter value + * @return Point on ray + */ + Vec3 at(Real t) const; + + /** + * @brief Check if t is within valid range + * @param t Parameter value + * @return true if t is valid + */ + bool is_valid_t(Real t) const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_RAY_H +``` + +### 文件:include/are/raytracer/hit_record.h +```cpp +/** + * @file hit_record.h + * @brief Ray-surface intersection record + */ + +#ifndef ARE_INCLUDE_RAYTRACER_HIT_RECORD_H +#define ARE_INCLUDE_RAYTRACER_HIT_RECORD_H + +#include + +namespace are { + +/** + * @struct HitRecord + * @brief Information about ray-surface intersection + */ +struct HitRecord { + Vec3 position_; ///< Hit position in world space + Vec3 normal_; ///< Surface normal at hit point + Vec2 texcoord_; ///< Texture coordinates at hit point + Vec3 tangent_; ///< Tangent vector at hit point + Real t_; ///< Ray parameter at hit point + MaterialHandle material_; ///< Material at hit point + uint32_t triangle_index_; ///< Triangle index that was hit + bool front_face_; ///< Whether ray hit front face + + /** + * @brief Default constructor + */ + HitRecord(); + + /** + * @brief Set face normal based on ray direction + * @param ray_direction Ray direction + * @param outward_normal Outward-facing normal + */ + void set_face_normal(const Vec3& ray_direction, const Vec3& outward_normal); + + /** + * @brief Check if hit record is valid + * @return true if hit occurred + */ + bool is_valid() const; +}; + +} // namespace are + +#endif // ARE_INCLUDE_RAYTRACER_HIT_RECORD_H +``` + +### 文件:include/are/scene/directional_light.h +```cpp +/** + * @file directional_light.h + * @brief Directional light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H +#define ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H + +#include + +namespace are { + +/** + * @class DirectionalLight + * @brief Directional light source (sun-like) + * + * Represents an infinitely distant light source with parallel rays. + */ +class DirectionalLight : public Light { +public: + /** + * @brief Default constructor + */ + DirectionalLight(); + + /** + * @brief Construct with direction and color + * @param direction Light direction (will be normalized) + * @param color Light color + * @param intensity Light intensity + */ + DirectionalLight(const Vec3& direction, const Vec3& color = Vec3(1.0f), + Real intensity = 1.0f); + + // Direction + void set_direction(const Vec3& direction); + const Vec3& get_direction() const { return direction_; } + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 direction_; ///< Light direction (normalized) +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H +``` + +### 文件:include/are/scene/point_light.h +```cpp +/** + * @file point_light.h + * @brief Point light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_POINT_LIGHT_H +#define ARE_INCLUDE_SCENE_POINT_LIGHT_H + +#include + +namespace are { + +/** + * @class PointLight + * @brief Point light source + * + * Emits light equally in all directions from a single point. + */ +class PointLight : public Light { +public: + /** + * @brief Default constructor + */ + PointLight(); + + /** + * @brief Construct with position and color + * @param position Light position + * @param color Light color + * @param intensity Light intensity + * @param range Light range (attenuation distance) + */ + PointLight(const Vec3& position, const Vec3& color = Vec3(1.0f), + Real intensity = 1.0f, Real range = 10.0f); + + // Position + void set_position(const Vec3& position); + const Vec3& get_position() const { return position_; } + + // Range (attenuation) + void set_range(Real range); + Real get_range() const { return range_; } + + // Attenuation parameters + void set_attenuation(Real constant, Real linear, Real quadratic); + Real get_constant_attenuation() const { return attenuation_constant_; } + Real get_linear_attenuation() const { return attenuation_linear_; } + Real get_quadratic_attenuation() const { return attenuation_quadratic_; } + + /** + * @brief Calculate attenuation at given distance + * @param distance Distance from light + * @return Attenuation factor [0, 1] + */ + Real calculate_attenuation(Real distance) const; + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 position_; ///< Light position + Real range_; ///< Light range + Real attenuation_constant_; ///< Constant attenuation factor + Real attenuation_linear_; ///< Linear attenuation factor + Real attenuation_quadratic_; ///< Quadratic attenuation factor +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_POINT_LIGHT_H +``` + +### 文件:include/are/scene/spot_light.h +```cpp +/** + * @file spot_light.h + * @brief Spot light implementation + */ + +#ifndef ARE_INCLUDE_SCENE_SPOT_LIGHT_H +#define ARE_INCLUDE_SCENE_SPOT_LIGHT_H + +#include + +namespace are { + +/** + * @class SpotLight + * @brief Spot light source + * + * Emits light in a cone from a single point. + */ +class SpotLight : public Light { +public: + /** + * @brief Default constructor + */ + SpotLight(); + + /** + * @brief Construct with position, direction, and angles + * @param position Light position + * @param direction Light direction + * @param inner_angle Inner cone angle in degrees + * @param outer_angle Outer cone angle in degrees + * @param color Light color + * @param intensity Light intensity + */ + SpotLight(const Vec3& position, const Vec3& direction,Real inner_angle, Real outer_angle, + const Vec3& color = Vec3(1.0f), Real intensity = 1.0f); + + // Position and direction + void set_position(const Vec3& position); + void set_direction(const Vec3& direction); + const Vec3& get_position() const { return position_; } + const Vec3& get_direction() const { return direction_; } + + // Cone angles (in degrees) + void set_inner_angle(Real angle); + void set_outer_angle(Real angle); + Real get_inner_angle() const { return inner_angle_; } + Real get_outer_angle() const { return outer_angle_; } + + // Range + void set_range(Real range); + Real get_range() const { return range_; } + + /** + * @brief Calculate spotlight intensity at given direction + * @param to_point Direction from light to point (normalized) + * @return Spotlight factor [0, 1] + */ + Real calculate_spot_factor(const Vec3& to_point) const; + + // Light interface + LightData pack() const override; + bool affects_point(const Vec3& point) const override; + +private: + Vec3 position_; ///< Light position + Vec3 direction_; ///< Light direction (normalized) + Real inner_angle_; ///< Inner cone angle (degrees) + Real outer_angle_; ///< Outer cone angle (degrees) + Real range_; ///< Light range + Real cos_inner_; ///< Cosine of inner angle (cache + Real cos_outer_; ///< Cosine of outer angle (cached) +}; + +} // namespace are + +#endif // ARE_INCLUDE_SCENE_SPOT_LIGHT_H +``` + +还有缺失的头文件需要补充吗?如果没有的话,我们就可以开始实现Phase 2了。 diff --git a/shaders/gbuffer/gbuffer.frag b/shaders/gbuffer/gbuffer.frag new file mode 100644 index 0000000..f8dbe90 --- /dev/null +++ b/shaders/gbuffer/gbuffer.frag @@ -0,0 +1,33 @@ +#version 430 core + +// Inputs from vertex shader +in vec3 v_world_position; +in vec3 v_world_normal; +in vec2 v_texcoord; +in vec3 v_world_tangent; + +// Material uniforms +uniform vec3 u_albedo; +uniform float u_metallic; +uniform float u_roughness; + +// G-Buffer outputs +layout(location = 0) out vec3 g_position; +layout(location = 1) out vec3 g_normal; +layout(location = 2) out vec4 g_albedo_metallic; +layout(location = 3) out vec2 g_roughness_ao; + +void main() { + // Output world position + g_position = v_world_position; + + // Output normalized world normal + g_normal = normalize(v_world_normal); + + // Output albedo (RGB) and metallic (A) + g_albedo_metallic = vec4(u_albedo, u_metallic); + + // Output roughness (R) and ambient occlusion (G) + // AO is set to 1.0 by default (no occlusion) + g_roughness_ao = vec2(u_roughness, 1.0); +} diff --git a/shaders/gbuffer/gbuffer.vert b/shaders/gbuffer/gbuffer.vert new file mode 100644 index 0000000..a280ef5 --- /dev/null +++ b/shaders/gbuffer/gbuffer.vert @@ -0,0 +1,35 @@ +#version 430 core + +// Vertex attributes +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec3 a_normal; +layout(location = 2) in vec2 a_texcoord; +layout(location = 3) in vec3 a_tangent; + +// Uniforms +uniform mat4 u_model; +uniform mat4 u_view; +uniform mat4 u_projection; +uniform mat3 u_normal_matrix; + +// Outputs to fragment shader +out vec3 v_world_position; +out vec3 v_world_normal; +out vec2 v_texcoord; +out vec3 v_world_tangent; + +void main() { + // Transform position to world space + vec4 world_pos = u_model * vec4(a_position, 1.0); + v_world_position = world_pos.xyz; + + // Transform normal and tangent to world space + v_world_normal = normalize(u_normal_matrix * a_normal); + v_world_tangent = normalize(u_normal_matrix * a_tangent); + + // Pass through texture coordinates + v_texcoord = a_texcoord; + + // Transform to clip space + gl_Position = u_projection * u_view * world_pos; +} diff --git a/src/acceleration/bvh.cpp b/src/acceleration/bvh.cpp new file mode 100644 index 0000000..59c3a68 --- /dev/null +++ b/src/acceleration/bvh.cpp @@ -0,0 +1,464 @@ +/** + * @file bvh.cpp + * @brief Implementation of BVH class (optimized version) + */ + +#include +#include +#include +#include + +namespace are { + +BVH::BVH() + : root_index_(0) { +} + +BVH::~BVH() { + clear(); +} + +bool BVH::build(const std::vector &triangles, const BVHBuildConfig &config) { + ARE_PROFILE_FUNCTION(); + + if (triangles.empty()) { + ARE_LOG_WARN("BVH: Cannot build from empty triangle list"); + return false; + } + + // Clear existing data + clear(); + + // Copy triangles + triangles_ = triangles; + + // Build BVH + BVHBuilder builder(config); + root_index_ = builder.build(triangles_, nodes_, primitive_indices_); + + // Get statistics + size_t node_count, leaf_count; + int max_depth; + builder.get_stats(node_count, leaf_count, max_depth); + + ARE_LOG_INFO("BVH: Built successfully"); + ARE_LOG_INFO(" Triangles: " + std::to_string(triangles_.size())); + ARE_LOG_INFO(" Nodes: " + std::to_string(node_count)); + ARE_LOG_INFO(" Leaves: " + std::to_string(leaf_count)); + ARE_LOG_INFO(" Max depth: " + std::to_string(max_depth)); + ARE_LOG_INFO(" Memory: " + std::to_string(get_memory_usage() / 1024) + " KB"); + + return true; +} + +bool BVH::intersect(const Ray &ray, HitRecord &hit) const { + // Note: No profiling here - this is a hot path + + if (!is_built()) { + return false; + } + + // Use iterative traversal with stack for better performance + return intersect_iterative(ray, hit); +} + +bool BVH::intersect_any(const Ray &ray, Real t_max) const { + // Note: No profiling here - this is a hot path + + if (!is_built()) { + return false; + } + + return intersect_any_iterative(ray, t_max); +} + +size_t BVH::get_memory_usage() const { + size_t total = 0; + total += nodes_.size() * sizeof(BVHNode); + total += primitive_indices_.size() * sizeof(uint32_t); + total += triangles_.size() * sizeof(Triangle); + return total; +} + +void BVH::clear() { + nodes_.clear(); + primitive_indices_.clear(); + triangles_.clear(); + root_index_ = 0; +} + +bool BVH::intersect_iterative(const Ray &ray, HitRecord &hit) const { + // Precompute inverse direction for faster AABB tests + Vec3 inv_dir( + 1.0f / ray.direction_.x, + 1.0f / ray.direction_.y, + 1.0f / ray.direction_.z); + + // Stack-based traversal (64 levels is enough for most scenes) + uint32_t stack[64]; + int stack_ptr = 0; + stack[stack_ptr++] = root_index_; + + bool hit_anything = false; + Real closest_t = ray.t_max_; + + while (stack_ptr > 0) { + uint32_t node_index = stack[--stack_ptr]; + + if (node_index >= nodes_.size()) { + continue; + } + + const BVHNode &node = nodes_[node_index]; + + // Fast AABB test with precomputed inverse direction + Real t_min, t_max; + if (!intersect_aabb_fast(node.bounds_, ray, inv_dir, closest_t, t_min, t_max)) { + continue; + } + + if (node.is_leaf()) { + // Test all primitives in leaf + for (uint32_t i = 0; i < node.primitive_count_; ++i) { + uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i]; + + if (prim_idx >= triangles_.size()) { + continue; + } + + const Triangle &triangle = triangles_[prim_idx]; + + HitRecord temp_hit; + if (intersect_triangle_fast(triangle, ray, closest_t, temp_hit)) { + closest_t = temp_hit.t_; + hit = temp_hit; + hit.triangle_index_ = prim_idx; + hit_anything = true; + } + } + } else { + // Push children to stack (far child first, so near child is processed first) + if (node.left_child_ >= nodes_.size() || node.right_child_ >= nodes_.size()) { + continue; + } + + const BVHNode &left = nodes_[node.left_child_]; + const BVHNode &right = nodes_[node.right_child_]; + + Real t_left_min, t_left_max; + Real t_right_min, t_right_max; + + bool hit_left = intersect_aabb_fast(left.bounds_, ray, inv_dir, closest_t, + t_left_min, t_left_max); + bool hit_right = intersect_aabb_fast(right.bounds_, ray, inv_dir, closest_t, + t_right_min, t_right_max); + + if (hit_left && hit_right) { + // Push far child first (so near child is popped first) + if (t_left_min < t_right_min) { + if (stack_ptr < 64) + stack[stack_ptr++] = node.right_child_; + if (stack_ptr < 64) + stack[stack_ptr++] = node.left_child_; + } else { + if (stack_ptr < 64) + stack[stack_ptr++] = node.left_child_; + if (stack_ptr < 64) + stack[stack_ptr++] = node.right_child_; + } + } else if (hit_left) { + if (stack_ptr < 64) + stack[stack_ptr++] = node.left_child_; + } else if (hit_right) { + if (stack_ptr < 64) + stack[stack_ptr++] = node.right_child_; + } + } + } + + return hit_anything; +} + +bool BVH::intersect_any_iterative(const Ray &ray, Real t_max) const { + // Precompute inverse direction + Vec3 inv_dir( + 1.0f / ray.direction_.x, + 1.0f / ray.direction_.y, + 1.0f / ray.direction_.z); + + // Stack-based traversal + uint32_t stack[64]; + int stack_ptr = 0; + stack[stack_ptr++] = root_index_; + + while (stack_ptr > 0) { + uint32_t node_index = stack[--stack_ptr]; + + if (node_index >= nodes_.size()) { + continue; + } + + const BVHNode &node = nodes_[node_index]; + + // Fast AABB test + Real t_min, t_max_box; + if (!intersect_aabb_fast(node.bounds_, ray, inv_dir, t_max, t_min, t_max_box)) { + continue; + } + + if (node.is_leaf()) { + // Test all primitives in leaf + for (uint32_t i = 0; i < node.primitive_count_; ++i) { + uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i]; + + if (prim_idx >= triangles_.size()) { + continue; + } + + const Triangle &triangle = triangles_[prim_idx]; + + if (triangle.intersect_fast(ray, t_max)) { + return true; // Early exit on first hit + } + } + } else { + // Push both children + if (node.left_child_ < nodes_.size() && stack_ptr < 64) { + stack[stack_ptr++] = node.left_child_; + } + if (node.right_child_ < nodes_.size() && stack_ptr < 64) { + stack[stack_ptr++] = node.right_child_; + } + } + } + + return false; +} + +inline bool BVH::intersect_aabb_fast(const AABB &bounds, const Ray &ray, + const Vec3 &inv_dir, Real t_max, + Real &t_min_out, Real &t_max_out) const { + // Optimized slab method with precomputed inverse direction + Real t_min = ray.t_min_; + Real t_max_local = t_max; + + // X axis + { + Real t0 = (bounds.min_.x - ray.origin_.x) * inv_dir.x; + Real t1 = (bounds.max_.x - ray.origin_.x) * inv_dir.x; + + if (inv_dir.x < 0.0f) { + Real temp = t0; + t0 = t1; + t1 = temp; + } + + t_min = std::max(t_min, t0); + t_max_local = std::min(t_max_local, t1); + + if (t_max_local < t_min) { + return false; + } + } + + // Y axis + { + Real t0 = (bounds.min_.y - ray.origin_.y) * inv_dir.y; + Real t1 = (bounds.max_.y - ray.origin_.y) * inv_dir.y; + + if (inv_dir.y < 0.0f) { + Real temp = t0; + t0 = t1; + t1 = temp; + } + + t_min = std::max(t_min, t0); + t_max_local = std::min(t_max_local, t1); + + if (t_max_local < t_min) { + return false; + } + } + + // Z axis + { + Real t0 = (bounds.min_.z - ray.origin_.z) * inv_dir.z; + Real t1 = (bounds.max_.z - ray.origin_.z) * inv_dir.z; + + if (inv_dir.z < 0.0f) { + Real temp = t0; + t0 = t1; + t1 = temp; + } + + t_min = std::max(t_min, t0); + t_max_local = std::min(t_max_local, t1); + + if (t_max_local < t_min) { + return false; + } + } + + t_min_out = t_min; + t_max_out = t_max_local; + return true; +} + +inline bool BVH::intersect_triangle_fast(const Triangle &triangle, const Ray &ray, + Real t_max, HitRecord &hit) const { + // Möller-Trumbore algorithm (inlined for performance) + const Vec3 &v0 = triangle.v0_.position_; + const Vec3 &v1 = triangle.v1_.position_; + const Vec3 &v2 = triangle.v2_.position_; + + const Vec3 edge1 = v1 - v0; + const Vec3 edge2 = v2 - v0; + + const Vec3 h = glm::cross(ray.direction_, edge2); + const Real a = glm::dot(edge1, h); + + // Check if ray is parallel to triangle + if (a > -are_epsilon && a < are_epsilon) { + return false; + } + + const Real f = 1.0f / a; + const Vec3 s = ray.origin_ - v0; + const Real u = f * glm::dot(s, h); + + if (u < 0.0f || u > 1.0f) { + return false; + } + + const Vec3 q = glm::cross(s, edge1); + const Real v = f * glm::dot(ray.direction_, q); + + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + const Real t = f * glm::dot(edge2, q); + + if (t < ray.t_min_ || t >= t_max) { + return false; + } + + // Fill hit record + const Real w = 1.0f - u - v; + + hit.t_ = t; + hit.position_ = ray.origin_ + ray.direction_ * t; + hit.material_ = triangle.material_; + + // Interpolate vertex attributes + hit.normal_ = glm::normalize( + w * triangle.v0_.normal_ + u * triangle.v1_.normal_ + v * triangle.v2_.normal_); + + hit.texcoord_ = w * triangle.v0_.texcoord_ + u * triangle.v1_.texcoord_ + v * triangle.v2_.texcoord_; + + hit.tangent_ = glm::normalize( + w * triangle.v0_.tangent_ + u * triangle.v1_.tangent_ + v * triangle.v2_.tangent_); + + // Determine front face + hit.set_face_normal(ray.direction_, hit.normal_); + + return true; +} + +// Keep recursive versions for reference/debugging +bool BVH::intersect_recursive(uint32_t node_index, const Ray &ray, HitRecord &hit) const { + if (node_index >= nodes_.size()) { + return false; + } + + const BVHNode &node = nodes_[node_index]; + + Real t_min, t_max; + if (!node.bounds_.intersect_ray(ray, t_min, t_max)) { + return false; + } + + if (t_min > hit.t_) { + return false; + } + + bool hit_anything = false; + + if (node.is_leaf()) { + for (uint32_t i = 0; i < node.primitive_count_; ++i) { + uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i]; + + if (prim_idx >= triangles_.size()) { + continue; + } + + const Triangle &triangle = triangles_[prim_idx]; + HitRecord temp_hit; + + if (triangle.intersect(ray, temp_hit) && temp_hit.t_ < hit.t_) { + hit = temp_hit; + hit.triangle_index_ = prim_idx; + hit_anything = true; + } + } + } else { + Real t_left_min, t_left_max; + Real t_right_min, t_right_max; + + bool hit_left = nodes_[node.left_child_].bounds_.intersect_ray(ray, t_left_min, t_left_max); + bool hit_right = nodes_[node.right_child_].bounds_.intersect_ray(ray, t_right_min, t_right_max); + + // Traverse closer child first + if (hit_left && hit_right) { + if (t_left_min < t_right_min) { + hit_anything |= intersect_recursive(node.left_child_, ray, hit); + hit_anything |= intersect_recursive(node.right_child_, ray, hit); + } else { + hit_anything |= intersect_recursive(node.right_child_, ray, hit); + hit_anything |= intersect_recursive(node.left_child_, ray, hit); + } + } else if (hit_left) { + hit_anything |= intersect_recursive(node.left_child_, ray, hit); + } else if (hit_right) { + hit_anything |= intersect_recursive(node.right_child_, ray, hit); + } + } + + return hit_anything; +} + +bool BVH::intersect_any_recursive(uint32_t node_index, const Ray &ray, Real t_max) const { + if (node_index >= nodes_.size()) { + return false; + } + + const BVHNode &node = nodes_[node_index]; + + Real t_min, t_max_box; + if (!node.bounds_.intersect_ray(ray, t_min, t_max_box)) { + return false; + } + + if (t_min > t_max) { + return false; + } + + if (node.is_leaf()) { + for (uint32_t i = 0; i < node.primitive_count_; ++i) { + uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i]; + + if (prim_idx >= triangles_.size()) { + continue; + } + + if (triangles_[prim_idx].intersect_fast(ray, t_max)) { + return true; + } + } + return false; + } else { + return intersect_any_recursive(node.left_child_, ray, t_max) || intersect_any_recursive(node.right_child_, ray, t_max); + } +} + +} // namespace are diff --git a/src/acceleration/bvh_builder.cpp b/src/acceleration/bvh_builder.cpp new file mode 100644 index 0000000..5eeb71c --- /dev/null +++ b/src/acceleration/bvh_builder.cpp @@ -0,0 +1,198 @@ +/** + * @file bvh_builder.cpp + * @brief Implementation of BVH construction algorithms + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ARE_USE_OPENMP +#include +#endif + +namespace are { + +BVHBuilder::BVHBuilder(const BVHBuildConfig& config) + : config_(config) + , node_count_(0) + , leaf_count_(0) + , max_depth_reached_(0) { +} + +uint32_t BVHBuilder::build(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices) { + ARE_PROFILE_FUNCTION(); + + if (triangles.empty()) { + ARE_LOG_WARN("BVHBuilder: Cannot build BVH from empty triangle list"); + return 0; + } + + ARE_LOG_INFO("BVHBuilder: Building BVH for " + std::to_string(triangles.size()) + " triangles"); + + // Reset statistics + node_count_ = 0; + leaf_count_ = 0; + max_depth_reached_ = 0; + + // Initialize primitive indices + primitive_indices.resize(triangles.size()); + for (size_t i = 0; i < triangles.size(); ++i) { + primitive_indices[i] = static_cast(i); + } + + // Reserve space for nodes (estimate: 2 * num_triangles) + nodes.clear(); + nodes.reserve(triangles.size() * 2); + + // Build BVH recursively + uint32_t root_index = build_recursive(triangles, nodes, primitive_indices, + 0, static_cast(triangles.size()), 0); + + ARE_LOG_INFO("BVHBuilder: Built BVH with " + std::to_string(node_count_) + " nodes, " + + std::to_string(leaf_count_) + " leaves, max depth " + + std::to_string(max_depth_reached_)); + + return root_index; +} + +void BVHBuilder::get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const { + node_count = node_count_; + leaf_count = leaf_count_; + max_depth = max_depth_reached_; +} + +uint32_t BVHBuilder::build_recursive(const std::vector& triangles, + std::vector& nodes, + std::vector& primitive_indices, + uint32_t start, uint32_t end, int depth) { + ARE_PROFILE_FUNCTION(); + + // Update statistics + max_depth_reached_ = std::max(max_depth_reached_, depth); + + // Create new node + uint32_t node_index = static_cast(nodes.size()); + nodes.emplace_back(); + BVHNode& node = nodes[node_index]; + node_count_++; + + // Compute bounding box for all primitives in range + node.bounds_ = AABB::invalid(); + for (uint32_t i = start; i < end; ++i) { + uint32_t prim_idx = primitive_indices[i]; + node.bounds_.expand(triangles[prim_idx].compute_aabb()); + } + + uint32_t count = end - start; + + // Check if we should create a leaf + bool should_create_leaf = (count <= static_cast(config_.max_leaf_size_)) || + (depth >= config_.max_depth_); + + if (should_create_leaf) { + // Create leaf node + node.first_primitive_ = start; + node.primitive_count_ = count; + leaf_count_++; + return node_index; + } + + // Find best split axis + int split_axis = find_best_split_axis(triangles, primitive_indices, start, end); + + // Sort primitives along split axis + std::sort(primitive_indices.begin() + start, + primitive_indices.begin() + end, + [&](uint32_t a, uint32_t b) { + return triangles[a].centroid()[split_axis] < + triangles[b].centroid()[split_axis]; + }); + + // Find split position + uint32_t mid = start + count / 2; + + // Use SAH if enabled + if (config_.split_method_ == BVHSplitMethod::ARE_BVH_SPLIT_SAH) { + Real best_cost = std::numeric_limits::max(); + uint32_t best_split = mid; + + // Try different split positions + const int num_buckets = 12; + for (int i = 1; i < num_buckets; ++i) { + uint32_t test_split = start + (count * i) / num_buckets; + + // Compute bounding boxes for left and right + AABB left_bounds = AABB::invalid(); + AABB right_bounds = AABB::invalid(); + + for (uint32_t j = start; j < test_split; ++j) { + left_bounds.expand(triangles[primitive_indices[j]].compute_aabb()); + } + for (uint32_t j = test_split; j < end; ++j) { + right_bounds.expand(triangles[primitive_indices[j]].compute_aabb()); + } + + // Compute SAH cost + Real left_cost = compute_sah_cost(left_bounds, test_split - start); + Real right_cost = compute_sah_cost(right_bounds, end - test_split); + Real cost = left_cost + right_cost; + + if (cost < best_cost) { + best_cost = cost; + best_split = test_split; + } + } + + mid = best_split; + } + + // Ensure we don't create empty children + if (mid == start || mid == end) { + mid = start + count / 2; + } + + // Create internal node + node.primitive_count_ = 0; // Mark as internal node + + // Build left and right children + uint32_t left_child = build_recursive(triangles, nodes, primitive_indices, + start, mid, depth + 1); + uint32_t right_child = build_recursive(triangles, nodes, primitive_indices, + mid, end, depth + 1); + + // Update node (it may have been reallocated) + nodes[node_index].left_child_ = left_child; + nodes[node_index].right_child_ = right_child; + + return node_index; +} + +int BVHBuilder::find_best_split_axis(const std::vector& triangles, + const std::vector& indices, + uint32_t start, uint32_t end) { + ARE_PROFILE_FUNCTION(); + + // Compute centroid bounds + AABB centroid_bounds = AABB::invalid(); + for (uint32_t i = start; i < end; ++i) { + centroid_bounds.expand(triangles[indices[i]].centroid()); + } + + // Return longest axis + return centroid_bounds.longest_axis(); +} + +Real BVHBuilder::compute_sah_cost(const AABB& bounds, uint32_t count) { + // SAH cost = surface_area * primitive_count + // This is a simplified version; full SAH includes traversal cost + return bounds.surface_area() * static_cast(count); +} + +} // namespace are diff --git a/src/acceleration/bvh_node.cpp b/src/acceleration/bvh_node.cpp new file mode 100644 index 0000000..f536ebb --- /dev/null +++ b/src/acceleration/bvh_node.cpp @@ -0,0 +1,13 @@ +/** + * @file bvh_node.cpp + * @brief Implementation of BVHNode structure + */ + +#include + +namespace are { + +// BVHNode is a POD structure, no implementation needed +// All methods are inline in the header + +} // namespace are diff --git a/src/geometry/aabb.cpp b/src/geometry/aabb.cpp index a19a3b4..507a94c 100644 --- a/src/geometry/aabb.cpp +++ b/src/geometry/aabb.cpp @@ -1,18 +1,20 @@ /** * @file aabb.cpp - * @brief Implementation of axis-aligned bounding box + * @brief Implementation of Axis-Aligned Bounding Box */ #include #include -#include +#include +#include #include +#include namespace are { -AABB::AABB() - : min_(std::numeric_limits::max()) - , max_(std::numeric_limits::lowest()) { +AABB::AABB() + : min_(std::numeric_limits::max()) + , max_(std::numeric_limits::lowest()) { } AABB::AABB(const Vec3& min, const Vec3& max) @@ -49,7 +51,6 @@ Real AABB::volume() const { int AABB::longest_axis() const { Vec3 d = size(); - if (d.x > d.y && d.x > d.z) { return 0; } else if (d.y > d.z) { @@ -65,8 +66,10 @@ void AABB::expand(const Vec3& point) { } void AABB::expand(const AABB& other) { - min_ = glm::min(min_, other.min_); - max_ = glm::max(max_, other.max_); + if (other.is_valid()) { + min_ = glm::min(min_, other.min_); + max_ = glm::max(max_, other.max_); + } } bool AABB::contains(const Vec3& point) const { @@ -76,27 +79,44 @@ bool AABB::contains(const Vec3& point) const { } bool AABB::intersects(const AABB& other) const { - return (min_.x <= other.max_.x && max_.x >= other.min_.x) && - (min_.y <= other.max_.y && max_.y >= other.min_.y) && - (min_.z <= other.max_.z && max_.z >= other.min_.z); + return min_.x <= other.max_.x && max_.x >= other.min_.x && + min_.y <= other.max_.y && max_.y >= other.min_.y && + min_.z <= other.max_.z && max_.z >= other.min_.z; } -bool AABB::intersect_ray(const Ray& ray, Real& t_min, Real& t_max) const { - // Optimized ray-AABB intersection (slab method) - Vec3 inv_dir = 1.0f / ray.direction_; - Vec3 t0 = (min_ - ray.origin_) * inv_dir; - Vec3 t1 = (max_ - ray.origin_) * inv_dir; +bool AABB::intersect_ray(const Ray& ray, Real& t_min_out, Real& t_max_out) const { + // Slab method for ray-AABB intersection + // Reference: "An Efficient and Robust Ray-Box Intersection Algorithm" by Williams et al. - Vec3 t_near = glm::min(t0, t1); - Vec3 t_far = glm::max(t0, t1); - - t_min = glm::max(glm::max(t_near.x, t_near.y), t_near.z); - t_max = glm::min(glm::min(t_far.x, t_far.y), t_far.z); - - return t_max >= t_min && t_max >= ray.t_min_; + Real t_min = ray.t_min_; + Real t_max = ray.t_max_; + + for (int i = 0; i < 3; ++i) { + Real inv_d = 1.0f / ray.direction_[i]; + Real t0 = (min_[i] - ray.origin_[i]) * inv_d; + Real t1 = (max_[i] - ray.origin_[i]) * inv_d; + + if (inv_d < 0.0f) { + std::swap(t0, t1); + } + + t_min = t0 > t_min ? t0 : t_min; + t_max = t1 < t_max ? t1 : t_max; + + if (t_max < t_min) { + return false; + } + } + + t_min_out = t_min; + t_max_out = t_max; + return true; } AABB AABB::merge(const AABB& a, const AABB& b) { + if (!a.is_valid()) return b; + if (!b.is_valid()) return a; + return AABB( glm::min(a.min_, b.min_), glm::max(a.max_, b.max_) diff --git a/src/geometry/transform.cpp b/src/geometry/transform.cpp index 064fa12..74bf0dd 100644 --- a/src/geometry/transform.cpp +++ b/src/geometry/transform.cpp @@ -1,10 +1,11 @@ /** * @file transform.cpp - * @brief Implementation of transformation utilities + * @brief Implementation of Transform class */ #define GLM_ENABLE_EXPERIMENTAL #include +#include #include #include @@ -16,7 +17,7 @@ Transform::Transform() , scale_(1.0f) , matrix_(1.0f) , inverse_matrix_(1.0f) - , dirty_(false) { + , dirty_(true) { } Transform::Transform(const Vec3& position, const Vec3& rotation, const Vec3& scale) @@ -66,27 +67,62 @@ Mat3 Transform::get_normal_matrix() const { if (dirty_) { update_matrix(); } + // Normal matrix is the transpose of the inverse of the upper-left 3x3 return glm::transpose(glm::inverse(Mat3(matrix_))); } Vec3 Transform::transform_point(const Vec3& point) const { - Vec4 result = get_matrix() * Vec4(point, 1.0f); - return Vec3(result) / result.w; + if (dirty_) { + update_matrix(); + } + Vec4 result = matrix_ * Vec4(point, 1.0f); + return Vec3(result); } Vec3 Transform::transform_direction(const Vec3& direction) const { - return Vec3(get_matrix() * Vec4(direction, 0.0f)); + if (dirty_) { + update_matrix(); + } + Vec4 result = matrix_ * Vec4(direction, 0.0f); + return Vec3(result); } Vec3 Transform::transform_normal(const Vec3& normal) const { - return glm::normalize(get_normal_matrix() * normal); + Mat3 normal_matrix = get_normal_matrix(); + return glm::normalize(normal_matrix * normal); } Transform Transform::operator*(const Transform& other) const { + ARE_PROFILE_FUNCTION(); + + // Combine transforms by multiplying matrices + // Note: This is an approximation; for exact results, + // we would need to decompose the combined matrix Transform result; - result.matrix_ = get_matrix() * other.get_matrix(); - result.inverse_matrix_ = other.get_inverse_matrix() * get_inverse_matrix(); - result.dirty_ = false; + + Mat4 combined = get_matrix() * other.get_matrix(); + + // Extract translation + result.position_ = Vec3(combined[3]); + + // Extract scale (approximate) + result.scale_.x = glm::length(Vec3(combined[0])); + result.scale_.y = glm::length(Vec3(combined[1])); + result.scale_.z = glm::length(Vec3(combined[2])); + + // Remove scale from matrix to extract rotation + Mat3 rotation_matrix; + rotation_matrix[0] = Vec3(combined[0]) / result.scale_.x; + rotation_matrix[1] = Vec3(combined[1]) / result.scale_.y; + rotation_matrix[2] = Vec3(combined[2]) / result.scale_.z; + + // Extract Euler angles (approximate, may have gimbal lock issues) + result.rotation_.x = std::atan2(rotation_matrix[2][1], rotation_matrix[2][2]); + result.rotation_.y = std::atan2(-rotation_matrix[2][0],std::sqrt(rotation_matrix[2][1] * rotation_matrix[2][1] + + rotation_matrix[2][2] * rotation_matrix[2][2])); + result.rotation_.z = std::atan2(rotation_matrix[1][0], rotation_matrix[0][0]); + + result.dirty_ = true; return result; } @@ -111,12 +147,22 @@ void Transform::mark_dirty() { } void Transform::update_matrix() const { - // Build transformation matrix: T * R * S - Mat4 translation = glm::translate(Mat4(1.0f), position_); - Mat4 rotation = glm::eulerAngleYXZ(rotation_.y, rotation_.x, rotation_.z); - Mat4 scale = glm::scale(Mat4(1.0f), scale_); + ARE_PROFILE_FUNCTION(); - matrix_ = translation * rotation * scale; + // Build transformation matrix: T * R * S + // Translation + Mat4 translation_matrix = glm::translate(Mat4(1.0f), position_); + + // Rotation (using Euler angles: YXZ order for typical camera/object rotation) + Mat4 rotation_matrix = glm::eulerAngleYXZ(rotation_.y, rotation_.x, rotation_.z); + + // Scale + Mat4 scale_matrix = glm::scale(Mat4(1.0f), scale_); + + // Combine: T * R * S + matrix_ = translation_matrix * rotation_matrix * scale_matrix; + + // Compute inverse inverse_matrix_ = glm::inverse(matrix_); dirty_ = false; diff --git a/src/geometry/triangle.cpp b/src/geometry/triangle.cpp index 5a53685..899023d 100644 --- a/src/geometry/triangle.cpp +++ b/src/geometry/triangle.cpp @@ -1,141 +1,145 @@ /** * @file triangle.cpp - * @brief Implementation of triangle primitive + * @brief Implementation of Triangle primitive */ +#include +#include #include -#include #include -#include +#include +#include namespace are { Triangle::Triangle() - : v0_() - , v1_() - , v2_() - , material_(are_invalid_handle) { + : material_(are_invalid_handle) { } -Triangle::Triangle(const Vertex& v0, const Vertex& v1, const Vertex& v2, - MaterialHandle material) - : v0_(v0) - , v1_(v1) - , v2_(v2) - , material_(material) { +Triangle::Triangle(const Vertex &v0, const Vertex &v1, const Vertex &v2, MaterialHandle material) + : v0_(v0) + , v1_(v1) + , v2_(v2) + , material_(material) { } Vec3 Triangle::centroid() const { - return (v0_.position_ + v1_.position_ + v2_.position_) / 3.0f; + return (v0_.position_ + v1_.position_ + v2_.position_) / 3.0f; } Vec3 Triangle::normal() const { - Vec3 edge1 = v1_.position_ - v0_.position_; - Vec3 edge2 = v2_.position_ - v0_.position_; - return glm::normalize(glm::cross(edge1, edge2)); + Vec3 edge1 = v1_.position_ - v0_.position_; + Vec3 edge2 = v2_.position_ - v0_.position_; + return glm::normalize(glm::cross(edge1, edge2)); } Real Triangle::area() const { - Vec3 edge1 = v1_.position_ - v0_.position_; - Vec3 edge2 = v2_.position_ - v0_.position_; - return glm::length(glm::cross(edge1, edge2)) * 0.5f; + Vec3 edge1 = v1_.position_ - v0_.position_; + Vec3 edge2 = v2_.position_ - v0_.position_; + return 0.5f * glm::length(glm::cross(edge1, edge2)); } AABB Triangle::compute_aabb() const { - AABB box(v0_.position_); - box.expand(v1_.position_); - box.expand(v2_.position_); - return box; + AABB aabb(v0_.position_); + aabb.expand(v1_.position_); + aabb.expand(v2_.position_); + return aabb; } -bool Triangle::intersect(const Ray& ray, HitRecord& hit) const { - // Möller-Trumbore ray-triangle intersection algorithm - const Vec3& v0 = v0_.position_; - const Vec3& v1 = v1_.position_; - const Vec3& v2 = v2_.position_; - - Vec3 edge1 = v1 - v0; - Vec3 edge2 = v2 - v0; - - Vec3 h = glm::cross(ray.direction_, edge2); - Real a = glm::dot(edge1, h); - - // Check if ray is parallel to triangle - if (std::abs(a) < are_epsilon) { - return false; - } - - Real f = 1.0f / a; - Vec3 s = ray.origin_ - v0; - Real u = f * glm::dot(s, h); - - if (u < 0.0f || u > 1.0f) { - return false; - } - - Vec3 q = glm::cross(s, edge1); - Real v = f * glm::dot(ray.direction_, q); - - if (v < 0.0f || u + v > 1.0f) { - return false; - } - - Real t = f * glm::dot(edge2, q); - - if (!ray.is_valid_t(t) || t >= hit.t_) { - return false; - } - - // Compute hit record - hit.t_ = t; - hit.position_ = ray.at(t); - - // Interpolate vertex attributes using barycentric coordinates - Real w = 1.0f - u - v; - hit.normal_ = glm::normalize(w * v0_.normal_ + u * v1_.normal_ + v * v2_.normal_); - hit.texcoord_ = w * v0_.texcoord_ + u * v1_.texcoord_ + v * v2_.texcoord_; - hit.tangent_ = glm::normalize(w * v0_.tangent_ + u * v1_.tangent_ + v * v2_.tangent_); - - hit.material_ = material_; - hit.set_face_normal(ray.direction_, hit.normal_); - - return true; +bool Triangle::intersect(const Ray &ray, HitRecord &hit) const { + ARE_PROFILE_FUNCTION(); + + // Möller-Trumbore algorithm + // Reference: "Fast, Minimum Storage Ray/Triangle Intersection" + + const Vec3 edge1 = v1_.position_ - v0_.position_; + const Vec3 edge2 = v2_.position_ - v0_.position_; + + const Vec3 h = glm::cross(ray.direction_, edge2); + const Real a = glm::dot(edge1, h); + + // Check if ray is parallel to triangle + if (a > -are_epsilon && a < are_epsilon) { + return false; + } + + const Real f = 1.0f / a; + const Vec3 s = ray.origin_ - v0_.position_; + const Real u = f * glm::dot(s, h); + + // Check barycentric coordinate u + if (u < 0.0f || u > 1.0f) { + return false; + } + + const Vec3 q = glm::cross(s, edge1); + const Real v = f * glm::dot(ray.direction_, q); + + // Check barycentric coordinate v + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + // Calculate t parameter + const Real t = f * glm::dot(edge2, q); + + // Check if intersection is within ray bounds + if (!ray.is_valid_t(t)) { + return false; + } + + // Fill hit record + const Real w = 1.0f - u - v; + + hit.t_ = t; + hit.position_ = ray.at(t); + hit.material_ = material_; + + // Interpolate vertex attributes using barycentric coordinates + hit.normal_ = glm::normalize( + w * v0_.normal_ + u * v1_.normal_ + v * v2_.normal_); + hit.texcoord_ = w * v0_.texcoord_ + u * v1_.texcoord_ + v * v2_.texcoord_; + hit.tangent_ = glm::normalize( + w * v0_.tangent_ + u * v1_.tangent_ + v * v2_.tangent_); + + // Determine front face + hit.set_face_normal(ray.direction_, hit.normal_); + + return true; } -bool Triangle::intersect_fast(const Ray& ray, Real t_max) const { - // Fast ray-triangle intersection (no hit record) - const Vec3& v0 = v0_.position_; - const Vec3& v1 = v1_.position_; - const Vec3& v2 = v2_.position_; - - Vec3 edge1 = v1 - v0; - Vec3 edge2 = v2 - v0; - - Vec3 h = glm::cross(ray.direction_, edge2); - Real a = glm::dot(edge1, h); - - if (std::abs(a) < are_epsilon) { - return false; - } - - Real f = 1.0f / a; - Vec3 s = ray.origin_ - v0; - Real u = f * glm::dot(s, h); - - if (u < 0.0f || u > 1.0f) { - return false; - } - - Vec3 q = glm::cross(s, edge1); - Real v = f * glm::dot(ray.direction_, q); - - if (v < 0.0f || u + v > 1.0f) { - return false; - } - - Real t = f * glm::dot(edge2, q); - - return ray.is_valid_t(t) && t < t_max; +bool Triangle::intersect_fast(const Ray &ray, Real t_max) const { + ARE_PROFILE_FUNCTION(); + + // Simplified Möller-Trumbore without hit record computation + const Vec3 edge1 = v1_.position_ - v0_.position_; + const Vec3 edge2 = v2_.position_ - v0_.position_; + + const Vec3 h = glm::cross(ray.direction_, edge2); + const Real a = glm::dot(edge1, h); + + if (a > -are_epsilon && a < are_epsilon) { + return false; + } + + const Real f = 1.0f / a; + const Vec3 s = ray.origin_ - v0_.position_; + const Real u = f * glm::dot(s, h); + + if (u < 0.0f || u > 1.0f) { + return false; + } + + const Vec3 q = glm::cross(s, edge1); + const Real v = f * glm::dot(ray.direction_, q); + + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + const Real t = f * glm::dot(edge2, q); + + return t > ray.t_min_ && t < t_max; } } // namespace are diff --git a/src/geometry/vertex.cpp b/src/geometry/vertex.cpp index acbfe9d..b65d613 100644 --- a/src/geometry/vertex.cpp +++ b/src/geometry/vertex.cpp @@ -1,13 +1,14 @@ /** * @file vertex.cpp - * @brief Implementation of vertex structure + * @brief Implementation of Vertex structure */ #include +#include namespace are { -Vertex::Vertex(const Vec3& pos) +Vertex::Vertex(const Vec3& pos) : position_(pos) , normal_(0.0f, 1.0f, 0.0f) , texcoord_(0.0f, 0.0f) diff --git a/src/rasterizer/gbuffer.cpp b/src/rasterizer/gbuffer.cpp new file mode 100644 index 0000000..5d989a9 --- /dev/null +++ b/src/rasterizer/gbuffer.cpp @@ -0,0 +1,252 @@ +/** + * @file gbuffer.cpp + * @brief Implementation of GBuffer class + */ + +#include +#include +#include +#include + +namespace are { + +GBuffer::GBuffer(int width, int height) + : fbo_(0) + , rbo_depth_(0) + , position_texture_(0) + , normal_texture_(0) + , albedo_texture_(0) + , material_texture_(0) + , depth_texture_(0) + , width_(width) + , height_(height) { + + create_textures(); + create_framebuffer(); + + ARE_LOG_INFO("GBuffer: Created " + std::to_string(width) + "x" + std::to_string(height)); +} + +GBuffer::~GBuffer() { + delete_textures(); + + if (rbo_depth_ != 0) { + glDeleteRenderbuffers(1, &rbo_depth_); + } + if (fbo_ != 0) { + glDeleteFramebuffers(1, &fbo_); + } +} + +void GBuffer::resize(int width, int height) { + ARE_PROFILE_FUNCTION(); + + if (width == width_ && height == height_) { + return; + } + + width_ = width; + height_ = height; + + // Recreate textures and framebuffer + delete_textures(); + if (rbo_depth_ != 0) { + glDeleteRenderbuffers(1, &rbo_depth_); + rbo_depth_ = 0; + } + + create_textures(); + create_framebuffer(); + + ARE_LOG_INFO("GBuffer: Resized to " + std::to_string(width) + "x" + std::to_string(height)); +} + +void GBuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glViewport(0, 0, width_, height_); +} + +void GBuffer::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GBuffer::clear() { + bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + unbind(); +} + +void GBuffer::bind_texture(int index, int texture_unit) { + glActiveTexture(GL_TEXTURE0 + texture_unit); + + switch (index) { + case 0: glBindTexture(GL_TEXTURE_2D, position_texture_); break; + case 1: glBindTexture(GL_TEXTURE_2D, normal_texture_); break; + case 2: glBindTexture(GL_TEXTURE_2D, albedo_texture_); break; + case 3: glBindTexture(GL_TEXTURE_2D, material_texture_); break; + case 4: glBindTexture(GL_TEXTURE_2D, depth_texture_); break; + default: + ARE_LOG_WARN("GBuffer: Invalid texture index " + std::to_string(index)); + break; + } +} + +void GBuffer::read_pixels(int index, void* data) { + ARE_PROFILE_FUNCTION(); + + bind(); + + GLenum attachment; + GLenum format; + GLenum type; + + switch (index) { + case 0: // Position + attachment = GL_COLOR_ATTACHMENT0; + format = GL_RGB; + type = GL_FLOAT; + break; + case 1: // Normal + attachment = GL_COLOR_ATTACHMENT1; + format = GL_RGB; + type = GL_FLOAT; + break; + case 2: // Albedo + attachment = GL_COLOR_ATTACHMENT2; + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case 3: // Material + attachment = GL_COLOR_ATTACHMENT3; + format = GL_RG; + type = GL_UNSIGNED_BYTE; + break; + case 4: // Depth + attachment = GL_DEPTH_ATTACHMENT; + format = GL_DEPTH_COMPONENT; + type = GL_FLOAT; + break; + default: + ARE_LOG_ERROR("GBuffer: Invalid buffer index for read_pixels"); + unbind(); + return; + } + + glReadBuffer(attachment); + glReadPixels(0, 0, width_, height_, format, type, data); + + unbind(); +} + +void GBuffer::create_textures() { + ARE_PROFILE_FUNCTION(); + + // Position texture (RGB16F) + glGenTextures(1, &position_texture_); + glBindTexture(GL_TEXTURE_2D, position_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, 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); + + // Normal texture (RGB16F) + glGenTextures(1, &normal_texture_); + glBindTexture(GL_TEXTURE_2D, normal_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, 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); + + // Albedo + Metallic texture (RGBA8) + glGenTextures(1, &albedo_texture_); + glBindTexture(GL_TEXTURE_2D, albedo_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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); + + // Roughness + AO texture (RG8) + glGenTextures(1, &material_texture_); + glBindTexture(GL_TEXTURE_2D, material_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width_, height_, 0, GL_RG, GL_UNSIGNED_BYTE, 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); + + // Depth texture (R32F) + glGenTextures(1, &depth_texture_); + glBindTexture(GL_TEXTURE_2D, depth_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width_, height_, 0, GL_RED, 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); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void GBuffer::delete_textures() { + if (position_texture_ != 0) { + glDeleteTextures(1, &position_texture_); + position_texture_ = 0; + } + if (normal_texture_ != 0) { + glDeleteTextures(1, &normal_texture_); + normal_texture_ = 0; + } + if (albedo_texture_ != 0) { + glDeleteTextures(1, &albedo_texture_); + albedo_texture_ = 0; + } + if (material_texture_ != 0) { + glDeleteTextures(1, &material_texture_); + material_texture_ = 0; + } + if (depth_texture_ != 0) { + glDeleteTextures(1, &depth_texture_); + depth_texture_ = 0; + } +} + +void GBuffer::create_framebuffer() { + ARE_PROFILE_FUNCTION(); + + // Create framebuffer + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + + // Attach textures + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, position_texture_, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normal_texture_, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, albedo_texture_, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, material_texture_, 0); + + // Specify draw buffers + GLenum draw_buffers[] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(4, draw_buffers); + + // Create depth renderbuffer + glGenRenderbuffers(1, &rbo_depth_); + glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth_); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_, height_); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth_); + + // Check framebuffer completeness + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + ARE_LOG_ERROR("GBuffer: Framebuffer is not complete! Status: " + std::to_string(status)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +} // namespace are diff --git a/src/rasterizer/rasterizer.cpp b/src/rasterizer/rasterizer.cpp new file mode 100644 index 0000000..3407bad --- /dev/null +++ b/src/rasterizer/rasterizer.cpp @@ -0,0 +1,281 @@ +/** + * @file rasterizer.cpp + * @brief Implementation of Rasterizer class + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace are { + +Rasterizer::Rasterizer(int width, int height) + : width_(width) + , height_(height) { + ARE_PROFILE_FUNCTION(); + + // Create G-Buffer + gbuffer_ = std::make_unique(width, height); + + // Create shader program + gbuffer_shader_ = std::make_unique(); + + ARE_LOG_INFO("Rasterizer: Created " + std::to_string(width) + "x" + std::to_string(height)); +} + +Rasterizer::~Rasterizer() { + ARE_LOG_INFO("Rasterizer: Destroyed"); +} + +void Rasterizer::resize(int width, int height) { + ARE_PROFILE_FUNCTION(); + + if (width == width_ && height == height_) { + return; + } + + width_ = width; + height_ = height; + + if (gbuffer_) { + gbuffer_->resize(width, height); + } + + ARE_LOG_INFO("Rasterizer: Resized to " + std::to_string(width) + "x" + std::to_string(height)); +} + +void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) { + ARE_PROFILE_FUNCTION(); + + if (!gbuffer_shader_ || !gbuffer_shader_->is_valid()) { + ARE_LOG_ERROR("Rasterizer: G-Buffer shader is not valid"); + return; + } + + // Bind G-Buffer for rendering + gbuffer_->bind(); + + // Clear buffers + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + // Enable face culling + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + + // Use G-Buffer shader + gbuffer_shader_->use(); + + // Set view and projection matrices + gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix()); + gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix()); + + // Render all meshes + const auto& meshes = scene.get_all_meshes(); + const auto& materials = scene.get_all_materials(); + + for (const auto& mesh : meshes) { + if (mesh.is_empty() || !mesh.has_gpu_resources()) { + continue; + } + + // Set model matrix (identity for now, can be extended with Transform) + Mat4 model_matrix = Mat4(1.0f); + gbuffer_shader_->set_uniform("u_model", model_matrix); + + // Calculate normal matrix + Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model_matrix))); + gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix); + + // Set material properties + MaterialHandle mat_handle = mesh.get_material(); + if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) { + const Material& material = materials[mat_handle - 1]; // Handle is 1-based + gbuffer_shader_->set_uniform("u_albedo", material.get_albedo()); + gbuffer_shader_->set_uniform("u_metallic", material.get_metallic()); + gbuffer_shader_->set_uniform("u_roughness", material.get_roughness()); + } else { + // Default material + gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f, 0.8f, 0.8f)); + gbuffer_shader_->set_uniform("u_metallic", 0.0f); + gbuffer_shader_->set_uniform("u_roughness", 0.5f); + } + + // Draw mesh + glBindVertexArray(mesh.get_vao()); + glDrawElements(GL_TRIANGLES, + static_cast(mesh.get_index_count()), + GL_UNSIGNED_INT, + nullptr); + glBindVertexArray(0); + } + + // Disable states + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + // Unbind G-Buffer + gbuffer_->unbind(); + + ARE_GL_CHECK(); +} + +GBuffer& Rasterizer::get_gbuffer() { + return *gbuffer_; +} + +const GBuffer& Rasterizer::get_gbuffer() const { + return *gbuffer_; +} + +void Rasterizer::upload_mesh(Mesh& mesh) { + ARE_PROFILE_FUNCTION(); + + if (mesh.is_empty()) { + ARE_LOG_WARN("Rasterizer: Attempting to upload empty mesh"); + return; + } + + // Delete existing GPU resources if any + if (mesh.has_gpu_resources()) { + delete_mesh(mesh); + } + + setup_mesh_buffers(mesh); + + ARE_LOG_DEBUG("Rasterizer: Uploaded mesh with " + + std::to_string(mesh.get_vertex_count()) + " vertices, " + + std::to_string(mesh.get_triangle_count()) + " triangles"); +} + +void Rasterizer::delete_mesh(Mesh& mesh) { + ARE_PROFILE_FUNCTION(); + + uint32_t vao = mesh.get_vao(); + uint32_t vbo = mesh.get_vbo(); + uint32_t ebo = mesh.get_ebo(); + + if (vao != 0) { + glDeleteVertexArrays(1, &vao); + } + if (vbo != 0) { + glDeleteBuffers(1, &vbo); + } + if (ebo != 0) { + glDeleteBuffers(1, &ebo); + } + + mesh.set_vao(0); + mesh.set_vbo(0); + mesh.set_ebo(0); +} + +void Rasterizer::initialize_shaders(const std::string& shader_dir) { + ARE_PROFILE_FUNCTION(); + + if (!gbuffer_shader_) { + gbuffer_shader_ = std::make_unique(); + } + + std::string vert_path = shader_dir + "gbuffer/gbuffer.vert"; + std::string frag_path = shader_dir + "gbuffer/gbuffer.frag"; + + bool success = true; + + if (!gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_VERTEX, vert_path)) { + ARE_LOG_ERROR("Rasterizer: Failed to load vertex shader: " + vert_path); + success = false; + } + + if (!gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_FRAGMENT, frag_path)) { + ARE_LOG_ERROR("Rasterizer: Failed to load fragment shader: " + frag_path); + success = false; + } + + if (success && !gbuffer_shader_->link()) { + ARE_LOG_ERROR("Rasterizer: Failed to link G-Buffer shader program"); + success = false; + } + + if (success) { + ARE_LOG_INFO("Rasterizer: Shaders initialized successfully"); + } +} + +void Rasterizer::setup_mesh_buffers(Mesh& mesh) { + ARE_PROFILE_FUNCTION(); + + uint32_t vao, vbo, ebo; + + // Create VAO + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // Create VBO + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, + mesh.get_vertex_count() * sizeof(Vertex), + mesh.get_vertices().data(), + GL_STATIC_DRAW); + + // Create EBO + glGenBuffers(1, &ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + mesh.get_index_count() * sizeof(uint32_t), + mesh.get_indices().data(), + GL_STATIC_DRAW); + + // Setup vertex attributes + // Position (location = 0) + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, + sizeof(Vertex), + reinterpret_cast(get_position_offset())); + + // Normal (location = 1) + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, + sizeof(Vertex), + reinterpret_cast(get_normal_offset())); + + // Texcoord (location = 2) + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, + sizeof(Vertex), + reinterpret_cast(get_texcoord_offset())); + + // Tangent (location = 3) + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, + sizeof(Vertex), + reinterpret_cast(get_tangent_offset())); + + // Unbind VAO + glBindVertexArray(0); + + // Store handles in mesh + mesh.set_vao(vao); + mesh.set_vbo(vbo); + mesh.set_ebo(ebo); + + ARE_GL_CHECK(); +} + +} // namespace are diff --git a/src/rasterizer/shader_program.cpp b/src/rasterizer/shader_program.cpp new file mode 100644 index 0000000..612ed75 --- /dev/null +++ b/src/rasterizer/shader_program.cpp @@ -0,0 +1,251 @@ +/** + * @file shader_program.cpp + * @brief Implementation of ShaderProgram class + */ + +#include +#include +#include +#include +#include +#include + +namespace are { + +ShaderProgram::ShaderProgram() + : program_(0) + , vertex_shader_(0) + , fragment_shader_(0) + , compute_shader_(0) + , linked_(false) { + program_ = glCreateProgram(); + if (program_ == 0) { + ARE_LOG_ERROR("ShaderProgram: Failed to create OpenGL program"); + } +} + +ShaderProgram::~ShaderProgram() { + if (vertex_shader_ != 0) { + glDeleteShader(vertex_shader_); + } + if (fragment_shader_ != 0) { + glDeleteShader(fragment_shader_); + } + if (compute_shader_ != 0) { + glDeleteShader(compute_shader_); + } + if (program_ != 0) { + glDeleteProgram(program_); + } +} + +bool ShaderProgram::load_shader(ShaderType type, const std::string& filepath) { + ARE_PROFILE_FUNCTION(); + + // Read shader source from file + std::string source = read_file_to_string(filepath); + if (source.empty()) { + ARE_LOG_ERROR("ShaderProgram: Failed to read shader file: " + filepath); + return false; + } + + ARE_LOG_INFO("ShaderProgram: Loaded shader from " + filepath); + return compile_shader(type, source); +} + +bool ShaderProgram::compile_shader(ShaderType type, const std::string& source) { + ARE_PROFILE_FUNCTION(); + + GLenum gl_type; + uint32_t* shader_id; + std::string type_name; + + switch (type) { + case ShaderType::ARE_SHADER_VERTEX: + gl_type = GL_VERTEX_SHADER; + shader_id = &vertex_shader_; + type_name = "vertex"; + break; + case ShaderType::ARE_SHADER_FRAGMENT: + gl_type = GL_FRAGMENT_SHADER; + shader_id = &fragment_shader_; + type_name = "fragment"; + break; + case ShaderType::ARE_SHADER_COMPUTE: + gl_type = GL_COMPUTE_SHADER; + shader_id = &compute_shader_; + type_name = "compute"; + break; + default: + ARE_LOG_ERROR("ShaderProgram: Unknown shader type"); + return false; + } + + // Delete existing shader if any + if (*shader_id != 0) { + glDeleteShader(*shader_id); + } + + // Create and compile shader + *shader_id = glCreateShader(gl_type); + const char* source_cstr = source.c_str(); + glShaderSource(*shader_id, 1, &source_cstr, nullptr); + glCompileShader(*shader_id); + + // Check compilation errors + if (!check_compile_errors(*shader_id, type)) { + ARE_LOG_ERROR("ShaderProgram: Failed to compile " + type_name + " shader"); + glDeleteShader(*shader_id); + *shader_id = 0; + return false; + } + + // Attach shader to program + glAttachShader(program_, *shader_id); + + ARE_LOG_INFO("ShaderProgram: Compiled " + type_name + " shader successfully"); + return true; +} + +bool ShaderProgram::link() { + ARE_PROFILE_FUNCTION(); + + if (program_ == 0) { + ARE_LOG_ERROR("ShaderProgram: Cannot link invalid program"); + return false; + } + + // Link program + glLinkProgram(program_); + + // Check link errors + if (!check_link_errors()) { + ARE_LOG_ERROR("ShaderProgram: Failed to link shader program"); + linked_ = false; + return false; + } + + // Detach and delete shaders after successful link + if (vertex_shader_ != 0) { + glDetachShader(program_, vertex_shader_); + glDeleteShader(vertex_shader_); + vertex_shader_ = 0; + } + if (fragment_shader_ != 0) { + glDetachShader(program_, fragment_shader_); + glDeleteShader(fragment_shader_); + fragment_shader_ = 0; + } + if (compute_shader_ != 0) { + glDetachShader(program_, compute_shader_); + glDeleteShader(compute_shader_); + compute_shader_ = 0; + } + + linked_ = true; + ARE_LOG_INFO("ShaderProgram: Linked shader program successfully"); + return true; +} + +void ShaderProgram::use() const { + if (is_valid()) { + glUseProgram(program_); + } else { + ARE_LOG_WARN("ShaderProgram: Attempting to use invalid program"); + } +} + +void ShaderProgram::set_uniform(const std::string& name, int value) { + glUniform1i(get_uniform_location(name), value); +} + +void ShaderProgram::set_uniform(const std::string& name, float value) { + glUniform1f(get_uniform_location(name), value); +} + +void ShaderProgram::set_uniform(const std::string& name, const Vec2& value) { + glUniform2fv(get_uniform_location(name), 1, glm::value_ptr(value)); +} + +void ShaderProgram::set_uniform(const std::string& name, const Vec3& value) { + glUniform3fv(get_uniform_location(name), 1, glm::value_ptr(value)); +} + +void ShaderProgram::set_uniform(const std::string& name, const Vec4& value) { + glUniform4fv(get_uniform_location(name), 1, glm::value_ptr(value)); +} + +void ShaderProgram::set_uniform(const std::string& name, const Mat3& value) { + glUniformMatrix3fv(get_uniform_location(name), 1, GL_FALSE, glm::value_ptr(value)); +} + +void ShaderProgram::set_uniform(const std::string& name, const Mat4& value) { + glUniformMatrix4fv(get_uniform_location(name), 1, GL_FALSE, glm::value_ptr(value)); +} + +int ShaderProgram::get_uniform_location(const std::string& name) { + // Check cache first + auto it = uniform_cache_.find(name); + if (it != uniform_cache_.end()) { + return it->second; + } + + // Query OpenGL + int location = glGetUniformLocation(program_, name.c_str()); + if (location == -1) { + ARE_LOG_WARN("ShaderProgram: Uniform '" + name + "' not found"); + } + + // Cache the location + uniform_cache_[name] = location; + return location; +} + +bool ShaderProgram::check_compile_errors(uint32_t shader, ShaderType type) { + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (!success) { + GLint log_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + + std::string info_log; + info_log.resize(log_length); + glGetShaderInfoLog(shader, log_length, nullptr, &info_log[0]); + + std::string type_name; + switch (type) { + case ShaderType::ARE_SHADER_VERTEX: type_name = "VERTEX"; break; + case ShaderType::ARE_SHADER_FRAGMENT: type_name = "FRAGMENT"; break; + case ShaderType::ARE_SHADER_COMPUTE: type_name = "COMPUTE"; break; + } + + ARE_LOG_ERROR("ShaderProgram: " + type_name + " shader compilation failed:"); + ARE_LOG_ERROR(info_log); + return false; + } + + return true; +} + +bool ShaderProgram::check_link_errors() { + GLint success; + glGetProgramiv(program_, GL_LINK_STATUS, &success); + + if (!success) { + GLint log_length; + glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length); + + std::string info_log; + info_log.resize(log_length); + glGetProgramInfoLog(program_, log_length, nullptr, &info_log[0]); + + ARE_LOG_ERROR("ShaderProgram: Program linking failed:"); + ARE_LOG_ERROR(info_log); + return false; + } + + return true; +} + +} // namespace are diff --git a/src/raytracer/cpu_raytracer.cpp b/src/raytracer/cpu_raytracer.cpp new file mode 100644 index 0000000..dda2622 --- /dev/null +++ b/src/raytracer/cpu_raytracer.cpp @@ -0,0 +1,306 @@ +/** + * @file cpu_raytracer.cpp + * @brief CPU ray tracing implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef ARE_USE_OPENMP +#include +#endif + +namespace are { + +CPURayTracer::CPURayTracer(const RayTracingConfig& config) + : RayTracer(config) + , bvh_(nullptr) + , scene_(nullptr) + , width_(0) + , height_(0) +{ + ARE_LOG_INFO("CPU ray tracer initialized"); +} + +CPURayTracer::~CPURayTracer() { + ARE_LOG_INFO("CPU ray tracer destroyed"); +} + +void CPURayTracer::update_bvh(const BVH& bvh) { + bvh_ = &bvh; + ARE_LOG_INFO("BVH updated for CPU ray tracer (" + + std::to_string(bvh.get_nodes().size()) + " nodes)"); +} + +void CPURayTracer::render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) { + ARE_PROFILE_FUNCTION(); + + if (!bvh_ || !bvh_->is_built()) { + ARE_LOG_ERROR("BVH not built, cannot render"); + return; + } + + scene_ = &scene; + + // Get framebuffer size from output texture + glBindTexture(GL_TEXTURE_2D, output_texture); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width_); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height_); + glBindTexture(GL_TEXTURE_2D, 0); + + if (width_ <= 0 || height_ <= 0) { + ARE_LOG_ERROR("Invalid output texture dimensions"); + return; + } + + // Resize framebuffer if needed + size_t pixel_count = width_ * height_; + if (framebuffer_.size() != pixel_count) { + framebuffer_.resize(pixel_count); + ARE_LOG_INFO("Framebuffer resized to " + std::to_string(width_) + "x" + std::to_string(height_)); + } + + // Render using ray tracing + ARE_LOG_INFO("Starting CPU ray tracing (" + std::to_string(config_.spp) + " spp)"); + + const int spp = config_.spp; + const Real inv_spp = 1.0f / static_cast(spp); + + #ifdef ARE_USE_OPENMP + #pragma omp parallel for schedule(dynamic, 16) + #endif + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < width_; ++x) { + Vec3 color(0.0f); + + // Multi-sampling + for (int s = 0; s < spp; ++s) { + // Jittered sampling + Real u = (x + random_float()) / static_cast(width_); + Real v = (y + random_float()) / static_cast(height_); + + // Generate ray + Vec3 ray_origin, ray_direction; + camera.generate_ray(u, v, ray_origin, ray_direction); + + Ray ray(ray_origin, ray_direction); + + // Trace ray + color += trace_ray(ray, 0); + } + + // Average samples + color *= inv_spp; + + // Store in framebuffer + size_t index = y * width_ + x; + framebuffer_[index] = color; + } + + // Progress logging (every 10%) + if (y % (height_ / 10) == 0) { + Real progress = 100.0f * y / height_; + ARE_LOG_INFO("Ray tracing progress: " + std::to_string(static_cast(progress)) + "%"); + } + } + + ARE_LOG_INFO("Ray tracing complete, uploading to GPU"); + + // Upload to GPU texture + glBindTexture(GL_TEXTURE_2D, output_texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, + GL_RGB, GL_FLOAT, framebuffer_.data()); + glBindTexture(GL_TEXTURE_2D, 0); +} + +Vec3 CPURayTracer::trace_ray(const Ray& ray, int depth) { + // Russian roulette termination + if (depth >= config_.max_depth) { + return Vec3(0.0f); + } + + // Intersect with scene + HitRecord hit; + if (!bvh_->intersect(ray, hit)) { + // Sky color (simple gradient) + Real t = 0.5f * (ray.direction_.y + 1.0f); + return glm::mix(Vec3(1.0f), Vec3(0.5f, 0.7f, 1.0f), t); + } + + // Shade hit point + return shade(hit, ray, depth); +} + +Vec3 CPURayTracer::shade(const HitRecord& hit, const Ray& ray, int depth) { + // Random real generator + thread_local RandomGenerator generator; + + // Get material + const Material* material = scene_->get_material(hit.material_); + if (!material) { + return Vec3(1.0f, 0.0f, 1.0f); // Magenta for missing material + } + + // Get material properties + Vec3 albedo = material->get_albedo(); + Real metallic = material->get_metallic(); + Real roughness = material->get_roughness(); + Vec3 emissive = material->get_emissive(); + + // Emissive materials + if (material->is_emissive()) { + return emissive; + } + + // Compute direct lighting + Vec3 direct_lighting = compute_direct_lighting(hit); + + // Compute ambient occlusion + Real ao = 1.0f; + if (config_.enable_ao) { + ao = compute_ambient_occlusion(hit); + } + + // Simple diffuse shading + Vec3 color = albedo * direct_lighting * ao; + + // Global illumination (indirect lighting) + if (config_.enable_gi && depth < config_.max_depth - 1) { + // Generate random direction in hemisphere + Vec3 scatter_direction = generator.random_cosine_direction(hit.normal_); + + // Trace secondary ray + Ray scatter_ray(hit.position_ + hit.normal_ * are_epsilon, scatter_direction); + Vec3 indirect = trace_ray(scatter_ray, depth + 1); + + // Add indirect lighting (weighted by albedo) + color += albedo * indirect * 0.5f; + } + + return color; +} + +Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) { + Vec3 lighting(0.0f); + + const auto& lights = scene_->get_all_lights(); + + for (const auto& light : lights) { + if (!light) continue; + + // Check if light affects this point + if (!light->affects_point(hit.position_)) { + continue; + } + + Vec3 light_dir; + Vec3 light_color = light->get_color() * light->get_intensity(); + Real light_distance = 1e30f; + + // Compute light direction based on type + if (light->get_type() == LightType::ARE_LIGHT_DIRECTIONAL) { + auto* dir_light = static_cast(light.get()); + light_dir = -dir_light->get_direction(); + light_distance = 1e30f; // Infinite distance + } + else if (light->get_type() == LightType::ARE_LIGHT_POINT) { + auto* point_light = static_cast(light.get()); + Vec3 to_light = point_light->get_position() - hit.position_; + light_distance = glm::length(to_light); + light_dir = to_light / light_distance; + + // Apply attenuation + Real attenuation = point_light->calculate_attenuation(light_distance); + light_color *= attenuation; + } + else if (light->get_type() == LightType::ARE_LIGHT_SPOT) { + auto* spot_light = static_cast(light.get()); + Vec3 to_light = spot_light->get_position() - hit.position_; + light_distance = glm::length(to_light); + light_dir = to_light / light_distance; + + // Apply spotlight factor + Real spot_factor = spot_light->calculate_spot_factor(-light_dir); + if (spot_factor <= 0.0f) { + continue; // Outside spotlight cone + } + + light_color *= spot_factor; + } + + // Shadow test + bool in_shadow = false; + if (light->get_cast_shadows()) { + in_shadow = is_in_shadow(hit.position_ + hit.normal_ * are_epsilon, + light_dir, light_distance); + } + + if (!in_shadow) { + // Lambertian diffuse + Real n_dot_l = glm::max(glm::dot(hit.normal_, light_dir), 0.0f); + lighting += light_color * n_dot_l; + } + } + + // Add ambient term + lighting += Vec3(0.03f); + + return lighting; +} + +Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) { + // Random real generator + thread_local RandomGenerator generator; + + const int num_samples = config_.ao_samples; + const Real radius = config_.ao_radius; + + int occluded_count = 0; + + for (int i = 0; i < num_samples; ++i) { + // Generate random direction in hemisphere + Vec3 sample_dir = generator.random_in_hemisphere(hit.normal_); + + // Cast AO ray + Ray ao_ray(hit.position_ + hit.normal_ * are_epsilon, sample_dir, + are_epsilon, radius); + + // Check if ray hits anything within radius + if (bvh_->intersect_any(ao_ray, radius)) { + occluded_count++; + } + } + + // AO factor (1.0 = no occlusion, 0.0 = full occlusion) + Real ao = 1.0f - (static_cast(occluded_count) / num_samples); + return ao; +} + +bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance) { + Ray shadow_ray(origin, direction, are_epsilon, max_distance - are_epsilon); + return bvh_->intersect_any(shadow_ray, max_distance); +} + +} // namespace are diff --git a/src/raytracer/hit_record.cpp b/src/raytracer/hit_record.cpp new file mode 100644 index 0000000..dabd6ce --- /dev/null +++ b/src/raytracer/hit_record.cpp @@ -0,0 +1,34 @@ +/** + * @file hit_record.cpp + * @brief Implementation of hit record + */ + +#include +#include + +namespace are { + +HitRecord::HitRecord() + : position_(0.0f) + , normal_(0.0f, 1.0f, 0.0f) + , texcoord_(0.0f) + , tangent_(1.0f, 0.0f, 0.0f) + , t_(-1.0f) + , material_(are_invalid_handle) + , triangle_index_(0) + , front_face_(true) { +} + +void HitRecord::set_face_normal(const Vec3 &ray_direction, const Vec3 &outward_normal) { + // Determine if ray hit front face or back face + front_face_ = glm::dot(ray_direction, outward_normal) < 0.0f; + + // Normal always points against ray direction + normal_ = front_face_ ? outward_normal : -outward_normal; +} + +bool HitRecord::is_valid() const { + return t_ >= 0.0f && material_ != are_invalid_handle; +} + +} // namespace are diff --git a/src/raytracer/ray.cpp b/src/raytracer/ray.cpp new file mode 100644 index 0000000..46a2392 --- /dev/null +++ b/src/raytracer/ray.cpp @@ -0,0 +1,41 @@ +/** + * @file ray.cpp + * @brief Implementation of Ray structure + */ + +#include +#include +#include + +namespace are { + +Ray::Ray() + : origin_(0.0f) + , direction_(0.0f, 0.0f, 1.0f) + , t_min_(are_epsilon) + , t_max_(1e30f) { +} + +Ray::Ray(const Vec3& origin, const Vec3& direction, Real t_min, Real t_max) + : origin_(origin) + , t_min_(t_min) + , t_max_(t_max) { + // Normalize direction vector + Real length = glm::length(direction); + if (length < are_epsilon) { + ARE_LOG_WARN("Ray: Direction vector has zero length, using default (0,0,1)"); + direction_ = Vec3(0.0f, 0.0f, 1.0f); + } else { + direction_ = direction / length; + } +} + +Vec3 Ray::at(Real t) const { + return origin_ + direction_ * t; +} + +bool Ray::is_valid_t(Real t) const { + return t >= t_min_ && t <= t_max_; +} + +} // namespace are diff --git a/src/raytracer/raytracer.cpp b/src/raytracer/raytracer.cpp new file mode 100644 index 0000000..926e472 --- /dev/null +++ b/src/raytracer/raytracer.cpp @@ -0,0 +1,22 @@ +/** + * @file raytracer.cpp + * @brief Implementation of RayTracer base class + */ + +#include +#include + +namespace are { + +RayTracer::RayTracer(const RayTracingConfig& config) + : config_(config) { + ARE_LOG_INFO("RayTracer: Created with max depth " + + std::to_string(config_.max_depth)); +} + +void RayTracer::set_config(const RayTracingConfig& config) { + config_ = config; + ARE_LOG_INFO("RayTracer: Configuration updated"); +} + +} // namespace are diff --git a/src/scene/camera.cpp b/src/scene/camera.cpp index ec21c62..ba11360 100644 --- a/src/scene/camera.cpp +++ b/src/scene/camera.cpp @@ -1,9 +1,12 @@ /** * @file camera.cpp - * @brief Implementation of camera system + * @brief Implementation of Camera class */ #include +#include +#include +#include #include namespace are { @@ -26,7 +29,7 @@ Camera::Camera() Camera::Camera(const Vec3& position, const Vec3& target, const Vec3& up) : position_(position) , target_(target) - , up_(up) + , up_(glm::normalize(up)) , fov_(45.0f) , aspect_ratio_(16.0f / 9.0f) , near_plane_(0.1f) @@ -51,7 +54,13 @@ void Camera::set_target(const Vec3& target) { } void Camera::set_up(const Vec3& up) { - up_ = up; + Real length = glm::length(up); + if (length < are_epsilon) { + ARE_LOG_WARN("Camera: Invalid up vector (zero length), using default"); + up_ = Vec3(0.0f, 1.0f, 0.0f); + } else { + up_ = up / length; + } view_dirty_ = true; dirty_ = true; } @@ -59,42 +68,51 @@ void Camera::set_up(const Vec3& up) { void Camera::look_at(const Vec3& position, const Vec3& target, const Vec3& up) { position_ = position; target_ = target; - up_ = up; - view_dirty_ = true; - dirty_ = true; + set_up(up); } void Camera::set_fov(Real fov_degrees) { - fov_ = fov_degrees; + // Clamp FOV to reasonable range + fov_ = clamp(fov_degrees, 1.0f, 179.0f); projection_dirty_ = true; dirty_ = true; } void Camera::set_aspect_ratio(Real aspect) { + if (aspect <= 0.0f) { + ARE_LOG_ERROR("Camera: Invalid aspect ratio (must be positive)"); + return; + } aspect_ratio_ = aspect; projection_dirty_ = true; dirty_ = true; } void Camera::set_near_plane(Real near) { + if (near <= 0.0f) { + ARE_LOG_ERROR("Camera: Invalid near plane (must be positive)"); + return; + } near_plane_ = near; projection_dirty_ = true; dirty_ = true; } void Camera::set_far_plane(Real far) { + if (far <= near_plane_) { + ARE_LOG_ERROR("Camera: Invalid far plane (must be greater than near plane)"); + return; + } far_plane_ = far; projection_dirty_ = true; dirty_ = true; } void Camera::set_perspective(Real fov_degrees, Real aspect, Real near, Real far) { - fov_ = fov_degrees; - aspect_ratio_ = aspect; - near_plane_ = near; - far_plane_ = far; - projection_dirty_ = true; - dirty_ = true; + set_fov(fov_degrees); + set_aspect_ratio(aspect); + set_near_plane(near); + set_far_plane(far); } Vec3 Camera::get_forward() const { @@ -124,36 +142,58 @@ Mat4 Camera::get_view_projection_matrix() const { } void Camera::generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const { - // Convert from [0,1] to [-1,1] + ARE_PROFILE_FUNCTION(); + + // Ray origin is camera position + origin = position_; + + // Calculate ray direction in camera space + // u, v are in [0, 1], convert to [-1, 1] Real x = 2.0f * u - 1.0f; - Real y = 1.0f - 2.0f * v; // Flip Y + Real y = 1.0f - 2.0f * v; // Flip y for screen coordinates - // Compute ray direction in camera space - Real tan_half_fov = std::tan(glm::radians(fov_ * 0.5f)); - Real camera_x = x * aspect_ratio_ * tan_half_fov; - Real camera_y = y * tan_half_fov; + // Calculate direction based on FOV and aspect ratio + Real tan_half_fov = std::tan(degrees_to_radians(fov_ * 0.5f)); + Real viewport_height = 2.0f * tan_half_fov; + Real viewport_width = viewport_height * aspect_ratio_; - // Transform to world space + // Get camera basis vectors Vec3 forward = get_forward(); Vec3 right = get_right(); Vec3 up = glm::normalize(glm::cross(right, forward)); - origin = position_; - direction = glm::normalize(forward + camera_x * right + camera_y * up); + // Calculate ray direction + direction = glm::normalize( + forward + + right * (x * viewport_width * 0.5f) + + up * (y * viewport_height * 0.5f) + ); } void Camera::update_view_matrix() const { - view_matrix_ = glm::lookAt(position_, target_, up_); + ARE_PROFILE_FUNCTION(); + + // Check if camera is looking at itself + if (glm::length(target_ - position_) < are_epsilon) { + ARE_LOG_WARN("Camera: Position and target are too close, using default view"); + view_matrix_ = Mat4(1.0f); + } else { + view_matrix_ = glm::lookAt(position_, target_, up_); + } + view_dirty_ = false; } void Camera::update_projection_matrix() const { + ARE_PROFILE_FUNCTION(); + projection_matrix_ = glm::perspective( - glm::radians(fov_), + degrees_to_radians(fov_), aspect_ratio_, near_plane_, far_plane_ ); + projection_dirty_ = false; } diff --git a/src/scene/directional_light.cpp b/src/scene/directional_light.cpp new file mode 100644 index 0000000..784055a --- /dev/null +++ b/src/scene/directional_light.cpp @@ -0,0 +1,59 @@ +/** + * @file directional_light.cpp + * @brief Implementation of DirectionalLight class + */ + +#include +#include +#include + +namespace are { + +DirectionalLight::DirectionalLight() + : Light(LightType::ARE_LIGHT_DIRECTIONAL) + , direction_(0.0f, -1.0f, 0.0f) { +} + +DirectionalLight::DirectionalLight(const Vec3& direction, const Vec3& color, Real intensity) + : Light(LightType::ARE_LIGHT_DIRECTIONAL) + , direction_(glm::normalize(direction)) { + set_color(color); + set_intensity(intensity); +} + +void DirectionalLight::set_direction(const Vec3& direction) { + Real length = glm::length(direction); + if (length < are_epsilon) { + ARE_LOG_WARN("DirectionalLight: Invalid direction vector (zero length), using default"); + direction_ = Vec3(0.0f, -1.0f, 0.0f); + } else { + direction_ = direction / length; + } +} + +LightData DirectionalLight::pack() const { + LightData data; + + // position_type_: xyz unused for directional, w = light type + data.position_type_ = Vec4(0.0f, 0.0f, 0.0f, + static_cast(LightType::ARE_LIGHT_DIRECTIONAL)); + + // direction_range_: xyz = direction, w = range (unused for directional) + data.direction_range_ = Vec4(direction_, 0.0f); + + // color_intensity_: xyz = color, w = intensity + data.color_intensity_ = Vec4(color_, intensity_); + + // params_: x = cast_shadows + data.params_ = Vec4(cast_shadows_ ? 1.0f : 0.0f, 0.0f, 0.0f, 0.0f); + + return data; +} + +bool DirectionalLight::affects_point(const Vec3& point) const { + // Directional light affects all points + (void)point; // Suppress unused parameter warning + return true; +} + +} // namespace are diff --git a/src/scene/light.cpp b/src/scene/light.cpp new file mode 100644 index 0000000..f01a018 --- /dev/null +++ b/src/scene/light.cpp @@ -0,0 +1,35 @@ +/** + * @file light.cpp + * @brief Implementation of base Light class + */ + +#include +#include +#include + +namespace are { + +Light::Light(LightType type) + : type_(type) + , color_(1.0f, 1.0f, 1.0f) + , intensity_(1.0f) + , cast_shadows_(true) { +} + +void Light::set_color(const Vec3& color) { + // Clamp color to valid range [0, 1] + color_.x = clamp(color.x, 0.0f, 1.0f); + color_.y = clamp(color.y, 0.0f, 1.0f); + color_.z = clamp(color.z, 0.0f, 1.0f); +} + +void Light::set_intensity(Real intensity) { + // Intensity can be HDR, so only clamp to non-negative + intensity_ = std::max(0.0f, intensity); +} + +void Light::set_cast_shadows(bool cast) { + cast_shadows_ = cast; +} + +} // namespace are diff --git a/src/scene/material.cpp b/src/scene/material.cpp index 22d067d..cbbe9fb 100644 --- a/src/scene/material.cpp +++ b/src/scene/material.cpp @@ -1,14 +1,16 @@ /** * @file material.cpp - * @brief Implementation of PBR material + * @brief Implementation of PBR Material class */ #include +#include +#include namespace are { Material::Material() - : albedo_(0.8f, 0.8f, 0.8f) + : albedo_(1.0f, 1.0f, 1.0f) , metallic_(0.0f) , roughness_(0.5f) , emissive_(0.0f, 0.0f, 0.0f) @@ -21,7 +23,10 @@ Material::Material() } void Material::set_albedo(const Vec3& albedo) { - albedo_ = albedo; + // Clamp albedo to valid range [0, 1] + albedo_.x = clamp(albedo.x, 0.0f, 1.0f); + albedo_.y = clamp(albedo.y, 0.0f, 1.0f); + albedo_.z = clamp(albedo.z, 0.0f, 1.0f); } void Material::set_albedo_map(const std::string& path) { @@ -29,7 +34,7 @@ void Material::set_albedo_map(const std::string& path) { } void Material::set_metallic(Real metallic) { - metallic_ = glm::clamp(metallic, 0.0f, 1.0f); + metallic_ = clamp(metallic, 0.0f, 1.0f); } void Material::set_metallic_map(const std::string& path) { @@ -37,7 +42,8 @@ void Material::set_metallic_map(const std::string& path) { } void Material::set_roughness(Real roughness) { - roughness_ = glm::clamp(roughness, 0.0f, 1.0f); + // Clamp roughness to avoid division by zero in BRDF calculations + roughness_ = clamp(roughness, 0.04f, 1.0f); } void Material::set_roughness_map(const std::string& path) { @@ -53,7 +59,10 @@ void Material::set_ao_map(const std::string& path) { } void Material::set_emissive(const Vec3& emissive) { - emissive_ = emissive; + // Emissive can be HDR, so no upper clamp + emissive_.x = std::max(0.0f, emissive.x); + emissive_.y = std::max(0.0f, emissive.y); + emissive_.z = std::max(0.0f, emissive.z); } void Material::set_emissive_map(const std::string& path) { @@ -61,7 +70,12 @@ void Material::set_emissive_map(const std::string& path) { } bool Material::is_emissive() const { - return glm::length(emissive_) > are_epsilon || !emissive_map_.empty(); + // Check if material has significant emission + const Real threshold = 0.001f; + return (emissive_.x > threshold || + emissive_.y > threshold || + emissive_.z > threshold) || + has_emissive_map(); } } // namespace are diff --git a/src/scene/mesh.cpp b/src/scene/mesh.cpp new file mode 100644 index 0000000..763dbd5 --- /dev/null +++ b/src/scene/mesh.cpp @@ -0,0 +1,202 @@ +/** + * @file mesh.cpp + * @brief Implementation of Mesh class + */ + +#include +#include +#include +#include +#include + +namespace are { + +Mesh::Mesh() + : material_id_(are_invalid_handle) + , vao_(0) + , vbo_(0) + , ebo_(0) { +} + +Mesh::Mesh(const std::vector& vertices, + const std::vector& indices, + MaterialHandle material_id) + : vertices_(vertices) + , indices_(indices) + , material_id_(material_id) + , vao_(0) + , vbo_(0) + , ebo_(0) { + compute_aabb(); +} + +Mesh::Mesh(const Vertex* vertices, size_t vertex_count, + const uint32_t* indices, size_t index_count, + MaterialHandle material_id) + : material_id_(material_id) + , vao_(0) + , vbo_(0) + , ebo_(0) { + if (vertices && vertex_count > 0) { + vertices_.assign(vertices, vertices + vertex_count); + } + + if (indices && index_count > 0) { + indices_.assign(indices, indices + index_count); + } + + compute_aabb(); +} + +void Mesh::set_vertices(const std::vector& vertices) { + vertices_ = vertices; + compute_aabb(); +} + +void Mesh::set_indices(const std::vector& indices) { + indices_ = indices; +} + +void Mesh::set_material(MaterialHandle material_id) { + material_id_ = material_id; +} + +void Mesh::compute_aabb() { + ARE_PROFILE_FUNCTION(); + + if (vertices_.empty()) { + aabb_ = AABB::invalid(); + return; + } + + aabb_ = AABB(vertices_[0].position_); + + for (size_t i = 1; i < vertices_.size(); ++i) { + aabb_.expand(vertices_[i].position_); + } +} + +void Mesh::compute_tangents() { + ARE_PROFILE_FUNCTION(); + + if (vertices_.empty() || indices_.empty()) { + ARE_LOG_WARN("Mesh: Cannot compute tangents for empty mesh"); + return; + } + + if (indices_.size() % 3 != 0) { + ARE_LOG_ERROR("Mesh: Index count is not a multiple of 3"); + return; + } + + // Initialize tangents to zero + std::vector tangents(vertices_.size(), Vec3(0.0f)); + std::vector bitangents(vertices_.size(), Vec3(0.0f)); + + // Calculate tangents for each triangle + for (size_t i = 0; i < indices_.size(); i += 3) { + uint32_t i0 = indices_[i]; + uint32_t i1 = indices_[i + 1]; + uint32_t i2 = indices_[i + 2]; + + if (i0 >= vertices_.size() || i1 >= vertices_.size() || i2 >= vertices_.size()) { + ARE_LOG_ERROR("Mesh: Invalid index in compute_tangents"); + continue; + } + + const Vertex& v0 = vertices_[i0]; + const Vertex& v1 = vertices_[i1]; + const Vertex& v2 = vertices_[i2]; + + // Calculate edges + Vec3 edge1 = v1.position_ - v0.position_; + Vec3 edge2 = v2.position_ - v0.position_; + + Vec2 delta_uv1 = v1.texcoord_ - v0.texcoord_; + Vec2 delta_uv2 = v2.texcoord_ - v0.texcoord_; + + // Calculate tangent and bitangent + Real f = delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y; + + if (std::abs(f) < are_epsilon) { + // Degenerate UV coordinates, use arbitrary tangent + Vec3 tangent, bitangent; + create_orthonormal_basis(v0.normal_, tangent, bitangent); + + tangents[i0] += tangent; + tangents[i1] += tangent; + tangents[i2] += tangent; + continue; + } + + f = 1.0f / f; + + Vec3 tangent; + tangent.x = f * (delta_uv2.y * edge1.x - delta_uv1.y * edge2.x); + tangent.y = f * (delta_uv2.y * edge1.y - delta_uv1.y * edge2.y); + tangent.z = f * (delta_uv2.y * edge1.z - delta_uv1.y * edge2.z); + + Vec3 bitangent; + bitangent.x = f * (-delta_uv2.x * edge1.x + delta_uv1.x * edge2.x); + bitangent.y = f * (-delta_uv2.x * edge1.y + delta_uv1.x * edge2.y); + bitangent.z = f * (-delta_uv2.x * edge1.z + delta_uv1.x * edge2.z); + + // Accumulate tangents for each vertex + tangents[i0] += tangent; + tangents[i1] += tangent; + tangents[i2] += tangent; + + bitangents[i0] += bitangent; + bitangents[i1] += bitangent; + bitangents[i2] += bitangent; + } + + // Orthogonalize and normalize tangents (Gram-Schmidt) + for (size_t i = 0; i < vertices_.size(); ++i) { + const Vec3& n = vertices_[i].normal_; + const Vec3& t = tangents[i]; + + // Gram-Schmidt orthogonalize + Vec3 tangent = t - n * glm::dot(n, t); + + Real length = glm::length(tangent); + if (length < are_epsilon) { + // If tangent is parallel to normal, create arbitrary tangent + create_orthonormal_basis(n, tangent, bitangents[i]); + } else { + tangent /= length; + } + + // Check handedness + Real handedness = glm::dot(glm::cross(n, t), bitangents[i]); + if (handedness < 0.0f) { + tangent = -tangent; + } + + vertices_[i].tangent_ = tangent; + } +} + +bool Mesh::get_triangle(size_t triangle_index, Vertex& v0, Vertex& v1, Vertex& v2) const { + if (triangle_index >= get_triangle_count()) { + return false; + } + + size_t base_index = triangle_index * 3; + uint32_t i0 = indices_[base_index]; + uint32_t i1 = indices_[base_index + 1]; + uint32_t i2 = indices_[base_index + 2]; + + if (i0 >= vertices_.size() || i1 >= vertices_.size() || i2 >= vertices_.size()) { + ARE_LOG_ERROR("Mesh: Invalid indices in get_triangle"); + return false; + } + + v0 = vertices_[i0]; + v1 = vertices_[i1]; + v2 = vertices_[i2]; + + return true; +} + +} // namespace are diff --git a/src/scene/point_light.cpp b/src/scene/point_light.cpp new file mode 100644 index 0000000..5a51b24 --- /dev/null +++ b/src/scene/point_light.cpp @@ -0,0 +1,97 @@ +/** + * @file point_light.cpp + * @brief Implementation of PointLight class + */ + +#include +#include +#include + +namespace are { + +PointLight::PointLight() + : Light(LightType::ARE_LIGHT_POINT) + , position_(0.0f) + , range_(10.0f) + , attenuation_constant_(1.0f) + , attenuation_linear_(0.09f) + , attenuation_quadratic_(0.032f) { +} + +PointLight::PointLight(const Vec3& position, const Vec3& color, Real intensity, Real range) + : Light(LightType::ARE_LIGHT_POINT) + , position_(position) + , range_(range) + , attenuation_constant_(1.0f) + , attenuation_linear_(0.09f) + , attenuation_quadratic_(0.032f) { + set_color(color); + set_intensity(intensity); + set_range(range); +} + +void PointLight::set_position(const Vec3& position) { + position_ = position; +} + +void PointLight::set_range(Real range) { + if (range <= 0.0f) { + ARE_LOG_WARN("PointLight: Invalid range (must be positive), using default"); + range_ = 10.0f; + } else { + range_ = range; + } +} + +void PointLight::set_attenuation(Real constant, Real linear, Real quadratic) { + attenuation_constant_ = std::max(0.0f, constant); + attenuation_linear_ = std::max(0.0f, linear); + attenuation_quadratic_ = std::max(0.0f, quadratic); + + // Ensure at least some attenuation to avoid division issues + if (attenuation_constant_ < are_epsilon && + attenuation_linear_ < are_epsilon && + attenuation_quadratic_ < are_epsilon) { + ARE_LOG_WARN("PointLight: All attenuation factors near zero, setting constant to 1.0"); + attenuation_constant_ = 1.0f; + } +} + +Real PointLight::calculate_attenuation(Real distance) const { + // Standard attenuation formula: 1 / (constant + linear*d + quadratic*d^2) + Real attenuation = attenuation_constant_ + + attenuation_linear_ * distance + + attenuation_quadratic_ * distance * distance; + return 1.0f / std::max(attenuation, are_epsilon); +} + +LightData PointLight::pack() const { + LightData data; + + // position_type_: xyz = position, w = light type + data.position_type_ = Vec4(position_, + static_cast(LightType::ARE_LIGHT_POINT)); + + // direction_range_: xyz unused for point light, w = range + data.direction_range_ = Vec4(0.0f, 0.0f, 0.0f, range_); + + // color_intensity_: xyz = color, w = intensity + data.color_intensity_ = Vec4(color_, intensity_); + + // params_: x = cast_shadows, y = constant, z = linear, w = quadratic + data.params_ = Vec4( + cast_shadows_ ? 1.0f : 0.0f, + attenuation_constant_, + attenuation_linear_, + attenuation_quadratic_ + ); + + return data; +} + +bool PointLight::affects_point(const Vec3& point) const { + Real distance = glm::length(point - position_); + return distance <= range_; +} + +} // namespace are diff --git a/src/scene/scene_manager.cpp b/src/scene/scene_manager.cpp new file mode 100644 index 0000000..c198af3 --- /dev/null +++ b/src/scene/scene_manager.cpp @@ -0,0 +1,303 @@ +/** + * @file scene_manager.cpp + * @brief Implementation of SceneManager class + */ + +#include +#include +#include + +namespace are { + +SceneManager::SceneManager() + : next_mesh_handle_(1) + , next_material_handle_(1) + , next_light_handle_(1) + , dirty_(false) { +} + +SceneManager::~SceneManager() { + clear(); +} + +MeshHandle SceneManager::add_mesh(const Mesh &mesh) { + ARE_PROFILE_FUNCTION(); + + if (mesh.is_empty()) { + ARE_LOG_WARN("SceneManager: Attempting to add empty mesh"); + return are_invalid_handle; + } + + MeshHandle handle = next_mesh_handle_++; + meshes_.push_back(mesh); + mesh_handle_map_[handle] = meshes_.size() - 1; + + dirty_ = true; + + ARE_LOG_DEBUG("SceneManager: Added mesh with handle " + std::to_string(handle)); + return handle; +} + +void SceneManager::remove_mesh(MeshHandle handle) { + ARE_PROFILE_FUNCTION(); + + auto it = mesh_handle_map_.find(handle); + if (it == mesh_handle_map_.end()) { + ARE_LOG_WARN("SceneManager: Attempting to remove invalid mesh handle"); + return; + } + + size_t index = it->second; + + // Swap with last element and pop + if (index < meshes_.size() - 1) { + meshes_[index] = meshes_.back(); + + // Update handle map for swapped element + for (auto &pair : mesh_handle_map_) { + if (pair.second == meshes_.size() - 1) { + pair.second = index; + break; + } + } + } + + meshes_.pop_back(); + mesh_handle_map_.erase(it); + + dirty_ = true; + + ARE_LOG_DEBUG("SceneManager: Removed mesh with handle " + std::to_string(handle)); +} + +void SceneManager::update_mesh(MeshHandle handle, const Mesh &mesh) { + ARE_PROFILE_FUNCTION(); + + Mesh *existing = get_mesh(handle); + if (!existing) { + ARE_LOG_WARN("SceneManager: Attempting to update invalid mesh handle"); + return; + } + + *existing = mesh; + dirty_ = true; +} + +Mesh *SceneManager::get_mesh(MeshHandle handle) { + auto it = mesh_handle_map_.find(handle); + if (it == mesh_handle_map_.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= meshes_.size()) { + ARE_LOG_ERROR("SceneManager: Mesh handle map corrupted"); + return nullptr; + } + + return &meshes_[index]; +} + +const Mesh *SceneManager::get_mesh(MeshHandle handle) const { + auto it = mesh_handle_map_.find(handle); + if (it == mesh_handle_map_.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= meshes_.size()) { + ARE_LOG_ERROR("SceneManager: Mesh handle map corrupted"); + return nullptr; + } + + return &meshes_[index]; +} + +MaterialHandle SceneManager::add_material(const Material &material) { + ARE_PROFILE_FUNCTION(); + + MaterialHandle handle = next_material_handle_++; + materials_.push_back(material); + material_handle_map_[handle] = materials_.size() - 1; + + ARE_LOG_DEBUG("SceneManager: Added material with handle " + std::to_string(handle)); + return handle; +} + +void SceneManager::remove_material(MaterialHandle handle) { + ARE_PROFILE_FUNCTION(); + + auto it = material_handle_map_.find(handle); + if (it == material_handle_map_.end()) { + ARE_LOG_WARN("SceneManager: Attempting to remove invalid material handle"); + return; + } + + size_t index = it->second; + + // Swap with last element and pop + if (index < materials_.size() - 1) { + materials_[index] = materials_.back(); + + // Update handle map for swapped element + for (auto &pair : material_handle_map_) { + if (pair.second == materials_.size() - 1) { + pair.second = index; + break; + } + } + } + + materials_.pop_back(); + material_handle_map_.erase(it); + + ARE_LOG_DEBUG("SceneManager: Removed material with handle " + std::to_string(handle)); +} + +void SceneManager::update_material(MaterialHandle handle, const Material &material) { + ARE_PROFILE_FUNCTION(); + + Material *existing = get_material(handle); + if (!existing) { + ARE_LOG_WARN("SceneManager: Attempting to update invalid material handle"); + return; + } + + *existing = material; +} + +Material *SceneManager::get_material(MaterialHandle handle) { + auto it = material_handle_map_.find(handle); + if (it == material_handle_map_.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= materials_.size()) { + ARE_LOG_ERROR("SceneManager: Material handle map corrupted"); + return nullptr; + } + + return &materials_[index]; +} + +const Material *SceneManager::get_material(MaterialHandle handle) const { + auto it = material_handle_map_.find(handle); + if (it == material_handle_map_.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= materials_.size()) { + ARE_LOG_ERROR("SceneManager: Material handle map corrupted"); + return nullptr; + } + + return &materials_[index]; +} + +LightHandle SceneManager::add_light(const std::shared_ptr &light) { + ARE_PROFILE_FUNCTION(); + + if (!light) { + ARE_LOG_WARN("SceneManager: Attempting to add null light"); + return are_invalid_handle; + } + + LightHandle handle = next_light_handle_++; + lights_.push_back(light); + light_handle_map_[handle] = lights_.size() - 1; + + dirty_ = true; + + ARE_LOG_DEBUG("SceneManager: Added light with handle " + std::to_string(handle)); + return handle; +} + +void SceneManager::remove_light(LightHandle handle) { + ARE_PROFILE_FUNCTION(); + + auto it = light_handle_map_.find(handle); + if (it == light_handle_map_.end()) { + ARE_LOG_WARN("SceneManager: Attempting to remove invalid light handle"); + return; + } + + size_t index = it->second; + + // Swap with last element and pop + if (index < lights_.size() - 1) { + lights_[index] = lights_.back(); + + // Update handle map for swapped element + for (auto &pair : light_handle_map_) { + if (pair.second == lights_.size() - 1) { + pair.second = index; + break; + } + } + } + + lights_.pop_back(); + light_handle_map_.erase(it); + + dirty_ = true; + + ARE_LOG_DEBUG("SceneManager: Removed light with handle " + std::to_string(handle)); +} + +std::shared_ptr SceneManager::get_light(LightHandle handle) { + auto it = light_handle_map_.find(handle); + if (it == light_handle_map_.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= lights_.size()) { + ARE_LOG_ERROR("SceneManager: Light handle map corrupted"); + return nullptr; + } + + return lights_[index]; +} + +size_t SceneManager::get_total_triangle_count() const { + ARE_PROFILE_FUNCTION(); + + size_t total = 0; + for (const auto &mesh : meshes_) { + total += mesh.get_triangle_count(); + } + return total; +} + +void SceneManager::clear() { + ARE_PROFILE_FUNCTION(); + + meshes_.clear(); + materials_.clear(); + lights_.clear(); + + mesh_handle_map_.clear(); + material_handle_map_.clear(); + light_handle_map_.clear(); + + next_mesh_handle_ = 1; + next_material_handle_ = 1; + next_light_handle_ = 1; + + dirty_ = true; + + ARE_LOG_INFO("SceneManager: Cleared all scene data"); +} + +void SceneManager::compact() { + ARE_PROFILE_FUNCTION(); + + // Remove invalid entries (this is a placeholder for future optimization) + // Currently, the handle-based system ensures no invalid entries exist + + ARE_LOG_DEBUG("SceneManager: Compacted scene data"); +} + +} // namespace are diff --git a/src/scene/spot_light.cpp b/src/scene/spot_light.cpp new file mode 100644 index 0000000..2adab35 --- /dev/null +++ b/src/scene/spot_light.cpp @@ -0,0 +1,152 @@ +/** + * @file spot_light.cpp + * @brief Implementation of SpotLight class + */ + +#include +#include +#include +#include + +namespace are { + +SpotLight::SpotLight() + : Light(LightType::ARE_LIGHT_SPOT) + , position_(0.0f) + , direction_(0.0f, -1.0f, 0.0f) + , inner_angle_(30.0f) + , outer_angle_(45.0f) + , range_(10.0f) + , cos_inner_(std::cos(degrees_to_radians(30.0f))) + , cos_outer_(std::cos(degrees_to_radians(45.0f))) { +} + +SpotLight::SpotLight(const Vec3& position, const Vec3& direction, + Real inner_angle, Real outer_angle, + const Vec3& color, Real intensity) + : Light(LightType::ARE_LIGHT_SPOT) + , position_(position) + , range_(10.0f) { + set_direction(direction); + set_inner_angle(inner_angle); + set_outer_angle(outer_angle); + set_color(color); + set_intensity(intensity); +} + +void SpotLight::set_position(const Vec3& position) { + position_ = position; +} + +void SpotLight::set_direction(const Vec3& direction) { + Real length = glm::length(direction); + if (length < are_epsilon) { + ARE_LOG_WARN("SpotLight: Invalid direction vector (zero length), using default"); + direction_ = Vec3(0.0f, -1.0f, 0.0f); + } else { + direction_ = direction / length; + } +} + +void SpotLight::set_inner_angle(Real angle) { + // Clamp to valid range [0, 90] degrees + inner_angle_ = clamp(angle, 0.0f, 90.0f); + cos_inner_ = std::cos(degrees_to_radians(inner_angle_)); + + // Ensure inner angle is not larger than outer angle + if (inner_angle_ > outer_angle_) { + ARE_LOG_WARN("SpotLight: Inner angle larger than outer angle, adjusting outer angle"); + outer_angle_ = inner_angle_; + cos_outer_ = cos_inner_; + } +} + +void SpotLight::set_outer_angle(Real angle) { + // Clamp to valid range [0, 90] degrees + outer_angle_ = clamp(angle, 0.0f, 90.0f); + cos_outer_ = std::cos(degrees_to_radians(outer_angle_)); + + // Ensure outer angle is not smaller than inner angle + if (outer_angle_ < inner_angle_) { + ARE_LOG_WARN("SpotLight: Outer angle smaller than inner angle, adjusting inner angle"); + inner_angle_ = outer_angle_; + cos_inner_ = cos_outer_; + } +} + +void SpotLight::set_range(Real range) { + if (range <= 0.0f) { + ARE_LOG_WARN("SpotLight: Invalid range (must be positive), using default"); + range_ = 10.0f; + } else { + range_ = range; + } +} + +Real SpotLight::calculate_spot_factor(const Vec3& to_point) const { + // Calculate angle between light direction and direction to point + Real cos_angle = glm::dot(direction_, glm::normalize(to_point)); + + // Outside outer cone + if (cos_angle < cos_outer_) { + return 0.0f; + } + + // Inside inner cone + if (cos_angle > cos_inner_) { + return 1.0f; + } + + // Smooth transition between inner and outer cone + Real delta = cos_inner_ - cos_outer_; + if (delta < are_epsilon) { + return 1.0f; + } + + return (cos_angle - cos_outer_) / delta; +} + +LightData SpotLight::pack() const { + LightData data; + + // position_type_: xyz = position, w = light type + data.position_type_ = Vec4(position_, + static_cast(LightType::ARE_LIGHT_SPOT)); + + // direction_range_: xyz = direction, w = range + data.direction_range_ = Vec4(direction_, range_); + + // color_intensity_: xyz = color, w = intensity + data.color_intensity_ = Vec4(color_, intensity_); + + // params_: x = cast_shadows, y = cos_inner, z = cos_outer, w unused + data.params_ = Vec4( + cast_shadows_ ? 1.0f : 0.0f, + cos_inner_, + cos_outer_, + 0.0f + ); + + return data; +} + +bool SpotLight::affects_point(const Vec3& point) const { + // Check if point is within range + Vec3 to_point = point - position_; + Real distance = glm::length(to_point); + + if (distance > range_) { + return false; + } + + // Check if point is within spotlight cone + if (distance > are_epsilon) { + to_point /= distance; + Real cos_angle = glm::dot(direction_, to_point); + return cos_angle >= cos_outer_; + } + + return true; +} + +} // namespace are diff --git a/src/texture/compute_texture.cpp b/src/texture/compute_texture.cpp new file mode 100644 index 0000000..fd6063a --- /dev/null +++ b/src/texture/compute_texture.cpp @@ -0,0 +1,61 @@ +/** + * @file compute_raytracer.cpp + * @brief Compute shader ray tracing implementation (placeholder) + */ + +#include +#include + +namespace are { + +ComputeRayTracer::ComputeRayTracer(const RayTracingConfig& config) + : RayTracer(config) + , bvh_buffer_(0) + , triangle_buffer_(0) + , material_buffer_(0) + , light_buffer_(0) + , buffers_initialized_(false) +{ + ARE_LOG_WARN("Compute shader ray tracer is not yet implemented"); + ARE_LOG_INFO("Compute ray tracer initialized (placeholder)"); +} + +ComputeRayTracer::~ComputeRayTracer() { + // TODO: Clean up GPU buffers + ARE_LOG_INFO("Compute ray tracer destroyed"); +} + +void ComputeRayTracer::render(const SceneManager& scene, + const Camera& camera, + const GBuffer* gbuffer, + uint32_t output_texture) { + ARE_LOG_WARN("Compute shader ray tracing not implemented yet, skipping render"); + + // TODO: Implement compute shader ray tracing + // For now, just do nothing +} + +void ComputeRayTracer::update_bvh(const BVH& bvh) { + ARE_LOG_INFO("BVH update for compute ray tracer (not implemented)"); + + // TODO: Upload BVH to GPU +} + +void ComputeRayTracer::initialize_compute_shader(const std::string& shader_dir) { + // TODO: Load and compile compute shader + ARE_LOG_WARN("Compute shader initialization not implemented"); +} + +void ComputeRayTracer::upload_scene_data(const SceneManager& scene) { + // TODO: Upload scene data to GPU +} + +void ComputeRayTracer::upload_bvh_data(const BVH& bvh) { + // TODO: Upload BVH to GPU +} + +void ComputeRayTracer::upload_camera_data(const Camera& camera) { + // TODO: Upload camera data to GPU +} + +} // namespace are diff --git a/src/texture/sampler.cpp b/src/texture/sampler.cpp new file mode 100644 index 0000000..945c407 --- /dev/null +++ b/src/texture/sampler.cpp @@ -0,0 +1,35 @@ +/** + * @file sampler.cpp + * @brief Texture sampling utilities implementation + */ + +#include +#include +#include + +namespace are { + +Vec4 Sampler::sample(const Texture& texture, const Vec2& uv) { + // Default to bilinear sampling + return sample_bilinear(texture, uv); +} + +Vec4 Sampler::sample_bilinear(const Texture& texture, const Vec2& uv) { + // TODO: Implement CPU-side bilinear sampling + // For now, return white + ARE_LOG_WARN("CPU texture sampling not implemented"); + return Vec4(1.0f); +} + +Vec4 Sampler::sample_nearest(const Texture& texture, const Vec2& uv) { + // TODO: Implement CPU-side nearest sampling + ARE_LOG_WARN("CPU texture sampling not implemented"); + return Vec4(1.0f); +} + +Vec2 Sampler::apply_wrap(const Vec2& uv, TextureWrap wrap) { + // TODO: Implement wrapping modes + return uv; +} + +} // namespace are diff --git a/src/texture/texture.cpp b/src/texture/texture.cpp new file mode 100644 index 0000000..12b4eb2 --- /dev/null +++ b/src/texture/texture.cpp @@ -0,0 +1,205 @@ +/** + * @file texture.cpp + * @brief Implementation of texture class + */ + +#include +#include +#include +#include + +namespace are { + +// Helper function to convert TextureFormat to OpenGL format +static GLenum get_gl_internal_format(TextureFormat format) { + switch (format) { + case TextureFormat::ARE_TEXTURE_R8: return GL_R8; + case TextureFormat::ARE_TEXTURE_RG8: return GL_RG8; + case TextureFormat::ARE_TEXTURE_RGB8: return GL_RGB8; + case TextureFormat::ARE_TEXTURE_RGBA8: return GL_RGBA8; + case TextureFormat::ARE_TEXTURE_R16F: return GL_R16F; + case TextureFormat::ARE_TEXTURE_RG16F: return GL_RG16F; + case TextureFormat::ARE_TEXTURE_RGB16F: return GL_RGB16F; + case TextureFormat::ARE_TEXTURE_RGBA16F: return GL_RGBA16F; + case TextureFormat::ARE_TEXTURE_R32F: return GL_R32F; + case TextureFormat::ARE_TEXTURE_RG32F: return GL_RG32F; + case TextureFormat::ARE_TEXTURE_RGB32F: return GL_RGB32F; + case TextureFormat::ARE_TEXTURE_RGBA32F: return GL_RGBA32F; + default: return GL_RGBA8; + } +} + +static GLenum get_gl_format(int channels) { + switch (channels) { + case 1: return GL_RED; + case 2: return GL_RG; + case 3: return GL_RGB; + case 4: return GL_RGBA; + default: return GL_RGBA; + } +} + +static GLenum get_gl_filter(TextureFilter filter) { + switch (filter) { + case TextureFilter::ARE_TEXTURE_FILTER_NEAREST: + return GL_NEAREST; + case TextureFilter::ARE_TEXTURE_FILTER_LINEAR: + return GL_LINEAR; + case TextureFilter::ARE_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: + return GL_NEAREST_MIPMAP_NEAREST; + case TextureFilter::ARE_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: + return GL_LINEAR_MIPMAP_NEAREST; + case TextureFilter::ARE_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: + return GL_NEAREST_MIPMAP_LINEAR; + case TextureFilter::ARE_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: + return GL_LINEAR_MIPMAP_LINEAR; + default: + return GL_LINEAR; + } +} + +static GLenum get_gl_wrap(TextureWrap wrap) { + switch (wrap) { + case TextureWrap::ARE_TEXTURE_WRAP_REPEAT: + return GL_REPEAT; + case TextureWrap::ARE_TEXTURE_WRAP_CLAMP_TO_EDGE: + return GL_CLAMP_TO_EDGE; + case TextureWrap::ARE_TEXTURE_WRAP_CLAMP_TO_BORDER: + return GL_CLAMP_TO_BORDER; + case TextureWrap::ARE_TEXTURE_WRAP_MIRRORED_REPEAT: + return GL_MIRRORED_REPEAT; + default: + return GL_REPEAT; + } +} + +Texture::Texture() + : texture_id_(0) + , width_(0) + , height_(0) + , format_(TextureFormat::ARE_TEXTURE_RGBA8) +{ +} + +Texture::~Texture() { + destroy(); +} + +bool Texture::load_from_file(const std::string& filepath, + TextureFormat format, + bool generate_mipmaps) { + // Load image data + ImageData image = load_image(filepath, true); // Flip vertically for OpenGL + + if (!image.is_valid()) { + ARE_LOG_ERROR("Failed to load image: " + filepath); + return false; + } + + return create_from_data(image.width_, image.height_, format, + image.data_.data(), generate_mipmaps); +} + +bool Texture::create_from_data(int width, int height, + TextureFormat format, + const void* data, + bool generate_mipmaps) { + if (width <= 0 || height <= 0) { + ARE_LOG_ERROR("Invalid texture dimensions"); + return false; + } + + // Delete old texture if exists + if (texture_id_ != 0) { + destroy(); + } + + width_ = width; + height_ = height; + format_ = format; + + // Create OpenGL texture + glGenTextures(1, &texture_id_); + glBindTexture(GL_TEXTURE_2D, texture_id_); + + // Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + generate_mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Upload texture data + GLenum internal_format = get_gl_internal_format(format); + GLenum data_format = GL_RGBA; // Assume RGBA input + + // Determine data format from input (assume 4 channels for now) + if (data) { + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, + data_format, GL_UNSIGNED_BYTE, data); + } else { + // Create empty texture + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, + data_format, GL_UNSIGNED_BYTE, nullptr); + } + + // Generate mipmaps + if (generate_mipmaps) { + glGenerateMipmap(GL_TEXTURE_2D); + } + + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +void Texture::bind(int unit) const { + if (texture_id_ == 0) { + ARE_LOG_WARN("Attempting to bind invalid texture"); + return; + } + + glActiveTexture(GL_TEXTURE0 + unit); + glBindTexture(GL_TEXTURE_2D, texture_id_); +} + +void Texture::unbind() const { + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::set_filter(TextureFilter min_filter, TextureFilter mag_filter) { + if (texture_id_ == 0) return; + + glBindTexture(GL_TEXTURE_2D, texture_id_); + 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 (texture_id_ == 0) return; + + glBindTexture(GL_TEXTURE_2D, texture_id_); + 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 (texture_id_ == 0) return; + + glBindTexture(GL_TEXTURE_2D, texture_id_); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::destroy() { + if (texture_id_ != 0) { + glDeleteTextures(1, &texture_id_); + texture_id_ = 0; + width_ = 0; + height_ = 0; + } +} + +} // namespace are diff --git a/src/texture/texture_manager.cpp b/src/texture/texture_manager.cpp new file mode 100644 index 0000000..a05319a --- /dev/null +++ b/src/texture/texture_manager.cpp @@ -0,0 +1,191 @@ +/** + * @file texture_manager.cpp + * @brief Implementation of texture manager + */ + +#include +#include + +namespace are { + +TextureManager::TextureManager() + : next_handle_(1) +{ + ARE_LOG_INFO("Texture manager initialized"); +} + +TextureManager::~TextureManager() { + clear(); + ARE_LOG_INFO("Texture manager destroyed"); +} + +TextureHandle TextureManager::load_texture(const std::string& filepath, + TextureFormat format, + bool generate_mipmaps) { + // Check if texture already loaded + auto it = path_to_handle_.find(filepath); + if (it != path_to_handle_.end()) { + ARE_LOG_INFO("Texture already loaded: " + filepath); + return it->second; + } + + // Create new texture + auto texture = std::make_unique(); + + if (!texture->load_from_file(filepath, format, generate_mipmaps)) { + ARE_LOG_ERROR("Failed to load texture: " + filepath); + return are_invalid_handle; + } + + // Assign handle + TextureHandle handle = next_handle_++; + + // Store texture + textures_[handle] = std::move(texture); + path_to_handle_[filepath] = handle; + + ARE_LOG_INFO("Texture loaded: " + filepath + " (handle: " + std::to_string(handle) + ")"); + + return handle; +} + +TextureHandle TextureManager::create_texture(const std::string& name, + int width, int height, + TextureFormat format, + const void* data, + bool generate_mipmaps) { + // Check if texture with this name already exists + auto it = path_to_handle_.find(name); + if (it != path_to_handle_.end()) { + ARE_LOG_WARN("Texture with name already exists: " + name); + return it->second; + } + + // Create new texture + auto texture = std::make_unique(); + + if (!texture->create_from_data(width, height, format, data, generate_mipmaps)) { + ARE_LOG_ERROR("Failed to create texture: " + name); + return are_invalid_handle; + } + + // Assign handle + TextureHandle handle = next_handle_++; + + // Store texture + textures_[handle] = std::move(texture); + path_to_handle_[name] = handle; + + ARE_LOG_INFO("Texture created: " + name + " (handle: " + std::to_string(handle) + ")"); + + return handle; +} + +Texture* TextureManager::get_texture(TextureHandle handle) { + auto it = textures_.find(handle); + if (it != textures_.end()) { + return it->second.get(); + } + return nullptr; +} + +const Texture* TextureManager::get_texture(TextureHandle handle) const { + auto it = textures_.find(handle); + if (it != textures_.end()) { + return it->second.get(); + } + return nullptr; +} + +void TextureManager::unload_texture(TextureHandle handle) { + auto it = textures_.find(handle); + if (it == textures_.end()) { + return; + } + + // Remove from path map + for (auto path_it = path_to_handle_.begin(); path_it != path_to_handle_.end(); ++path_it) { + if (path_it->second == handle) { + path_to_handle_.erase(path_it); + break; + } + } + + // Remove texture + textures_.erase(it); + + ARE_LOG_INFO("Texture unloaded (handle: " + std::to_string(handle) + ")"); +} + +void TextureManager::clear() { + textures_.clear(); + path_to_handle_.clear(); + next_handle_ = 1; + + ARE_LOG_INFO("All textures cleared"); +} + +size_t TextureManager::get_memory_usage() const { + size_t total = 0; + + for (const auto& [handle, texture] : textures_) { + if (texture && texture->is_valid()) { + // Estimate memory usage (width * height * bytes_per_pixel) + int width = texture->get_width(); + int height = texture->get_height(); + + // Estimate bytes per pixel based on format + int bytes_per_pixel = 4; // Default RGBA8 + + switch (texture->get_format()) { + case TextureFormat::ARE_TEXTURE_R8: + bytes_per_pixel = 1; + break; + case TextureFormat::ARE_TEXTURE_RG8: + bytes_per_pixel = 2; + break; + case TextureFormat::ARE_TEXTURE_RGB8: + bytes_per_pixel = 3; + break; + case TextureFormat::ARE_TEXTURE_RGBA8: + bytes_per_pixel = 4; + break; + case TextureFormat::ARE_TEXTURE_R16F: + bytes_per_pixel = 2; + break; + case TextureFormat::ARE_TEXTURE_RG16F: + bytes_per_pixel = 4; + break; + case TextureFormat::ARE_TEXTURE_RGB16F: + bytes_per_pixel = 6; + break; + case TextureFormat::ARE_TEXTURE_RGBA16F: + bytes_per_pixel = 8; + break; + case TextureFormat::ARE_TEXTURE_R32F: + bytes_per_pixel = 4; + break; + case TextureFormat::ARE_TEXTURE_RG32F: + bytes_per_pixel = 8; + break; + case TextureFormat::ARE_TEXTURE_RGB32F: + bytes_per_pixel = 12; + break; + case TextureFormat::ARE_TEXTURE_RGBA32F: + bytes_per_pixel = 16; + break; + } + + size_t texture_size = width * height * bytes_per_pixel; + + // Account for mipmaps (approximately 1.33x base size) + texture_size = static_cast(texture_size * 1.33); + + total += texture_size; + } + } + + return total; +} + +} // namespace are diff --git a/write.sh b/write.sh new file mode 100644 index 0000000..b622c6c --- /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_headers.md" + +# 清空或创建输出文件 +> "$OUTPUT_FILE" + +echo "正在扫描目录: $TARGET_DIR" +# 使用 find 命令查找所有 .h 文件 +H_FILES=$(find "$TARGET_DIR" -type f -name "*.h") + +# 检查是否找到了 .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 中。"