Refactor:重写所有代码、重新设计架构,实现大体架构和G-Buffer渲染

master
ternaryop8479 2026-02-09 22:16:57 +08:00
parent 136677c456
commit dbf5b8579c
48 changed files with 5686 additions and 0 deletions

121
CMakeLists.txt Normal file
View File

@ -0,0 +1,121 @@
cmake_minimum_required(VERSION 3.15)
project(AuroraRenderingEngine VERSION 1.0.0 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Options
option(ARE_BUILD_TESTS "Build tests" ON)
option(ARE_BUILD_EXAMPLES "Build examples" ON)
# Find dependencies
find_package(OpenGL REQUIRED)
find_package(glm REQUIRED)
find_package(glfw3 REQUIRED)
# GLAD library
add_library(glad STATIC
lib/glad/src/glad.c
)
target_include_directories(glad PUBLIC lib/glad/include)
# stb_image library
set(STB_IMAGE_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/lib/stb/stb_image.cpp)
if(NOT EXISTS ${STB_IMAGE_SOURCE})
file(WRITE ${STB_IMAGE_SOURCE}
"#define STB_IMAGE_IMPLEMENTATION
#include \"stb_image.h\"
")
endif()
add_library(stb_image STATIC ${STB_IMAGE_SOURCE})
target_include_directories(stb_image PUBLIC lib/stb)
# Collect all source files automatically
file(GLOB_RECURSE ARE_SOURCES
"src/*.cpp"
"src/*.c"
)
# Collect all header files
file(GLOB_RECURSE ARE_HEADERS
"include/*.h"
"include/*.hpp"
)
# Aurora Rendering Engine library
add_library(are STATIC ${ARE_SOURCES} ${ARE_HEADERS})
target_include_directories(are
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(are
PUBLIC
OpenGL::GL
glm::glm
PRIVATE
glad
stb_image
)
# Copy shaders to build directory
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
# Examples
if(ARE_BUILD_EXAMPLES)
# Cornell Box example
add_executable(cornell_box examples/cornell_box.cpp)
target_link_libraries(cornell_box
PRIVATE
are
glfw
)
# Copy shaders to example directory
add_custom_command(TARGET cornell_box POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/shaders
$<TARGET_FILE_DIR:cornell_box>/shaders
)
endif()
# Tests
if(ARE_BUILD_TESTS)
enable_testing()
# Basic test
add_executable(test_basic tests/test_basic.cpp)
target_link_libraries(test_basic PRIVATE are)
add_test(NAME BasicTest COMMAND test_basic)
endif()
# Install
install(TARGETS are
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
)
install(DIRECTORY shaders/
DESTINATION share/are/shaders
)
# Print configuration
message(STATUS "Aurora Rendering Engine Configuration:")
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS " Build examples: ${ARE_BUILD_EXAMPLES}")
message(STATUS " Build tests: ${ARE_BUILD_TESTS}")
message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}")

BIN
examples/cornell_box Normal file

Binary file not shown.

473
examples/cornell_box.cpp Normal file
View File

@ -0,0 +1,473 @@
#include <core/renderer.h>
#include <scene/scene.h>
#include <scene/camera.h>
#include <scene/mesh.h>
#include <scene/material.h>
#include <scene/light.h>
#include <utils/logger.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <memory>
using namespace are;
// Window dimensions
const uint WINDOW_WIDTH = 800;
const uint WINDOW_HEIGHT = 800;
// Global state
GLFWwindow* g_window = nullptr;
std::unique_ptr<Renderer> g_renderer = nullptr;
std::unique_ptr<Scene> g_scene = nullptr;
// GLFW error callback
void glfw_error_callback(int error, const char* description) {
Logger::error("GLFW Error " + std::to_string(error) + ": " + std::string(description));
}
/// @brief Create a quad mesh
std::shared_ptr<Mesh> create_quad(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3,
const Vec3& normal, uint material_id) {
auto mesh = std::make_shared<Mesh>();
std::vector<Vertex> vertices = {
{v0, normal, Vec2(0.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f)},
{v1, normal, Vec2(1.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f)},
{v2, normal, Vec2(1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f)},
{v3, normal, Vec2(0.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f)}
};
std::vector<uint> indices = {0, 1, 2, 0, 2, 3};
mesh->set_vertices(vertices);
mesh->set_indices(indices);
mesh->set_material(material_id);
return mesh;
}
/// @brief Create a box mesh
std::shared_ptr<Mesh> create_box(const Vec3& min, const Vec3& max, uint material_id) {
auto mesh = std::make_shared<Mesh>();
std::vector<Vertex> vertices = {
// Front face
{{min.x, min.y, max.z}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, min.y, max.z}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, max.y, max.z}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
{{min.x, max.y, max.z}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
// Back face
{{max.x, min.y, min.z}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}},
{{min.x, min.y, min.z}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}},
{{min.x, max.y, min.z}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}},
{{max.x, max.y, min.z}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}},
// Top face
{{min.x, max.y, max.z}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, max.y, max.z}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, max.y, min.z}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
{{min.x, max.y, min.z}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
// Bottom face
{{min.x, min.y, min.z}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, min.y, min.z}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{max.x, min.y, max.z}, {0.0f, -1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
{{min.x, min.y, max.z}, {0.0f, -1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
// Right face
{{max.x, min.y, max.z}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
{{max.x, min.y, min.z}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
{{max.x, max.y, min.z}, {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}},
{{max.x, max.y, max.z}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}},
// Left face
{{min.x, min.y, min.z}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{min.x, min.y, max.z}, {-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{min.x, max.y, max.z}, {-1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
{{min.x, max.y, min.z}, {-1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}
};
std::vector<uint> indices = {
0, 1, 2, 0, 2, 3, // Front
4, 5, 6, 4, 6, 7, // Back
8, 9, 10, 8, 10, 11, // Top
12, 13, 14, 12, 14, 15, // Bottom
16, 17, 18, 16, 18, 19, // Right
20, 21, 22, 20, 22, 23 // Left
};
mesh->set_vertices(vertices);
mesh->set_indices(indices);
mesh->set_material(material_id);
return mesh;
}
/// @brief Setup Cornell Box scene
void setup_cornell_box() {
g_scene = std::make_unique<Scene>();
// Create materials
// 0: White diffuse
auto white_material = std::make_shared<Material>();
white_material->set_albedo(Vec3(0.73f, 0.73f, 0.73f));
white_material->set_type(MaterialType::DIFFUSE);
uint white_id = g_scene->add_material(white_material);
// 1: Red diffuse (left wall)
auto red_material = std::make_shared<Material>();
red_material->set_albedo(Vec3(0.65f, 0.05f, 0.05f));
red_material->set_type(MaterialType::DIFFUSE);
uint red_id = g_scene->add_material(red_material);
// 2: Green diffuse (right wall)
auto green_material = std::make_shared<Material>();
green_material->set_albedo(Vec3(0.12f, 0.45f, 0.15f));
green_material->set_type(MaterialType::DIFFUSE);
uint green_id = g_scene->add_material(green_material);
// 3: Light emissive
auto light_material = std::make_shared<Material>();
light_material->set_albedo(Vec3(1.0f, 1.0f, 1.0f));
light_material->set_emission(Vec3(15.0f, 15.0f, 15.0f));
light_material->set_type(MaterialType::EMISSIVE);
uint light_id = g_scene->add_material(light_material);
// 4: Metal (for one box)
auto metal_material = std::make_shared<Material>();
metal_material->set_albedo(Vec3(0.95f, 0.93f, 0.88f));
metal_material->set_metallic(1.0f);
metal_material->set_roughness(0.1f);
metal_material->set_type(MaterialType::METAL);
uint metal_id = g_scene->add_material(metal_material);
// Create room (Cornell Box)
float room_size = 2.0f;
// Floor (white)
auto floor = create_quad(
Vec3(-room_size, -room_size, -room_size),
Vec3(room_size, -room_size, -room_size),
Vec3(room_size, -room_size, room_size),
Vec3(-room_size, -room_size, room_size),
Vec3(0.0f, 1.0f, 0.0f),
white_id
);
floor->upload_to_gpu();
g_scene->add_mesh(floor);
// Ceiling (white)
auto ceiling = create_quad(
Vec3(-room_size, room_size, room_size),
Vec3(room_size, room_size, room_size),
Vec3(room_size, room_size, -room_size),
Vec3(-room_size, room_size, -room_size),
Vec3(0.0f, -1.0f, 0.0f),
white_id
);
ceiling->upload_to_gpu();
g_scene->add_mesh(ceiling);
// Back wall (white)
auto back_wall = create_quad(
Vec3(-room_size, -room_size, -room_size),
Vec3(-room_size, room_size, -room_size),
Vec3(room_size, room_size, -room_size),
Vec3(room_size, -room_size, -room_size),
Vec3(0.0f, 0.0f, 1.0f),
white_id
);
back_wall->upload_to_gpu();
g_scene->add_mesh(back_wall);
// Left wall (red)
auto left_wall = create_quad(
Vec3(-room_size, -room_size, room_size),
Vec3(-room_size, room_size, room_size),
Vec3(-room_size, room_size, -room_size),
Vec3(-room_size, -room_size, -room_size),
Vec3(1.0f, 0.0f, 0.0f),
red_id
);
left_wall->upload_to_gpu();
g_scene->add_mesh(left_wall);
// Right wall (green)
auto right_wall = create_quad(
Vec3(room_size, -room_size, -room_size),
Vec3(room_size, room_size, -room_size),
Vec3(room_size, room_size, room_size),
Vec3(room_size, -room_size, room_size),
Vec3(-1.0f, 0.0f, 0.0f),
green_id
);
right_wall->upload_to_gpu();
g_scene->add_mesh(right_wall);
// Area light on ceiling
float light_size = 0.5f;
auto area_light = create_quad(
Vec3(-light_size, room_size - 0.01f, -light_size),
Vec3(light_size, room_size - 0.01f, -light_size),
Vec3(light_size, room_size - 0.01f, light_size),
Vec3(-light_size, room_size - 0.01f, light_size),
Vec3(0.0f, -1.0f, 0.0f),
light_id
);
area_light->upload_to_gpu();
g_scene->add_mesh(area_light);
// Tall box (white, left side)
auto tall_box = create_box(Vec3(-0.7f, -room_size, -0.7f), Vec3(-0.2f, 0.6f, -0.2f), white_id);
tall_box->upload_to_gpu();
g_scene->add_mesh(tall_box);
// Short box (metal, right side)
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), metal_id);
short_box->upload_to_gpu();
g_scene->add_mesh(short_box);
// Setup camera
auto camera = std::make_shared<Camera>();
camera->set_position(Vec3(0.0f, 0.0f, 4.5f));
camera->set_target(Vec3(0.0f, 0.0f, 0.0f));
camera->set_up(Vec3(0.0f, 1.0f, 0.0f));
camera->set_perspective(45.0f, static_cast<float>(WINDOW_WIDTH) / WINDOW_HEIGHT, 0.1f, 100.0f);
g_scene->set_camera(camera);
// Add point light
auto light = std::make_shared<Light>();
light->set_type(LightType::POINT);
light->set_position(Vec3(0.0f, 1.8f, 0.0f));
light->set_color(Vec3(1.0f, 1.0f, 1.0f));
light->set_intensity(10.0f);
light->set_range(10.0f);
g_scene->add_light(light);
Logger::info("Cornell Box scene created");
}
/// @brief Initialize GLFW and create window
bool init_window() {
// Set error callback before init
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
Logger::error("Failed to initialize GLFW");
return false;
}
Logger::info("GLFW initialized successfully");
// Request OpenGL 4.5 Core Profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Additional hints for better compatibility
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_SAMPLES, 0);
Logger::info("Creating window...");
g_window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Aurora - Cornell Box", nullptr, nullptr);
if (!g_window) {
Logger::error("Failed to create GLFW window");
Logger::error("Possible reasons:");
Logger::error(" 1. OpenGL 4.5 not supported by your GPU/driver");
Logger::error(" 2. No display server running (X11/Wayland)");
Logger::error(" 3. Insufficient GPU resources");
// Try to get more info
int major, minor, rev;
glfwGetVersion(&major, &minor, &rev);
Logger::info("GLFW version: " + std::to_string(major) + "." +
std::to_string(minor) + "." + std::to_string(rev));
glfwTerminate();
return false;
}
Logger::info("Window created successfully");
glfwMakeContextCurrent(g_window);
glfwSwapInterval(1); // Enable vsync
// Load OpenGL functions
Logger::info("Loading OpenGL functions...");
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
Logger::error("Failed to initialize GLAD");
return false;
}
// Print OpenGL info
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
const char* version = (const char*)glGetString(GL_VERSION);
const char* glsl_version = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
Logger::info("OpenGL Vendor: " + std::string(vendor ? vendor : "Unknown"));
Logger::info("OpenGL Renderer: " + std::string(renderer ? renderer : "Unknown"));
Logger::info("OpenGL Version: " + std::string(version ? version : "Unknown"));
Logger::info("GLSL Version: " + std::string(glsl_version ? glsl_version : "Unknown"));
// Check OpenGL version
GLint major_ver, minor_ver;
glGetIntegerv(GL_MAJOR_VERSION, &major_ver);
glGetIntegerv(GL_MINOR_VERSION, &minor_ver);
Logger::info("OpenGL Context: " + std::to_string(major_ver) + "." + std::to_string(minor_ver));
// if (major_ver < 4 || (major_ver == 4 && minor_ver < 5)) {
// Logger::error("OpenGL 4.5 or higher is required!");
// Logger::error("Your system supports: OpenGL " + std::to_string(major_ver) + "." + std::to_string(minor_ver));
// return false;
// }
// Check compute shader support
GLint max_compute_work_group_invocations;
glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &max_compute_work_group_invocations);
Logger::info("Max compute work group invocations: " + std::to_string(max_compute_work_group_invocations));
return true;
}
/// @brief Main render loop
void render_loop() {
Logger::info("Entering render loop...");
int frame_count = 0;
double last_time = glfwGetTime();
double fps_time = last_time;
while (!glfwWindowShouldClose(g_window)) {
// Render
RenderStats stats = g_renderer->render(*g_scene);
// Swap buffers
glfwSwapBuffers(g_window);
glfwPollEvents();
// Calculate FPS
frame_count++;
double current_time = glfwGetTime();
double delta = current_time - fps_time;
if (delta >= 1.0) {
double fps = frame_count / delta;
std::string title = "Aurora - Cornell Box | FPS: " + std::to_string((int)fps) +
" | Frame: " + std::to_string((int)stats.frame_time_ms_) + "ms";
glfwSetWindowTitle(g_window, title.c_str());
frame_count = 0;
fps_time = current_time;
}
// Print detailed stats every 60 frames
static int stat_frame_count = 0;
if (++stat_frame_count % 60 == 0) {
Logger::info("Frame time: " + std::to_string(stats.frame_time_ms_) + " ms (" +
std::to_string(1000.0f / stats.frame_time_ms_) + " FPS)");
Logger::info(" G-Buffer: " + std::to_string(stats.gbuffer_time_ms_) + " ms");
Logger::info(" Ray trace: " + std::to_string(stats.raytrace_time_ms_) + " ms");
Logger::info(" Triangles: " + std::to_string(stats.triangle_count_));
}
// ESC to exit
if (glfwGetKey(g_window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(g_window, true);
}
}
Logger::info("Exiting render loop");
}
/// @brief Cleanup
void cleanup() {
Logger::info("Cleaning up...");
if (g_renderer) {
g_renderer->shutdown();
g_renderer.reset();
}
g_scene.reset();
if (g_window) {
glfwDestroyWindow(g_window);
glfwTerminate();
}
Logger::info("Cleanup complete");
}
int main() {
// Initialize logger
Logger::initialize();
Logger::info("===========================================");
Logger::info("Aurora Rendering Engine - Cornell Box Demo");
Logger::info("===========================================");
// Check environment
const char* display = getenv("DISPLAY");
const char* wayland_display = getenv("WAYLAND_DISPLAY");
if (!display && !wayland_display) {
Logger::error("No display server detected!");
Logger::error("Make sure you're running in a graphical environment (X11 or Wayland)");
Logger::error("DISPLAY=" + std::string(display ? display : "not set"));
Logger::error("WAYLAND_DISPLAY=" + std::string(wayland_display ? wayland_display : "not set"));
return -1;
}
Logger::info("Display server: " + std::string(display ? display : wayland_display));
// Initialize window
if (!init_window()) {
cleanup();
Logger::error("Failed to initialize window");
Logger::shutdown();
return -1;
}
// Setup scene
Logger::info("Setting up Cornell Box scene...");
setup_cornell_box();
// Initialize renderer
Logger::info("Initializing renderer...");
RendererConfig config;
config.width_ = WINDOW_WIDTH;
config.height_ = WINDOW_HEIGHT;
config.samples_per_pixel_ = 1;
config.max_ray_depth_ = 4;
config.enable_accumulation_ = false;
config.enable_denoising_ = false;
g_renderer = std::make_unique<Renderer>(config);
if (!g_renderer->initialize()) {
Logger::error("Failed to initialize renderer");
cleanup();
Logger::shutdown();
return -1;
}
Logger::info("===========================================");
Logger::info("Renderer initialized successfully!");
Logger::info("Press ESC to exit");
Logger::info("===========================================");
// Main loop
render_loop();
// Cleanup
cleanup();
Logger::info("Cornell Box demo finished");
Logger::shutdown();
return 0;
}

32
include/basic/constants.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef ARE_INCLUDE_BASIC_CONSTANTS_H
#define ARE_INCLUDE_BASIC_CONSTANTS_H
namespace are {
/// @brief Maximum number of lights in scene
constexpr int MAX_LIGHTS = 16;
/// @brief Maximum ray tracing depth
constexpr int MAX_RAY_DEPTH = 8;
/// @brief Default samples per pixel for ray tracing
constexpr int DEFAULT_SPP = 1;
/// @brief G-Buffer attachment indices
constexpr int GBUFFER_POSITION = 0;
constexpr int GBUFFER_NORMAL = 1;
constexpr int GBUFFER_ALBEDO = 2;
constexpr int GBUFFER_COUNT = 3;
/// @brief Compute shader work group size
constexpr int COMPUTE_GROUP_SIZE_X = 16;
constexpr int COMPUTE_GROUP_SIZE_Y = 16;
/// @brief Mathematical constants
constexpr float PI = 3.14159265359f;
constexpr float INV_PI = 0.31830988618f;
constexpr float EPSILON = 1e-4f;
} // namespace are
#endif // ARE_INCLUDE_BASIC_CONSTANTS_H

59
include/basic/math.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef ARE_INCLUDE_BASIC_MATH_UTILS_H
#define ARE_INCLUDE_BASIC_MATH_UTILS_H
#include "types.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace are {
/// @brief Math utility functions wrapping GLM
class MathUtils {
public:
/// @brief Create perspective projection matrix
/// @param fov Field of view in radians
/// @param aspect Aspect ratio
/// @param near Near plane distance
/// @param far Far plane distance
/// @return Projection matrix
static Mat4 perspective(float fov, float aspect, float near, float far);
/// @brief Create look-at view matrix
/// @param eye Camera position
/// @param center Look-at target
/// @param up Up vector
/// @return View matrix
static Mat4 look_at(const Vec3& eye, const Vec3& center, const Vec3& up);
/// @brief Normalize a vector
/// @param v Input vector
/// @return Normalized vector
static Vec3 normalize(const Vec3& v);
/// @brief Calculate dot product
/// @param a First vector
/// @param b Second vector
/// @return Dot product
static float dot(const Vec3& a, const Vec3& b);
/// @brief Calculate cross product
/// @param a First vector
/// @param b Second vector
/// @return Cross product
static Vec3 cross(const Vec3& a, const Vec3& b);
/// @brief Reflect vector around normal
/// @param incident Incident vector
/// @param normal Surface normal
/// @return Reflected vector
static Vec3 reflect(const Vec3& incident, const Vec3& normal);
/// @brief Get pointer to matrix data (for OpenGL)
/// @param mat Input matrix
/// @return Pointer to matrix data
static const float* value_ptr(const Mat4& mat);
};
} // namespace are
#endif // ARE_INCLUDE_BASIC_MATH_UTILS_H

71
include/basic/types.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef ARE_INCLUDE_BASIC_TYPES_H
#define ARE_INCLUDE_BASIC_TYPES_H
#include <cstdint>
#include <memory>
#include <vector>
#include <string>
#include <glm/glm.hpp>
namespace are {
/// @brief Basic vector types using GLM
using Vec2 = glm::vec2;
using Vec3 = glm::vec3;
using Vec4 = glm::vec4;
/// @brief Basic matrix types using GLM
using Mat3 = glm::mat3;
using Mat4 = glm::mat4;
/// @brief Basic integer types
using uint = uint32_t;
using uchar = uint8_t;
/// @brief Handle types for GPU resources
using TextureHandle = uint;
using BufferHandle = uint;
using ShaderHandle = uint;
using FramebufferHandle = uint;
/// @brief Invalid handle constant
constexpr uint INVALID_HANDLE = 0;
/// @brief Vertex structure for mesh data
struct Vertex {
Vec3 position_;
Vec3 normal_;
Vec2 texcoord_;
Vec3 tangent_;
};
/// @brief Ray structure for ray tracing
struct Ray {
Vec3 origin_;
Vec3 direction_;
float t_min_;
float t_max_;
};
/// @brief Hit information for ray-surface intersection
struct HitInfo {
bool hit_;
float t_;
Vec3 position_;
Vec3 normal_;
Vec2 texcoord_;
uint material_id_;
};
/// @brief Rendering statistics
struct RenderStats {
float frame_time_ms_;
uint triangle_count_;
uint ray_count_;
float gbuffer_time_ms_;
float raytrace_time_ms_;
};
} // namespace are
#endif // ARE_INCLUDE_BASIC_TYPES_H

124
include/core/bvh.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef ARE_INCLUDE_CORE_BVH_H
#define ARE_INCLUDE_CORE_BVH_H
#include "basic/types.h"
#include "scene/mesh.h"
#include "resource/buffer.h"
#include <vector>
#include <memory>
namespace are {
/// @brief Axis-aligned bounding box
struct AABB {
Vec3 min_;
Vec3 max_;
/// @brief Construct AABB from min and max points
AABB(const Vec3& min = Vec3(0.0f), const Vec3& max = Vec3(0.0f))
: min_(min), max_(max) {}
/// @brief Expand AABB to include point
void expand(const Vec3& point);
/// @brief Expand AABB to include another AABB
void expand(const AABB& other);
/// @brief Get center of AABB
Vec3 center() const { return (min_ + max_) * 0.5f; }
/// @brief Get surface area of AABB
float surface_area() const;
/// @brief Check if AABB is valid
bool is_valid() const;
};
/// @brief Triangle primitive for BVH
struct Triangle {
Vec3 v0_, v1_, v2_;
Vec3 n0_, n1_, n2_;
Vec2 uv0_, uv1_, uv2_;
uint material_id_;
/// @brief Get bounding box of triangle
AABB get_bounds() const;
/// @brief Get centroid of triangle
Vec3 get_centroid() const;
};
/// @brief BVH node for GPU
struct BVHNode {
Vec3 aabb_min_;
uint left_first_; // Left child index or first primitive index
Vec3 aabb_max_;
uint count_; // 0 for interior node, >0 for leaf node
};
/// @brief Bounding Volume Hierarchy for ray tracing acceleration
class BVH {
public:
/// @brief Constructor
BVH();
/// @brief Destructor
~BVH();
/// @brief Build BVH from meshes
/// @param meshes Mesh list
/// @return True if build succeeded
bool build(const std::vector<std::shared_ptr<Mesh>>& meshes);
/// @brief Upload BVH to GPU
/// @param node_buffer Buffer for BVH nodes
/// @param triangle_buffer Buffer for triangles
/// @return True if upload succeeded
bool upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer);
/// @brief Get total node count
/// @return Node count
uint get_node_count() const { return static_cast<uint>(nodes_.size()); }
/// @brief Get total triangle count
/// @return Triangle count
uint get_triangle_count() const { return static_cast<uint>(triangles_.size()); }
/// @brief Clear BVH data
void clear();
private:
std::vector<BVHNode> nodes_;
std::vector<Triangle> triangles_;
std::vector<uint> triangle_indices_;
/// @brief Recursively build BVH
/// @param node_idx Current node index
/// @param first_prim First primitive index
/// @param prim_count Primitive count
void build_recursive_(uint node_idx, uint first_prim, uint prim_count);
/// @brief Find best split using SAH
/// @param first_prim First primitive index
/// @param prim_count Primitive count
/// @param axis Split axis (output)
/// @param split_pos Split position (output)
/// @return Split cost
float find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos);
/// @brief Calculate node bounds
/// @param first_prim First primitive index
/// @param prim_count Primitive count
/// @return Bounding box
AABB calculate_bounds_(uint first_prim, uint prim_count);
/// @brief Calculate centroid bounds
/// @param first_prim First primitive index
/// @param prim_count Primitive count
/// @return Centroid bounding box
AABB calculate_centroid_bounds_(uint first_prim, uint prim_count);
};
} // namespace are
#endif // ARE_INCLUDE_CORE_BVH_H

72
include/core/gbuffer.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef ARE_INCLUDE_CORE_GBUFFER_H
#define ARE_INCLUDE_CORE_GBUFFER_H
#include "basic/types.h"
#include "basic/constants.h"
#include "scene/scene.h"
#include "resource/shader.h"
namespace are {
/// @brief G-Buffer manager for deferred rendering
class GBuffer {
public:
/// @brief Constructor
/// @param width Buffer width
/// @param height Buffer height
GBuffer(uint width, uint height);
/// @brief Destructor
~GBuffer();
/// @brief Initialize G-Buffer (create framebuffer and textures)
/// @return True if initialization succeeded
bool initialize();
/// @brief Release G-Buffer resources
void release();
/// @brief Render scene to G-Buffer
/// @param scene Scene to render
/// @param shader Shader program for G-Buffer pass
void render(const Scene& scene, const Shader& shader);
/// @brief Resize G-Buffer
/// @param width New width
/// @param height New height
void resize(uint width, uint height);
/// @brief Get texture handle for specific buffer
/// @param index Buffer index (GBUFFER_POSITION, GBUFFER_NORMAL, etc.)
/// @return Texture handle
TextureHandle get_texture(int index) const;
/// @brief Get framebuffer handle
/// @return Framebuffer handle
FramebufferHandle get_framebuffer() const { return fbo_; }
/// @brief Get buffer dimensions
/// @param width Output width
/// @param height Output height
void get_dimensions(uint& width, uint& height) const;
private:
uint width_;
uint height_;
FramebufferHandle fbo_;
TextureHandle textures_[GBUFFER_COUNT];
TextureHandle depth_texture_;
bool initialized_;
/// @brief Create texture for G-Buffer attachment
/// @param internal_format OpenGL internal format
/// @param format OpenGL format
/// @param type OpenGL type
/// @return Texture handle
TextureHandle create_texture_(uint internal_format, uint format, uint type);
};
} // namespace are
#endif // ARE_INCLUDE_CORE_GBUFFER_H

106
include/core/raytracer.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef ARE_INCLUDE_CORE_RAYTRACER_H
#define ARE_INCLUDE_CORE_RAYTRACER_H
#include "basic/types.h"
#include "core/bvh.h" // 添加
#include "core/gbuffer.h"
#include "resource/buffer.h"
#include "resource/shader.h"
#include "scene/scene.h"
namespace are {
/// @brief Ray tracing configuration
struct RayTracerConfig {
uint samples_per_pixel_;
uint max_depth_;
bool enable_shadows_;
bool enable_reflections_;
bool enable_accumulation_;
bool use_bvh_; // 添加BVH开关
};
/// @brief Compute shader based ray tracer
class RayTracer {
public:
/// @brief Constructor
/// @param width Output width
/// @param height Output height
/// @param config Ray tracer configuration
RayTracer(uint width, uint height, const RayTracerConfig &config);
/// @brief Destructor
~RayTracer();
/// @brief Initialize ray tracer
/// @return True if initialization succeeded
bool initialize();
/// @brief Release resources
void release();
/// @brief Trace rays using G-Buffer as input
/// @param scene Scene data
/// @param gbuffer G-Buffer containing geometry information
/// @param output_texture Output texture for ray traced result
void trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle output_texture);
/// @brief Resize output
/// @param width New width
/// @param height New height
void resize(uint width, uint height);
/// @brief Reset accumulation buffer
void reset_accumulation();
/// @brief Get current configuration
/// @return Current configuration
const RayTracerConfig &get_config() const {
return config_;
}
/// @brief Update configuration
/// @param config New configuration
void set_config(const RayTracerConfig &config);
/// @brief Rebuild BVH from scene
/// @param scene Scene to build BVH from
/// @return True if build succeeded
bool rebuild_bvh(const Scene &scene);
/// @brief Set compute shader (called by renderer)
/// @param shader Compute shader
void set_compute_shader(const Shader &shader);
private:
uint width_;
uint height_;
RayTracerConfig config_;
Shader compute_shader_;
TextureHandle accumulation_texture_;
BufferHandle scene_buffer_;
BufferHandle material_buffer_;
BufferHandle light_buffer_;
// BVH related
std::unique_ptr<BVH> bvh_; // 添加
Buffer bvh_node_buffer_; // 添加
Buffer bvh_triangle_buffer_; // 添加
bool bvh_built_; // 添加
uint frame_count_;
bool initialized_;
/// @brief Upload scene data to GPU buffers
/// @param scene Scene to upload
void upload_scene_data_(const Scene &scene);
/// @brief Bind G-Buffer textures to compute shader
/// @param gbuffer G-Buffer to bind
void bind_gbuffer_(const GBuffer &gbuffer);
};
} // namespace are
#endif // ARE_INCLUDE_CORE_RAYTRACER_H

73
include/core/renderer.h Normal file
View File

@ -0,0 +1,73 @@
#ifndef ARE_INCLUDE_CORE_RENDERER_H
#define ARE_INCLUDE_CORE_RENDERER_H
#include "basic/types.h"
#include "scene/scene.h"
#include "core/gbuffer.h"
#include "core/raytracer.h"
#include "core/screen_blit.h"
#include "core/shader_manager.h"
#include <memory>
namespace are {
/// @brief Main renderer configuration
struct RendererConfig {
uint width_;
uint height_;
uint samples_per_pixel_;
uint max_ray_depth_;
bool enable_denoising_;
bool enable_accumulation_;
};
/// @brief Main rendering engine interface
class Renderer {
public:
/// @brief Constructor
/// @param config Renderer configuration
Renderer(const RendererConfig& config);
/// @brief Destructor
~Renderer();
/// @brief Initialize renderer (OpenGL context must be current)
/// @return True if initialization succeeded
bool initialize();
/// @brief Shutdown renderer and release resources
void shutdown();
/// @brief Render a frame
/// @param scene Scene to render
/// @param output_texture Output texture handle (0 for default framebuffer)
/// @return Rendering statistics
RenderStats render(const Scene& scene, TextureHandle output_texture = 0);
/// @brief Resize render targets
/// @param width New width
/// @param height New height
void resize(uint width, uint height);
/// @brief Get current configuration
/// @return Current configuration
const RendererConfig& get_config() const { return config_; }
/// @brief Update configuration
/// @param config New configuration
void set_config(const RendererConfig& config);
private:
RendererConfig config_;
std::unique_ptr<GBuffer> gbuffer_;
std::unique_ptr<RayTracer> raytracer_;
std::unique_ptr<ShaderManager> shader_manager_;
std::unique_ptr<ScreenBlit> screen_blit_;
bool initialized_;
uint frame_count_;
};
} // namespace are
#endif // ARE_INCLUDE_CORE_RENDERER_H

View File

@ -0,0 +1,49 @@
#ifndef ARE_INCLUDE_CORE_SCREEN_BLIT_H
#define ARE_INCLUDE_CORE_SCREEN_BLIT_H
#include "basic/types.h"
#include "resource/shader.h"
namespace are {
/// @brief Screen blit utility for rendering texture to screen
class ScreenBlit {
public:
/// @brief Constructor
ScreenBlit();
/// @brief Destructor
~ScreenBlit();
/// @brief Initialize screen blit
/// @return True if initialization succeeded
bool initialize();
/// @brief Release resources
void release();
/// @brief Blit texture to screen
/// @param texture Texture to blit
/// @param x Screen X position
/// @param y Screen Y position
/// @param width Blit width
/// @param height Blit height
void blit(TextureHandle texture, int x, int y, uint width, uint height);
/// @brief Blit texture to full screen
/// @param texture Texture to blit
void blit_fullscreen(TextureHandle texture);
private:
Shader shader_;
uint vao_;
uint vbo_;
bool initialized_;
/// @brief Create fullscreen quad
void create_quad_();
};
} // namespace are
#endif // ARE_INCLUDE_CORE_SCREEN_BLIT_H

View File

@ -0,0 +1,70 @@
#ifndef ARE_INCLUDE_CORE_SHADER_MANAGER_H
#define ARE_INCLUDE_CORE_SHADER_MANAGER_H
#include "basic/types.h"
#include "resource/shader.h"
#include <unordered_map>
#include <string>
namespace are {
/// @brief Shader manager for loading and caching shaders
class ShaderManager {
public:
/// @brief Constructor
ShaderManager();
/// @brief Destructor
~ShaderManager();
/// @brief Initialize shader manager and load built-in shaders
/// @return True if initialization succeeded
bool initialize();
/// @brief Release all shaders
void release();
/// @brief Load shader from files
/// @param name Shader name for caching
/// @param vertex_path Vertex shader file path
/// @param fragment_path Fragment shader file path
/// @return Shader object
Shader load_shader(const std::string& name,
const std::string& vertex_path,
const std::string& fragment_path);
/// @brief Load compute shader from file
/// @param name Shader name for caching
/// @param compute_path Compute shader file path
/// @return Shader object
Shader load_compute_shader(const std::string& name,
const std::string& compute_path);
/// @brief Get cached shader by name
/// @param name Shader name
/// @return Shader object (invalid if not found)
Shader get_shader(const std::string& name) const;
/// @brief Get G-Buffer shader
/// @return G-Buffer shader
const Shader& get_gbuffer_shader() const { return gbuffer_shader_; }
/// @brief Get ray tracing compute shader
/// @return Ray tracing shader
const Shader& get_raytracing_shader() const { return raytracing_shader_; }
private:
std::unordered_map<std::string, Shader> shader_cache_;
Shader gbuffer_shader_;
Shader raytracing_shader_;
bool initialized_;
/// @brief Load built-in shaders
/// @return True if loading succeeded
bool load_builtin_shaders_();
};
} // namespace are
#endif // ARE_INCLUDE_CORE_SHADER_MANAGER_H

View File

@ -0,0 +1,4 @@
core
scene
resource
utils

84
include/resource/buffer.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef ARE_INCLUDE_RESOURCE_BUFFER_H
#define ARE_INCLUDE_RESOURCE_BUFFER_H
#include "basic/types.h"
namespace are {
/// @brief Buffer usage hint
enum class BufferUsage {
STATIC_DRAW,
DYNAMIC_DRAW,
STREAM_DRAW
};
/// @brief Buffer type
enum class BufferType {
VERTEX_BUFFER,
INDEX_BUFFER,
UNIFORM_BUFFER,
SHADER_STORAGE_BUFFER
};
/// @brief GPU buffer resource
class Buffer {
public:
/// @brief Constructor
Buffer();
/// @brief Destructor
~Buffer();
/// @brief Create buffer
/// @param type Buffer type
/// @param size Buffer size in bytes
/// @param data Initial data (nullptr for empty buffer)
/// @param usage Usage hint
/// @return True if creation succeeded
bool create(BufferType type, size_t size, const void* data, BufferUsage usage);
/// @brief Update buffer data
/// @param offset Offset in bytes
/// @param size Size in bytes
/// @param data Data to upload
void update(size_t offset, size_t size, const void* data);
/// @brief Bind buffer
void bind() const;
/// @brief Bind buffer to binding point (for UBO/SSBO)
/// @param binding_point Binding point index
void bind_base(uint binding_point) const;
/// @brief Unbind buffer
void unbind() const;
/// @brief Release buffer resources
void release();
/// @brief Get buffer handle
/// @return Buffer handle
BufferHandle get_handle() const { return handle_; }
/// @brief Get buffer size
/// @return Size in bytes
size_t get_size() const { return size_; }
/// @brief Get buffer type
/// @return Buffer type
BufferType get_type() const { return type_; }
/// @brief Check if buffer is valid
/// @return True if valid
bool is_valid() const { return handle_ != INVALID_HANDLE; }
private:
BufferHandle handle_;
BufferType type_;
size_t size_;
BufferUsage usage_;
};
} // namespace are
#endif // ARE_INCLUDE_RESOURCE_BUFFER_H

View File

@ -0,0 +1,58 @@
#ifndef ARE_INCLUDE_RESOURCE_MODEL_LOADER_H
#define ARE_INCLUDE_RESOURCE_MODEL_LOADER_H
#include "basic/types.h"
#include "scene/mesh.h"
#include "scene/material.h"
#include <string>
#include <vector>
#include <memory>
namespace are {
/// @brief Model loader using Assimp
class ModelLoader {
public:
/// @brief Load model from file
/// @param path Model file path
/// @param meshes Output mesh list
/// @param materials Output material list
/// @param flip_uvs Flip UV coordinates vertically
/// @return True if loading succeeded
static bool load(const std::string& path,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
bool flip_uvs = true);
/// @brief Load model and automatically upload to GPU
/// @param path Model file path
/// @param meshes Output mesh list
/// @param materials Output material list
/// @param flip_uvs Flip UV coordinates vertically
/// @return True if loading succeeded
static bool load_and_upload(const std::string& path,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
bool flip_uvs = true);
private:
/// @brief Process Assimp node recursively
static void process_node_(void* node, void* scene,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
const std::string& directory);
/// @brief Process Assimp mesh
static std::shared_ptr<Mesh> process_mesh_(void* mesh, void* scene,
std::vector<std::shared_ptr<Material>>& materials,
const std::string& directory);
/// @brief Load material textures
static void load_material_textures_(void* material, int type,
std::shared_ptr<Material>& mat,
const std::string& directory);
};
} // namespace are
#endif // ARE_INCLUDE_RESOURCE_MODEL_LOADER_H

129
include/resource/shader.h Normal file
View File

@ -0,0 +1,129 @@
#ifndef ARE_INCLUDE_RESOURCE_SHADER_H
#define ARE_INCLUDE_RESOURCE_SHADER_H
#include "basic/types.h"
#include <string>
#include <unordered_map>
namespace are {
/// @brief Shader program resource
class Shader {
public:
/// @brief Constructor
Shader();
/// @brief Destructor
~Shader();
/// @brief Load and compile shader from files
/// @param vertex_path Vertex shader path
/// @param fragment_path Fragment shader path
/// @return True if compilation succeeded
bool load(const std::string& vertex_path, const std::string& fragment_path);
/// @brief Load and compile compute shader
/// @param compute_path Compute shader path
/// @return True if compilation succeeded
bool load_compute(const std::string& compute_path);
/// @brief Compile shader from source strings
/// @param vertex_source Vertex shader source
/// @param fragment_source Fragment shader source
/// @return True if compilation succeeded
bool compile(const std::string& vertex_source, const std::string& fragment_source);
/// @brief Compile compute shader from source
/// @param compute_source Compute shader source
/// @return True if compilation succeeded
bool compile_compute(const std::string& compute_source);
/// @brief Use/activate shader program
void use() const; // 改为const
/// @brief Release shader resources
void release();
/// @brief Set uniform boolean
/// @param name Uniform name
/// @param value Value
void set_bool(const std::string& name, bool value) const; // 新增const
/// @brief Set uniform integer
/// @param name Uniform name
/// @param value Value
void set_int(const std::string& name, int value) const; // 改为const
/// @brief Set uniform unsigned integer
/// @param name Uniform name
/// @param value Value
void set_uint(const std::string& name, uint value) const; // 改为const
/// @brief Set uniform float
/// @param name Uniform name
/// @param value Value
void set_float(const std::string& name, float value) const; // 改为const
/// @brief Set uniform vec2
/// @param name Uniform name
/// @param value Value
void set_vec2(const std::string& name, const Vec2& value) const; // 改为const
/// @brief Set uniform vec3
/// @param name Uniform name
/// @param value Value
void set_vec3(const std::string& name, const Vec3& value) const; // 改为const
/// @brief Set uniform vec4
/// @param name Uniform name
/// @param value Value
void set_vec4(const std::string& name, const Vec4& value) const; // 改为const
/// @brief Set uniform mat3
/// @param name Uniform name
/// @param value Value
void set_mat3(const std::string& name, const Mat3& value) const; // 改为const
/// @brief Set uniform mat4
/// @param name Uniform name
/// @param value Value
void set_mat4(const std::string& name, const Mat4& value) const; // 改为const
/// @brief Get shader program handle
/// @return Shader handle
ShaderHandle get_handle() const { return handle_; }
/// @brief Check if shader is valid
/// @return True if valid
bool is_valid() const { return handle_ != INVALID_HANDLE; }
private:
ShaderHandle handle_;
mutable std::unordered_map<std::string, int> uniform_cache_; // 改为mutable
/// @brief Get uniform location (with caching)
/// @param name Uniform name
/// @return Uniform location
int get_uniform_location_(const std::string& name) const; // 改为const
/// @brief Compile shader stage
/// @param source Shader source code
/// @param type Shader type (GL_VERTEX_SHADER, etc.)
/// @return Shader object handle (0 on failure)
uint compile_shader_(const std::string& source, uint type);
/// @brief Link shader program
/// @param shaders Array of shader object handles
/// @param count Number of shaders
/// @return True if linking succeeded
bool link_program_(const uint* shaders, uint count);
/// @brief Read file content
/// @param path File path
/// @return File content
std::string read_file_(const std::string& path);
};
} // namespace are
#endif // ARE_INCLUDE_RESOURCE_SHADER_H

127
include/resource/texture.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef ARE_INCLUDE_RESOURCE_TEXTURE_H
#define ARE_INCLUDE_RESOURCE_TEXTURE_H
#include "basic/types.h"
#include <string>
namespace are {
/// @brief Texture format enumeration
enum class TextureFormat {
R8,
RG8,
RGB8,
RGBA8,
R16F,
RG16F,
RGB16F,
RGBA16F,
R32F,
RG32F,
RGB32F,
RGBA32F,
DEPTH24_STENCIL8
};
/// @brief Texture filter mode
enum class TextureFilter {
NEAREST,
LINEAR,
NEAREST_MIPMAP_NEAREST,
LINEAR_MIPMAP_NEAREST,
NEAREST_MIPMAP_LINEAR,
LINEAR_MIPMAP_LINEAR
};
/// @brief Texture wrap mode
enum class TextureWrap {
REPEAT,
MIRRORED_REPEAT,
CLAMP_TO_EDGE,
CLAMP_TO_BORDER
};
/// @brief Texture resource
class Texture {
public:
/// @brief Constructor
Texture();
/// @brief Destructor
~Texture();
/// @brief Load texture from file
/// @param path File path
/// @param generate_mipmaps Generate mipmaps
/// @return True if loading succeeded
bool load_from_file(const std::string& path, bool generate_mipmaps = true);
/// @brief Create empty texture
/// @param width Texture width
/// @param height Texture height
/// @param format Texture format
/// @return True if creation succeeded
bool create(uint width, uint height, TextureFormat format);
/// @brief Upload data to texture
/// @param data Pixel data
/// @param width Data width
/// @param height Data height
/// @param format Data format
/// @return True if upload succeeded
bool upload(const void* data, uint width, uint height, TextureFormat format);
/// @brief Set texture filter mode
/// @param min_filter Minification filter
/// @param mag_filter Magnification filter
void set_filter(TextureFilter min_filter, TextureFilter mag_filter);
/// @brief Set texture wrap mode
/// @param wrap_s Wrap mode for S coordinate
/// @param wrap_t Wrap mode for T coordinate
void set_wrap(TextureWrap wrap_s, TextureWrap wrap_t);
/// @brief Generate mipmaps
void generate_mipmaps();
/// @brief Bind texture to texture unit
/// @param unit Texture unit
void bind(uint unit) const;
/// @brief Unbind texture
void unbind() const;
/// @brief Release texture resources
void release();
/// @brief Get texture handle
/// @return Texture handle
TextureHandle get_handle() const { return handle_; }
/// @brief Get texture width
/// @return Width
uint get_width() const { return width_; }
/// @brief Get texture height
/// @return Height
uint get_height() const { return height_; }
/// @brief Get texture format
/// @return Format
TextureFormat get_format() const { return format_; }
/// @brief Check if texture is valid
/// @return True if valid
bool is_valid() const { return handle_ != INVALID_HANDLE; }
private:
TextureHandle handle_;
uint width_;
uint height_;
TextureFormat format_;
bool has_mipmaps_;
};
} // namespace are
#endif // ARE_INCLUDE_RESOURCE_TEXTURE_H

105
include/scene/camera.h Normal file
View File

@ -0,0 +1,105 @@
#ifndef ARE_INCLUDE_SCENE_CAMERA_H
#define ARE_INCLUDE_SCENE_CAMERA_H
#include "basic/types.h"
namespace are {
/// @brief Camera projection type
enum class ProjectionType {
PERSPECTIVE,
ORTHOGRAPHIC
};
/// @brief Camera for rendering
class Camera {
public:
/// @brief Constructor
Camera();
/// @brief Destructor
~Camera();
/// @brief Set perspective projection
/// @param fov Field of view in degrees
/// @param aspect Aspect ratio
/// @param near Near plane
/// @param far Far plane
void set_perspective(float fov, float aspect, float near, float far);
/// @brief Set orthographic projection
/// @param left Left plane
/// @param right Right plane
/// @param bottom Bottom plane
/// @param top Top plane
/// @param near Near plane
/// @param far Far plane
void set_orthographic(float left, float right, float bottom, float top, float near, float far);
/// @brief Set camera position
/// @param position Position
void set_position(const Vec3& position);
/// @brief Set camera target
/// @param target Target position
void set_target(const Vec3& target);
/// @brief Set camera up vector
/// @param up Up vector
void set_up(const Vec3& up);
/// @brief Get view matrix
/// @return View matrix
Mat4 get_view_matrix() const;
/// @brief Get projection matrix
/// @return Projection matrix
Mat4 get_projection_matrix() const;
/// @brief Get view-projection matrix
/// @return View-projection matrix
Mat4 get_view_projection_matrix() const;
/// @brief Get camera position
/// @return Position
const Vec3& get_position() const { return position_; }
/// @brief Get camera forward direction
/// @return Forward direction
Vec3 get_forward() const;
/// @brief Get camera right direction
/// @return Right direction
Vec3 get_right() const;
/// @brief Get camera up direction
/// @return Up direction
Vec3 get_up() const;
private:
Vec3 position_;
Vec3 target_;
Vec3 up_;
ProjectionType projection_type_;
// Perspective parameters
float fov_;
float aspect_;
// Orthographic parameters
float left_, right_, bottom_, top_;
// Common parameters
float near_;
float far_;
mutable Mat4 view_matrix_;
mutable Mat4 projection_matrix_;
mutable bool view_dirty_;
mutable bool projection_dirty_;
};
} // namespace are
#endif // ARE_INCLUDE_SCENE_CAMERA_H

114
include/scene/light.h Normal file
View File

@ -0,0 +1,114 @@
#ifndef ARE_INCLUDE_SCENE_LIGHT_H
#define ARE_INCLUDE_SCENE_LIGHT_H
#include "basic/types.h"
namespace are {
/// @brief Light type enumeration
enum class LightType {
DIRECTIONAL = 0,
POINT = 1,
SPOT = 2
};
/// @brief Light source
class Light {
public:
/// @brief Constructor
Light();
/// @brief Destructor
~Light();
/// @brief Set light type
/// @param type Light type
void set_type(LightType type);
/// @brief Set light position (for point and spot lights)
/// @param position Light position
void set_position(const Vec3 &position);
/// @brief Set light direction (for directional and spot lights)
/// @param direction Light direction
void set_direction(const Vec3 &direction);
/// @brief Set light color
/// @param color Light color
void set_color(const Vec3 &color);
/// @brief Set light intensity
/// @param intensity Light intensity
void set_intensity(float intensity);
/// @brief Set light range (for point and spot lights)
/// @param range Light range
void set_range(float range);
/// @brief Set spot light angles
/// @param inner_angle Inner cone angle in degrees
/// @param outer_angle Outer cone angle in degrees
void set_spot_angles(float inner_angle, float outer_angle);
/// @brief Get light type
/// @return Light type
LightType get_type() const {
return type_;
}
/// @brief Get light position
/// @return Light position
const Vec3 &get_position() const {
return position_;
}
/// @brief Get light direction
/// @return Light direction
const Vec3 &get_direction() const {
return direction_;
}
/// @brief Get light color
/// @return Light color
const Vec3 &get_color() const {
return color_;
}
/// @brief Get light intensity
/// @return Light intensity
float get_intensity() const {
return intensity_;
}
/// @brief Get light range
/// @return Light range
float get_range() const {
return range_;
}
/// @brief Get spot light inner angle
/// @return Inner angle in radians
float get_inner_angle() const {
return inner_angle_;
}
/// @brief Get spot light outer angle
/// @return Outer angle in radians
float get_outer_angle() const {
return outer_angle_;
}
private:
LightType type_;
Vec3 position_;
Vec3 direction_;
Vec3 color_;
float intensity_;
float range_;
float inner_angle_;
float outer_angle_;
};
} // namespace are
#endif // ARE_INCLUDE_SCENE_LIGHT_H

105
include/scene/material.h Normal file
View File

@ -0,0 +1,105 @@
#ifndef ARE_INCLUDE_SCENE_MATERIAL_H
#define ARE_INCLUDE_SCENE_MATERIAL_H
#include "basic/types.h"
#include "resource/texture.h"
#include <memory>
namespace are {
/// @brief Material type enumeration
enum class MaterialType {
DIFFUSE = 0,
METAL = 1,
DIELECTRIC = 2,
EMISSIVE = 3
};
/// @brief Material properties
class Material {
public:
/// @brief Constructor
Material();
/// @brief Destructor
~Material();
/// @brief Set albedo color
/// @param albedo Albedo color
void set_albedo(const Vec3& albedo);
/// @brief Set emission color
/// @param emission Emission color
void set_emission(const Vec3& emission);
/// @brief Set metallic value
/// @param metallic Metallic (0-1)
void set_metallic(float metallic);
/// @brief Set roughness value
/// @param roughness Roughness (0-1)
void set_roughness(float roughness);
/// @brief Set index of refraction
/// @param ior Index of refraction
void set_ior(float ior);
/// @brief Set material type
/// @param type Material type
void set_type(MaterialType type);
/// @brief Set albedo texture
/// @param texture Albedo texture
void set_albedo_texture(std::shared_ptr<Texture> texture);
/// @brief Set normal map
/// @param texture Normal map texture
void set_normal_texture(std::shared_ptr<Texture> texture);
/// @brief Get albedo color
/// @return Albedo color
const Vec3& get_albedo() const { return albedo_; }
/// @brief Get emission color
/// @return Emission color
const Vec3& get_emission() const { return emission_; }
/// @brief Get metallic value
/// @return Metallic
float get_metallic() const { return metallic_; }
/// @brief Get roughness value
/// @return Roughness
float get_roughness() const { return roughness_; }
/// @brief Get index of refraction
/// @return IOR
float get_ior() const { return ior_; }
/// @brief Get material type
/// @return Material type
MaterialType get_type() const { return type_; }
/// @brief Get albedo texture
/// @return Albedo texture (nullptr if none)
std::shared_ptr<Texture> get_albedo_texture() const { return albedo_texture_; }
/// @brief Get normal texture
/// @return Normal texture (nullptr if none)
std::shared_ptr<Texture> get_normal_texture() const { return normal_texture_; }
private:
Vec3 albedo_;
Vec3 emission_;
float metallic_;
float roughness_;
float ior_;
MaterialType type_;
std::shared_ptr<Texture> albedo_texture_;
std::shared_ptr<Texture> normal_texture_;
};
} // namespace are
#endif // ARE_INCLUDE_SCENE_MATERIAL_H

79
include/scene/mesh.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef ARE_INCLUDE_SCENE_MESH_H
#define ARE_INCLUDE_SCENE_MESH_H
#include "basic/types.h"
#include <vector>
namespace are {
/// @brief Mesh data container
class Mesh {
public:
/// @brief Constructor
Mesh();
/// @brief Destructor
~Mesh();
/// @brief Set vertex data
/// @param vertices Vertex array
void set_vertices(const std::vector<Vertex>& vertices);
/// @brief Set index data
/// @param indices Index array
void set_indices(const std::vector<uint>& indices);
/// @brief Set material index
/// @param material_id Material index
void set_material(uint material_id);
/// @brief Set transform matrix
/// @param transform Transform matrix
void set_transform(const Mat4& transform);
/// @brief Get vertices
/// @return Vertex array
const std::vector<Vertex>& get_vertices() const { return vertices_; }
/// @brief Get indices
/// @return Index array
const std::vector<uint>& get_indices() const { return indices_; }
/// @brief Get material index
/// @return Material index
uint get_material() const { return material_id_; }
/// @brief Get transform matrix
/// @return Transform matrix
const Mat4& get_transform() const { return transform_; }
/// @brief Upload mesh data to GPU
/// @return True if upload succeeded
bool upload_to_gpu();
/// @brief Release GPU resources
void release_gpu_resources();
/// @brief Get VAO handle
/// @return VAO handle
uint get_vao() const { return vao_; }
/// @brief Check if mesh is uploaded to GPU
/// @return True if uploaded
bool is_uploaded() const { return uploaded_; }
private:
std::vector<Vertex> vertices_;
std::vector<uint> indices_;
uint material_id_;
Mat4 transform_;
uint vao_;
uint vbo_;
uint ebo_;
bool uploaded_;
};
} // namespace are
#endif // ARE_INCLUDE_SCENE_MESH_H

74
include/scene/scene.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef ARE_INCLUDE_SCENE_SCENE_H
#define ARE_INCLUDE_SCENE_SCENE_H
#include "basic/types.h"
#include "scene/camera.h"
#include "scene/mesh.h"
#include "scene/material.h"
#include "scene/light.h"
#include <vector>
#include <memory>
namespace are {
/// @brief Scene container holding all scene objects
class Scene {
public:
/// @brief Constructor
Scene();
/// @brief Destructor
~Scene();
/// @brief Add mesh to scene
/// @param mesh Mesh to add
/// @return Mesh index
uint add_mesh(std::shared_ptr<Mesh> mesh);
/// @brief Add material to scene
/// @param material Material to add
/// @return Material index
uint add_material(std::shared_ptr<Material> material);
/// @brief Add light to scene
/// @param light Light to add
/// @return Light index
uint add_light(std::shared_ptr<Light> light);
/// @brief Set active camera
/// @param camera Camera to set
void set_camera(std::shared_ptr<Camera> camera);
/// @brief Get active camera
/// @return Active camera
const Camera& get_camera() const { return *camera_; }
/// @brief Get all meshes
/// @return Mesh list
const std::vector<std::shared_ptr<Mesh>>& get_meshes() const { return meshes_; }
/// @brief Get all materials
/// @return Material list
const std::vector<std::shared_ptr<Material>>& get_materials() const { return materials_; }
/// @brief Get all lights
/// @return Light list
const std::vector<std::shared_ptr<Light>>& get_lights() const { return lights_; }
/// @brief Clear all scene objects
void clear();
/// @brief Update scene (animations, transforms, etc.)
/// @param delta_time Time since last update
void update(float delta_time);
private:
std::shared_ptr<Camera> camera_;
std::vector<std::shared_ptr<Mesh>> meshes_;
std::vector<std::shared_ptr<Material>> materials_;
std::vector<std::shared_ptr<Light>> lights_;
};
} // namespace are
#endif // ARE_INCLUDE_SCENE_SCENE_H

70
include/utils/config.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef ARE_INCLUDE_UTILS_CONFIG_H
#define ARE_INCLUDE_UTILS_CONFIG_H
#include <string>
#include <unordered_map>
namespace are {
/// @brief Configuration manager for loading engine settings
/// @note This module should be implemented by the user
class Config {
public:
/// @brief Load configuration from file
/// @param path Configuration file path
/// @return True if loading succeeded
static bool load(const std::string& path);
/// @brief Save configuration to file
/// @param path Configuration file path
/// @return True if saving succeeded
static bool save(const std::string& path);
/// @brief Get string value
/// @param key Configuration key
/// @param default_value Default value if key not found
/// @return Configuration value
static std::string get_string(const std::string& key, const std::string& default_value = "");
/// @brief Get integer value
/// @param key Configuration key
/// @param default_value Default value if key not found
/// @return Configuration value
static int get_int(const std::string& key, int default_value = 0);
/// @brief Get float value
/// @param key Configuration key
/// @param default_value Default value if key not found
/// @return Configuration value
static float get_float(const std::string& key, float default_value = 0.0f);
/// @brief Get boolean value
/// @param key Configuration key
/// @param default_value Default value if key not found
/// @return Configuration value
static bool get_bool(const std::string& key, bool default_value = false);
/// @brief Set string value
/// @param key Configuration key
/// @param value Configuration value
static void set_string(const std::string& key, const std::string& value);
/// @brief Set integer value
/// @param key Configuration key
/// @param value Configuration value
static void set_int(const std::string& key, int value);
/// @brief Set float value
/// @param key Configuration key
/// @param value Configuration value
static void set_float(const std::string& key, float value);
/// @brief Set boolean value
/// @param key Configuration key
/// @param value Configuration value
static void set_bool(const std::string& key, bool value);
};
} // namespace are
#endif // ARE_INCLUDE_UTILS_CONFIG_H

61
include/utils/logger.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef ARE_INCLUDE_UTILS_LOGGER_H
#define ARE_INCLUDE_UTILS_LOGGER_H
#include <string>
namespace are {
/// @brief Log level enumeration
enum class LogLevel {
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
/// @brief Logger interface for engine logging
/// @note This module should be implemented by the user
class Logger {
public:
/// @brief Initialize logger
/// @param log_file Log file path (empty for console only)
/// @return True if initialization succeeded
static bool initialize(const std::string& log_file = "");
/// @brief Shutdown logger
static void shutdown();
/// @brief Log message
/// @param level Log level
/// @param message Message content
static void log(LogLevel level, const std::string& message);
/// @brief Log debug message
/// @param message Message content
static void debug(const std::string& message);
/// @brief Log info message
/// @param message Message content
static void info(const std::string& message);
/// @brief Log warning message
/// @param message Message content
static void warning(const std::string& message);
/// @brief Log error message
/// @param message Message content
static void error(const std::string& message);
/// @brief Log fatal message
/// @param message Message content
static void fatal(const std::string& message);
/// @brief Set minimum log level
/// @param level Minimum level to log
static void set_level(LogLevel level);
};
} // namespace are
#endif // ARE_INCLUDE_UTILS_LOGGER_H

95
scripts/check_env.sh Normal file
View File

@ -0,0 +1,95 @@
#!/bin/bash
echo "=========================================="
echo "Aurora Rendering Engine - System Check"
echo "=========================================="
echo ""
# Check display server
echo "1. Display Server:"
if [ -n "$DISPLAY" ]; then
echo " ✓ X11 detected: $DISPLAY"
elif [ -n "$WAYLAND_DISPLAY" ]; then
echo " ✓ Wayland detected: $WAYLAND_DISPLAY"
else
echo " ✗ No display server detected!"
echo " Please run this in a graphical environment"
fi
echo ""
# Check OpenGL
echo "2. OpenGL Information:"
if command -v glxinfo &> /dev/null; then
GL_VERSION=$(glxinfo | grep "OpenGL version" | cut -d: -f2)
GL_RENDERER=$(glxinfo | grep "OpenGL renderer" | cut -d: -f2)
GL_VENDOR=$(glxinfo | grep "OpenGL vendor" | cut -d: -f2)
echo " Vendor: $GL_VENDOR"
echo " Renderer:$GL_RENDERER"
echo " Version: $GL_VERSION"
# Check version
MAJOR=$(echo $GL_VERSION | grep -oP '\d+' | head -1)
MINOR=$(echo $GL_VERSION | grep -oP '\d+' | head -2 | tail -1)
if [ "$MAJOR" -ge 4 ] && [ "$MINOR" -ge 5 ]; then
echo " ✓ OpenGL 4.5+ supported"
else
echo " ✗ OpenGL 4.5+ NOT supported (found $MAJOR.$MINOR)"
echo " This engine requires OpenGL 4.5 or higher"
fi
else
echo " ✗ glxinfo not found (install mesa-utils)"
fi
echo ""
# Check GLFW
echo "3. GLFW Library:"
if ldconfig -p | grep -q libglfw; then
echo " ✓ GLFW library found"
else
echo " ✗ GLFW library not found"
echo " Install: sudo apt-get install libglfw3-dev"
fi
echo ""
# Check GLM
echo "4. GLM Library:"
if [ -d "/usr/include/glm" ] || [ -d "/usr/local/include/glm" ]; then
echo " ✓ GLM headers found"
else
echo " ✗ GLM headers not found"
echo " Install: sudo apt-get install libglm-dev"
fi
echo ""
# Check GPU
echo "5. GPU Information:"
if command -v lspci &> /dev/null; then
GPU=$(lspci | grep -i vga | cut -d: -f3)
echo " GPU:$GPU"
else
echo " ✗ lspci not found"
fi
echo ""
# Check drivers
echo "6. Graphics Drivers:"
if lsmod | grep -q nvidia; then
echo " ✓ NVIDIA driver loaded"
if command -v nvidia-smi &> /dev/null; then
DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader)
echo " Driver version: $DRIVER_VERSION"
fi
elif lsmod | grep -q amdgpu; then
echo " ✓ AMD driver (amdgpu) loaded"
elif lsmod | grep -q i915; then
echo " ✓ Intel driver (i915) loaded"
else
echo " ? Unknown driver"
fi
echo ""
echo "=========================================="
echo "System check complete"
echo "=========================================="

49
shaders/gbuffer.frag Normal file
View File

@ -0,0 +1,49 @@
#version 430 core
// Material types
const uint MATERIAL_DIFFUSE = 0u;
const uint MATERIAL_METAL = 1u;
const uint MATERIAL_DIELECTRIC = 2u;
const uint MATERIAL_EMISSIVE = 3u;
in VS_OUT {
vec3 frag_pos;
vec3 normal;
vec2 texcoord;
vec3 tangent;
} fs_in;
layout(location = 0) out vec4 g_position;
layout(location = 1) out vec4 g_normal;
layout(location = 2) out vec4 g_albedo;
layout(location = 3) out vec4 g_material;
// Material uniforms
uniform vec3 u_albedo;
uniform float u_metallic;
uniform float u_roughness;
uniform float u_ior;
uniform vec3 u_emission;
uniform uint u_material_type;
uniform bool u_has_albedo_map;
uniform sampler2D u_albedo_map;
void main() {
// Position
g_position = vec4(fs_in.frag_pos, 1.0);
// Normal
vec3 normal = normalize(fs_in.normal);
g_normal = vec4(normal, 0.0);
// Albedo
vec3 albedo = u_albedo;
if (u_has_albedo_map) {
albedo *= texture(u_albedo_map, fs_in.texcoord).rgb;
}
g_albedo = vec4(albedo, 1.0);
// Material properties
g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type));
}

27
shaders/gbuffer.vert Normal file
View File

@ -0,0 +1,27 @@
#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;
out VS_OUT {
vec3 frag_pos;
vec3 normal;
vec2 texcoord;
vec3 tangent;
} vs_out;
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
void main() {
vec4 world_pos = u_model * vec4(a_position, 1.0);
vs_out.frag_pos = world_pos.xyz;
vs_out.normal = mat3(transpose(inverse(u_model))) * a_normal;
vs_out.texcoord = a_texcoord;
vs_out.tangent = mat3(u_model) * a_tangent;
gl_Position = u_projection * u_view * world_pos;
}

503
shaders/raytracing.comp Normal file
View File

@ -0,0 +1,503 @@
#version 430 core
// Constants
#define PI 3.14159265359
#define INV_PI 0.31830988618
#define EPSILON 1e-4
#define MAX_FLOAT 3.402823466e38
#define MAX_RAY_DEPTH 8
#define MAX_LIGHTS 16
// Material types
#define MATERIAL_DIFFUSE 0
#define MATERIAL_METAL 1
#define MATERIAL_DIELECTRIC 2
#define MATERIAL_EMISSIVE 3
// Light types
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define LIGHT_SPOT 2
// Structures
struct Material {
vec3 albedo;
float metallic;
vec3 emission;
float roughness;
int type;
float ior;
vec2 padding;
};
struct Light {
vec3 position;
int type;
vec3 direction;
float intensity;
vec3 color;
float range;
vec2 spot_angles; // inner, outer
vec2 padding;
};
struct Ray {
vec3 origin;
vec3 direction;
};
struct HitInfo {
bool hit;
float t;
vec3 position;
vec3 normal;
vec2 texcoord;
uint material_id;
};
// Utility functions
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
vec3 saturate(vec3 x) {
return clamp(x, vec3(0.0), vec3(1.0));
}
// Random number generation (PCG Hash)
uint pcg_hash(uint seed) {
uint state = seed * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
float random_float(inout uint seed) {
seed = pcg_hash(seed);
return float(seed) / 4294967296.0;
}
vec2 random_vec2(inout uint seed) {
return vec2(random_float(seed), random_float(seed));
}
vec3 random_vec3(inout uint seed) {
return vec3(random_float(seed), random_float(seed), random_float(seed));
}
// Random direction in hemisphere
vec3 random_hemisphere_direction(vec3 normal, inout uint seed) {
float z = random_float(seed);
float r = sqrt(max(0.0, 1.0 - z * z));
float phi = 2.0 * PI * random_float(seed);
vec3 dir = vec3(r * cos(phi), r * sin(phi), z);
// Create coordinate system
vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, normal));
vec3 bitangent = cross(normal, tangent);
return normalize(tangent * dir.x + bitangent * dir.y + normal * dir.z);
}
// Cosine-weighted hemisphere sampling
// vec3 cosine_weighted_hemisphere(vec3 normal, inout uint seed) {
// vec2 r = random_vec2(seed);
// float r1 = 2.0 * PI * r.x;
// float r2 = r.y;
// float r2s = sqrt(r2);
//
// vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
// vec3 tangent = normalize(cross(up, normal));
// vec3 bitangent = cross(normal, tangent);
//
// vec3 dir = tangent * cos(r1) * r2s + bitangent * sin(r1) * r2s + normal * sqrt(1.0 - r2);
// return normalize(dir);
// }
// Schlick's approximation for Fresnel
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
}
// GGX distribution
float distribution_ggx(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, EPSILON);
}
// Smith's geometry function
float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float r = roughness + 1.0;
float k = (r * r) / 8.0;
float ggx1 = NdotV / (NdotV * (1.0 - k) + k);
float ggx2 = NdotL / (NdotL * (1.0 - k) + k);
return ggx1 * ggx2;
}
layout(local_size_x = 16, local_size_y = 16) in;
// G-Buffer inputs
layout(binding = 0, rgba32f) uniform readonly image2D g_position;
layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
// Output
layout(binding = 3, rgba32f) uniform image2D output_image;
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
// Scene data
layout(std430, binding = 0) readonly buffer MaterialBuffer {
Material materials[];
};
layout(std430, binding = 1) readonly buffer LightBuffer {
Light lights[];
};
// Uniforms
uniform uint u_frame_count;
uniform uint u_samples_per_pixel;
uniform uint u_max_depth;
uniform uint u_light_count;
uniform vec3 u_camera_position;
uniform mat4 u_inv_view_projection;
uniform bool u_enable_accumulation;
uniform bool u_use_bvh;
// Evaluate direct lighting
vec3 evaluate_direct_lighting(vec3 position, vec3 normal, vec3 view_dir,
Material material, inout uint seed) {
vec3 direct_light = vec3(0.0);
for (uint i = 0u; i < u_light_count; i++) {
Light light = lights[i];
vec3 light_dir;
float light_distance;
float attenuation = 1.0;
if (light.type == LIGHT_POINT) {
vec3 to_light = light.position - position;
light_distance = length(to_light);
light_dir = to_light / light_distance;
attenuation = 1.0 / max(light_distance * light_distance, 0.01);
if (light_distance > light.range) continue;
} else if (light.type == LIGHT_DIRECTIONAL) {
light_dir = normalize(-light.direction);
light_distance = MAX_FLOAT;
} else {
continue;
}
float NdotL = max(dot(normal, light_dir), 0.0);
if (NdotL > 0.0) {
vec3 H = normalize(view_dir + light_dir);
float NdotV = max(dot(normal, view_dir), 0.0);
// PBR lighting
vec3 F0 = mix(vec3(0.04), material.albedo, material.metallic);
vec3 F = fresnel_schlick(max(dot(H, view_dir), 0.0), F0);
float D = distribution_ggx(normal, H, max(material.roughness, 0.04));
float G = geometry_smith(normal, view_dir, light_dir, material.roughness);
vec3 numerator = D * G * F;
float denominator = 4.0 * NdotV * NdotL + EPSILON;
vec3 specular = numerator / denominator;
vec3 kS = F;
vec3 kD = (vec3(1.0) - kS) * (1.0 - material.metallic);
vec3 radiance = light.color * light.intensity * attenuation;
direct_light += (kD * material.albedo * INV_PI + specular) * radiance * NdotL;
}
}
return direct_light;
}
void main() {
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 image_size = imageSize(output_image);
if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) {
return;
}
// Read G-Buffer
vec4 position_data = imageLoad(g_position, pixel_coords);
vec4 normal_data = imageLoad(g_normal, pixel_coords);
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
// Background
if (position_data.w < 0.5) {
vec3 background = vec3(0.1, 0.1, 0.15);
imageStore(output_image, pixel_coords, vec4(background, 1.0));
imageStore(accumulation_image, pixel_coords, vec4(background, 1.0));
return;
}
vec3 position = position_data.xyz;
vec3 normal = normalize(normal_data.xyz);
vec3 albedo = albedo_data.rgb;
// 关键修复从G-Buffer的alpha通道读取material_id
// 注意albedo_data是从RGBA8纹理读取的alpha值范围是[0,1]
// 我们需要将其转换回整数ID
uint material_id = uint(albedo_data.a * 255.0 + 0.5);
// Initialize random seed
uint seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x) + u_frame_count * 719393u;
vec3 color = vec3(0.0);
// Get material from buffer
Material material;
uint mat_count = uint(materials.length());
if (material_id < mat_count) {
// 从SSBO读取材质
material = materials[material_id];
} else {
// 使用G-Buffer中的albedo作为fallback
material.albedo = albedo;
material.metallic = 0.0;
material.roughness = 0.5;
material.emission = vec3(0.0);
material.type = MATERIAL_DIFFUSE;
material.ior = 1.5;
}
// Add emission
color += material.emission;
// Direct lighting
vec3 view_dir = normalize(u_camera_position - position);
if (u_light_count > 0u) {
color += evaluate_direct_lighting(position, normal, view_dir, material, seed);
}
// Ambient lighting
color += material.albedo * 0.05;
// Clamp
color = clamp(color, vec3(0.0), vec3(10.0));
// Accumulation
if (u_enable_accumulation && u_frame_count > 0u) {
vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
float weight = 1.0 / float(u_frame_count + 1u);
color = mix(accumulated, color, weight);
}
imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
imageStore(output_image, pixel_coords, vec4(color, 1.0));
}
// layout(local_size_x = 16, local_size_y = 16) in;
//
// // G-Buffer inputs
// layout(binding = 0, rgba32f) uniform readonly image2D g_position;
// layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
// layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
//
// // Output
// layout(binding = 3, rgba32f) uniform image2D output_image;
// layout(binding = 4, rgba32f) uniform image2D accumulation_image;
//
// // Scene data
// layout(std430, binding = 0) readonly buffer MaterialBuffer {
// Material materials[];
// };
//
// layout(std430, binding = 1) readonly buffer LightBuffer {
// Light lights[];
// };
//
// // Uniforms
// uniform uint u_frame_count;
// uniform uint u_samples_per_pixel;
// uniform uint u_max_depth;
// uniform uint u_light_count;
// uniform vec3 u_camera_position;
// uniform mat4 u_inv_view_projection;
// uniform bool u_enable_accumulation;
// uniform bool u_use_bvh;
//
// // Cosine-weighted hemisphere sampling
// vec3 cosine_weighted_hemisphere(vec3 normal, inout uint seed) {
// vec2 r = random_vec2(seed);
// float r1 = 2.0 * PI * r.x;
// float r2 = r.y;
// float r2s = sqrt(r2);
//
// vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
// vec3 tangent = normalize(cross(up, normal));
// vec3 bitangent = cross(normal, tangent);
//
// vec3 dir = tangent * cos(r1) * r2s + bitangent * sin(r1) * r2s + normal * sqrt(1.0 - r2);
// return normalize(dir);
// }
//
// // Evaluate direct lighting
// vec3 evaluate_direct_lighting(vec3 position, vec3 normal, vec3 view_dir,
// Material material, inout uint seed) {
// vec3 direct_light = vec3(0.0);
//
// for (uint i = 0u; i < u_light_count; i++) {
// Light light = lights[i];
// vec3 light_dir;
// float light_distance;
// float attenuation = 1.0;
//
// if (light.type == LIGHT_POINT) {
// vec3 to_light = light.position - position;
// light_distance = length(to_light);
// light_dir = to_light / light_distance;
// attenuation = 1.0 / max(light_distance * light_distance, 0.01);
//
// if (light_distance > light.range) continue;
// } else if (light.type == LIGHT_DIRECTIONAL) {
// light_dir = normalize(-light.direction);
// light_distance = MAX_FLOAT;
// } else {
// continue;
// }
//
// float NdotL = max(dot(normal, light_dir), 0.0);
//
// if (NdotL > 0.0) {
// vec3 H = normalize(view_dir + light_dir);
// float NdotV = max(dot(normal, view_dir), 0.0);
//
// // PBR lighting
// vec3 F0 = mix(vec3(0.04), material.albedo, material.metallic);
// vec3 F = fresnel_schlick(max(dot(H, view_dir), 0.0), F0);
// float D = distribution_ggx(normal, H, max(material.roughness, 0.04));
// float G = geometry_smith(normal, view_dir, light_dir, material.roughness);
//
// vec3 numerator = D * G * F;
// float denominator = 4.0 * NdotV * NdotL + EPSILON;
// vec3 specular = numerator / denominator;
//
// vec3 kS = F;
// vec3 kD = (vec3(1.0) - kS) * (1.0 - material.metallic);
//
// vec3 radiance = light.color * light.intensity * attenuation;
// direct_light += (kD * material.albedo * INV_PI + specular) * radiance * NdotL;
// }
// }
//
// return direct_light;
// }
//
// // Trace indirect lighting (simple path tracing)
// vec3 trace_indirect(vec3 position, vec3 normal, Material material, inout uint seed) {
// vec3 color = vec3(0.0);
//
// // Sample random direction
// vec3 ray_dir = cosine_weighted_hemisphere(normal, seed);
//
// // Sky color based on direction
// float t = 0.5 * (ray_dir.y + 1.0);
// vec3 sky_color = mix(vec3(1.0), vec3(0.5, 0.7, 1.0), t) * 0.2;
//
// color = sky_color;
//
// return color;
// }
//
// void main() {
// ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
// ivec2 image_size = imageSize(output_image);
//
// if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) {
// return;
// }
//
// // Read G-Buffer
// vec4 position_data = imageLoad(g_position, pixel_coords);
// vec4 normal_data = imageLoad(g_normal, pixel_coords);
// vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
//
// // Check if this pixel has valid geometry
// if (position_data.w < 0.5) {
// vec3 background = vec3(0.1, 0.1, 0.15);
// imageStore(output_image, pixel_coords, vec4(background, 1.0));
// imageStore(accumulation_image, pixel_coords, vec4(background, 1.0));
// return;
// }
//
// vec3 position = position_data.xyz;
// vec3 normal = normalize(normal_data.xyz);
// vec3 albedo = albedo_data.rgb;
// uint material_id = floatBitsToUint(albedo_data.a);
//
// if (material_id >= 1000u) {
// material_id = 0u;
// }
//
// // Initialize random seed
// uint seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x) + u_frame_count * 719393u;
//
// vec3 color = vec3(0.0);
//
// // Get material
// Material material;
// if (material_id < uint(materials.length())) {
// material = materials[material_id];
// } else {
// material.albedo = albedo;
// material.metallic = 0.0;
// material.roughness = 0.5;
// material.emission = vec3(0.0);
// material.type = MATERIAL_DIFFUSE;
// material.ior = 1.5;
// }
//
// // Add emission
// color += material.emission;
//
// // Direct lighting
// vec3 view_dir = normalize(u_camera_position - position);
//
// if (u_light_count > 0u) {
// color += evaluate_direct_lighting(position, normal, view_dir, material, seed);
// }
//
// // Indirect lighting (path tracing) - THIS ADDS NOISE
// for (uint samp_idx = 0u; samp_idx < u_samples_per_pixel; samp_idx++) { // 修复: sample -> samp_idx
// vec3 indirect = trace_indirect(position, normal, material, seed);
// color += indirect * material.albedo * INV_PI;
// }
//
// // Ambient
// color += material.albedo * 0.02;
//
// // Clamp
// color = clamp(color, vec3(0.0), vec3(100.0));
//
// // Accumulation for denoising
// if (u_enable_accumulation && u_frame_count > 0u) {
// vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
// float weight = 1.0 / float(u_frame_count + 1u);
// color = mix(accumulated, color, weight);
// }
//
// imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
// imageStore(output_image, pixel_coords, vec4(color, 1.0));
// }

11
shaders/screen_blit.frag Normal file
View File

@ -0,0 +1,11 @@
#version 430 core
in vec2 v_texcoord;
out vec4 frag_color;
uniform sampler2D u_texture;
void main() {
frag_color = texture(u_texture, v_texcoord);
}

11
shaders/screen_blit.vert Normal file
View File

@ -0,0 +1,11 @@
#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);
}

33
src/basic/math.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "basic/math.h"
namespace are {
Mat4 MathUtils::perspective(float fov, float aspect, float near, float far) {
return glm::perspective(fov, aspect, near, far);
}
Mat4 MathUtils::look_at(const Vec3& eye, const Vec3& center, const Vec3& up) {
return glm::lookAt(eye, center, up);
}
Vec3 MathUtils::normalize(const Vec3& v) {
return glm::normalize(v);
}
float MathUtils::dot(const Vec3& a, const Vec3& b) {
return glm::dot(a, b);
}
Vec3 MathUtils::cross(const Vec3& a, const Vec3& b) {
return glm::cross(a, b);
}
Vec3 MathUtils::reflect(const Vec3& incident, const Vec3& normal) {
return glm::reflect(incident, normal);
}
const float* MathUtils::value_ptr(const Mat4& mat) {
return glm::value_ptr(mat);
}
} // namespace are

297
src/core/bvh.cpp Normal file
View File

@ -0,0 +1,297 @@
#include "core/bvh.h"
#include "utils/logger.h"
#include "basic/constants.h"
#include <algorithm>
#include <limits>
namespace are {
// AABB implementation
void AABB::expand(const Vec3& point) {
min_ = glm::min(min_, point);
max_ = glm::max(max_, point);
}
void AABB::expand(const AABB& other) {
min_ = glm::min(min_, other.min_);
max_ = glm::max(max_, other.max_);
}
float AABB::surface_area() const {
Vec3 extent = max_ - min_;
return 2.0f * (extent.x * extent.y + extent.y * extent.z + extent.z * extent.x);
}
bool AABB::is_valid() const {
return min_.x <= max_.x && min_.y <= max_.y && min_.z <= max_.z;
}
// Triangle implementation
AABB Triangle::get_bounds() const {
AABB bounds(v0_, v0_);
bounds.expand(v1_);
bounds.expand(v2_);
return bounds;
}
Vec3 Triangle::get_centroid() const {
return (v0_ + v1_ + v2_) / 3.0f;
}
// BVH implementation
BVH::BVH() {
}
BVH::~BVH() {
clear();
}
bool BVH::build(const std::vector<std::shared_ptr<Mesh>>& meshes) {
clear();
Logger::info("Building BVH...");
// Extract all triangles from meshes
for (const auto& mesh : meshes) {
const auto& vertices = mesh->get_vertices();
const auto& indices = mesh->get_indices();
uint material_id = mesh->get_material();
Mat4 transform = mesh->get_transform();
for (size_t i = 0; i < indices.size(); i += 3) {
Triangle tri;
// Transform vertices
Vec4 v0 = transform * Vec4(vertices[indices[i]].position_, 1.0f);
Vec4 v1 = transform * Vec4(vertices[indices[i + 1]].position_, 1.0f);
Vec4 v2 = transform * Vec4(vertices[indices[i + 2]].position_, 1.0f);
tri.v0_ = Vec3(v0) / v0.w;
tri.v1_ = Vec3(v1) / v1.w;
tri.v2_ = Vec3(v2) / v2.w;
// Transform normals
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(transform)));
tri.n0_ = glm::normalize(normal_matrix * vertices[indices[i]].normal_);
tri.n1_ = glm::normalize(normal_matrix * vertices[indices[i + 1]].normal_);
tri.n2_ = glm::normalize(normal_matrix * vertices[indices[i + 2]].normal_);
// Copy UVs
tri.uv0_ = vertices[indices[i]].texcoord_;
tri.uv1_ = vertices[indices[i + 1]].texcoord_;
tri.uv2_ = vertices[indices[i + 2]].texcoord_;
tri.material_id_ = material_id;
triangles_.push_back(tri);
}
}
if (triangles_.empty()) {
Logger::warning("No triangles to build BVH");
return false;
}
// Initialize triangle indices
triangle_indices_.resize(triangles_.size());
for (size_t i = 0; i < triangles_.size(); ++i) {
triangle_indices_[i] = static_cast<uint>(i);
}
// Reserve space for nodes (estimate)
nodes_.reserve(triangles_.size() * 2);
// Create root node
nodes_.emplace_back();
// Build BVH recursively
build_recursive_(0, 0, static_cast<uint>(triangles_.size()));
Logger::info("BVH built: " + std::to_string(nodes_.size()) + " nodes, " +
std::to_string(triangles_.size()) + " triangles");
return true;
}
void BVH::build_recursive_(uint node_idx, uint first_prim, uint prim_count) {
BVHNode& node = nodes_[node_idx];
// Calculate bounds
AABB bounds = calculate_bounds_(first_prim, prim_count);
node.aabb_min_ = bounds.min_;
node.aabb_max_ = bounds.max_;
// Leaf node threshold
const uint LEAF_SIZE = 4;
if (prim_count <= LEAF_SIZE) {
// Create leaf node
node.left_first_ = first_prim;
node.count_ = prim_count;
return;
}
// Find best split
int axis;
float split_pos;
float split_cost = find_best_split_(first_prim, prim_count, axis, split_pos);
// Check if split is beneficial
float no_split_cost = prim_count * bounds.surface_area();
if (split_cost >= no_split_cost) {
// Create leaf node
node.left_first_ = first_prim;
node.count_ = prim_count;
return;
}
// Partition primitives
uint mid = first_prim;
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
Triangle& tri = triangles_[triangle_indices_[i]];
float centroid = tri.get_centroid()[axis];
if (centroid < split_pos) {
std::swap(triangle_indices_[i], triangle_indices_[mid]);
mid++;
}
}
// Ensure we have primitives on both sides
if (mid == first_prim || mid == first_prim + prim_count) {
mid = first_prim + prim_count / 2;
}
// Create interior node
uint left_count = mid - first_prim;
uint right_count = prim_count - left_count;
node.left_first_ = static_cast<uint>(nodes_.size());
node.count_ = 0;
// Create child nodes
nodes_.emplace_back();
nodes_.emplace_back();
// Recursively build children
build_recursive_(node.left_first_, first_prim, left_count);
build_recursive_(node.left_first_ + 1, mid, right_count);
}
float BVH::find_best_split_(uint first_prim, uint prim_count, int& axis, float& split_pos) {
float best_cost = std::numeric_limits<float>::max();
AABB centroid_bounds = calculate_centroid_bounds_(first_prim, prim_count);
// Try each axis
for (int a = 0; a < 3; ++a) {
float extent = centroid_bounds.max_[a] - centroid_bounds.min_[a];
if (extent < EPSILON) continue;
// Try multiple split positions
const int NUM_BINS = 16;
for (int i = 1; i < NUM_BINS; ++i) {
float t = static_cast<float>(i) / NUM_BINS;
float pos = centroid_bounds.min_[a] + t * extent;
// Count primitives and calculate bounds for each side
AABB left_bounds, right_bounds;
uint left_count = 0, right_count = 0;
for (uint j = first_prim; j < first_prim + prim_count; ++j) {
Triangle& tri = triangles_[triangle_indices_[j]];
float centroid = tri.get_centroid()[a];
if (centroid < pos) {
left_bounds.expand(tri.get_bounds());
left_count++;
} else {
right_bounds.expand(tri.get_bounds());
right_count++;
}
}
// Calculate SAH cost
if (left_count == 0 || right_count == 0) continue;
float cost = left_count * left_bounds.surface_area() +
right_count * right_bounds.surface_area();
if (cost < best_cost) {
best_cost = cost;
axis = a;
split_pos = pos;
}
}
}
return best_cost;
}
AABB BVH::calculate_bounds_(uint first_prim, uint prim_count) {
AABB bounds{Vec3(std::numeric_limits<float>::max()),
Vec3(std::numeric_limits<float>::lowest())};
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
Triangle& tri = triangles_[triangle_indices_[i]];
bounds.expand(tri.get_bounds());
}
return bounds;
}
AABB BVH::calculate_centroid_bounds_(uint first_prim, uint prim_count) {
AABB bounds{Vec3(std::numeric_limits<float>::max()),
Vec3(std::numeric_limits<float>::lowest())};
for (uint i = first_prim; i < first_prim + prim_count; ++i) {
Triangle& tri = triangles_[triangle_indices_[i]];
bounds.expand(tri.get_centroid());
}
return bounds;
}
bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) {
if (nodes_.empty() || triangles_.empty()) {
Logger::error("Cannot upload empty BVH to GPU");
return false;
}
// Reorder triangles according to BVH layout
std::vector<Triangle> ordered_triangles;
ordered_triangles.reserve(triangles_.size());
for (uint idx : triangle_indices_) {
ordered_triangles.push_back(triangles_[idx]);
}
// Upload nodes
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
nodes_.size() * sizeof(BVHNode),
nodes_.data(),
BufferUsage::STATIC_DRAW)) {
Logger::error("Failed to upload BVH nodes to GPU");
return false;
}
// Upload triangles
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
ordered_triangles.size() * sizeof(Triangle),
ordered_triangles.data(),
BufferUsage::STATIC_DRAW)) {
Logger::error("Failed to upload BVH triangles to GPU");
return false;
}
Logger::info("BVH uploaded to GPU successfully");
return true;
}
void BVH::clear() {
nodes_.clear();
triangles_.clear();
triangle_indices_.clear();
}
} // namespace are

221
src/core/gbuffer.cpp Normal file
View File

@ -0,0 +1,221 @@
#include "core/gbuffer.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
GBuffer::GBuffer(uint width, uint height)
: width_(width)
, height_(height)
, fbo_(INVALID_HANDLE)
, depth_texture_(INVALID_HANDLE)
, initialized_(false) {
for (int i = 0; i < GBUFFER_COUNT; ++i) {
textures_[i] = INVALID_HANDLE;
}
}
GBuffer::~GBuffer() {
release();
}
bool GBuffer::initialize() {
if (initialized_) {
Logger::warning("GBuffer already initialized");
return true;
}
// Create framebuffer
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// Create G-Buffer textures
textures_[GBUFFER_POSITION] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
textures_[GBUFFER_NORMAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
textures_[GBUFFER_ALBEDO] = create_texture_(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
// Attach textures to framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0);
// Create depth texture
glGenTextures(1, &depth_texture_);
glBindTexture(GL_TEXTURE_2D, depth_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width_, height_, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, depth_texture_, 0);
// Set draw buffers
GLenum draw_buffers[GBUFFER_COUNT] = {
GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO
};
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
// Check framebuffer completeness
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Logger::error("GBuffer framebuffer is not complete");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
initialized_ = true;
Logger::info("GBuffer initialized successfully");
return true;
}
void GBuffer::release() {
if (!initialized_) return;
if (fbo_ != INVALID_HANDLE) {
glDeleteFramebuffers(1, &fbo_);
fbo_ = INVALID_HANDLE;
}
for (int i = 0; i < GBUFFER_COUNT; ++i) {
if (textures_[i] != INVALID_HANDLE) {
glDeleteTextures(1, &textures_[i]);
textures_[i] = INVALID_HANDLE;
}
}
if (depth_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &depth_texture_);
depth_texture_ = INVALID_HANDLE;
}
initialized_ = false;
Logger::info("GBuffer released");
}
void GBuffer::render(const Scene& scene, const Shader& shader) {
if (!initialized_) {
Logger::error("GBuffer not initialized");
return;
}
// Bind framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
glViewport(0, 0, width_, height_);
// Clear buffers
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
// Use shader
shader.use();
// Set camera matrices
const Camera& camera = scene.get_camera();
Mat4 view = camera.get_view_matrix();
Mat4 projection = camera.get_projection_matrix();
shader.set_mat4("u_view", view);
shader.set_mat4("u_projection", projection);
// Render all meshes
const auto& meshes = scene.get_meshes();
const auto& materials = scene.get_materials();
for (const auto& mesh : meshes) {
if (!mesh->is_uploaded()) {
Logger::warning("Mesh not uploaded to GPU, skipping");
continue;
}
// Set model matrix
Mat4 model = mesh->get_transform();
shader.set_mat4("u_model", model);
// Set material properties
uint material_id = mesh->get_material();
if (material_id < materials.size()) {
const auto& material = materials[material_id];
shader.set_vec3("u_albedo", material->get_albedo());
shader.set_float("u_metallic", material->get_metallic());
shader.set_float("u_roughness", material->get_roughness());
shader.set_uint("u_material_id", material_id);
// Bind textures
auto albedo_tex = material->get_albedo_texture();
if (albedo_tex && albedo_tex->is_valid()) {
albedo_tex->bind(0);
shader.set_int("u_albedo_map", 0);
shader.set_int("u_has_albedo_map", 1);
} else {
shader.set_int("u_has_albedo_map", 0);
}
auto normal_tex = material->get_normal_texture();
if (normal_tex && normal_tex->is_valid()) {
normal_tex->bind(1);
shader.set_int("u_normal_map", 1);
shader.set_int("u_has_normal_map", 1);
} else {
shader.set_int("u_has_normal_map", 0);
}
}
// Draw mesh
glBindVertexArray(mesh->get_vao());
glDrawElements(GL_TRIANGLES, mesh->get_indices().size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
// Unbind framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void GBuffer::resize(uint width, uint height) {
if (width == width_ && height == height_) return;
width_ = width;
height_ = height;
if (initialized_) {
release();
initialize();
}
}
TextureHandle GBuffer::get_texture(int index) const {
if (index < 0 || index >= GBUFFER_COUNT) {
Logger::error("Invalid G-Buffer texture index");
return INVALID_HANDLE;
}
return textures_[index];
}
void GBuffer::get_dimensions(uint& width, uint& height) const {
width = width_;
height = height_;
}
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
TextureHandle texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;
}
} // namespace are

316
src/core/raytracer.cpp Normal file
View File

@ -0,0 +1,316 @@
#include "core/raytracer.h"
#include "utils/logger.h"
#include "basic/constants.h"
#include <glad/glad.h>
namespace are {
RayTracer::RayTracer(uint width, uint height, const RayTracerConfig& config)
: width_(width)
, height_(height)
, config_(config)
, accumulation_texture_(INVALID_HANDLE)
, scene_buffer_(INVALID_HANDLE)
, material_buffer_(INVALID_HANDLE)
, light_buffer_(INVALID_HANDLE)
, bvh_(nullptr)
, bvh_built_(false)
, frame_count_(0)
, initialized_(false) {
}
RayTracer::~RayTracer() {
release();
}
bool RayTracer::initialize() {
if (initialized_) {
Logger::warning("RayTracer already initialized");
return true;
}
// Create accumulation texture
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create shader storage buffers
glGenBuffers(1, &material_buffer_);
glGenBuffers(1, &light_buffer_);
// Load compute shader
Logger::info("Loading ray tracing compute shader in RayTracer...");
if (!compute_shader_.load_compute("shaders/raytracing.comp")) {
Logger::error("Failed to load ray tracing compute shader in RayTracer");
return false;
}
Logger::info("Ray tracing compute shader loaded in RayTracer");
// Initialize BVH if enabled
if (config_.use_bvh_) {
bvh_ = std::make_unique<BVH>();
}
initialized_ = true;
Logger::info("RayTracer initialized successfully");
return true;
}
void RayTracer::release() {
if (!initialized_) return;
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
accumulation_texture_ = INVALID_HANDLE;
}
if (material_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &material_buffer_);
material_buffer_ = INVALID_HANDLE;
}
if (light_buffer_ != INVALID_HANDLE) {
glDeleteBuffers(1, &light_buffer_);
light_buffer_ = INVALID_HANDLE;
}
bvh_node_buffer_.release();
bvh_triangle_buffer_.release();
compute_shader_.release();
bvh_.reset();
bvh_built_ = false;
initialized_ = false;
Logger::info("RayTracer released");
}
bool RayTracer::rebuild_bvh(const Scene& scene) {
if (!config_.use_bvh_) {
Logger::warning("BVH is disabled in configuration");
return false;
}
if (!bvh_) {
bvh_ = std::make_unique<BVH>();
}
Logger::info("Building BVH for ray tracing...");
if (!bvh_->build(scene.get_meshes())) {
Logger::error("Failed to build BVH");
return false;
}
if (!bvh_->upload_to_gpu(bvh_node_buffer_, bvh_triangle_buffer_)) {
Logger::error("Failed to upload BVH to GPU");
return false;
}
bvh_built_ = true;
Logger::info("BVH built and uploaded successfully");
return true;
}
void RayTracer::trace(const Scene& scene, const GBuffer& gbuffer, TextureHandle output_texture) {
if (!initialized_) {
Logger::error("RayTracer not initialized");
return;
}
if (!compute_shader_.is_valid()) {
Logger::error("Ray tracing compute shader not loaded");
return;
}
// Build BVH if enabled and not built yet
if (config_.use_bvh_ && !bvh_built_) {
rebuild_bvh(scene);
}
// Upload scene data
upload_scene_data_(scene);
// Use compute shader
compute_shader_.use();
// Bind G-Buffer textures
bind_gbuffer_(gbuffer);
// Bind output and accumulation textures
glBindImageTexture(3, output_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glBindImageTexture(4, accumulation_texture_, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
// Bind BVH buffers if enabled
if (config_.use_bvh_ && bvh_built_) {
bvh_node_buffer_.bind_base(2);
bvh_triangle_buffer_.bind_base(3);
compute_shader_.set_bool("u_use_bvh", true);
compute_shader_.set_uint("u_bvh_node_count", bvh_->get_node_count());
} else {
compute_shader_.set_bool("u_use_bvh", false);
}
// Set uniforms
compute_shader_.set_uint("u_frame_count", frame_count_);
compute_shader_.set_uint("u_samples_per_pixel", config_.samples_per_pixel_);
compute_shader_.set_uint("u_max_depth", config_.max_depth_);
compute_shader_.set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
compute_shader_.set_bool("u_enable_accumulation", config_.enable_accumulation_);
// Set camera data
const Camera& camera = scene.get_camera();
compute_shader_.set_vec3("u_camera_position", camera.get_position());
Mat4 inv_vp = glm::inverse(camera.get_view_projection_matrix());
compute_shader_.set_mat4("u_inv_view_projection", inv_vp);
// Dispatch compute shader
uint num_groups_x = (width_ + COMPUTE_GROUP_SIZE_X - 1) / COMPUTE_GROUP_SIZE_X;
uint num_groups_y = (height_ + COMPUTE_GROUP_SIZE_Y - 1) / COMPUTE_GROUP_SIZE_Y;
glDispatchCompute(num_groups_x, num_groups_y, 1);
// Memory barrier
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// Increment frame count for accumulation
if (config_.enable_accumulation_) {
frame_count_++;
}
}
void RayTracer::resize(uint width, uint height) {
if (width == width_ && height == height_) return;
width_ = width;
height_ = height;
if (initialized_) {
// Recreate accumulation texture
if (accumulation_texture_ != INVALID_HANDLE) {
glDeleteTextures(1, &accumulation_texture_);
}
glGenTextures(1, &accumulation_texture_);
glBindTexture(GL_TEXTURE_2D, accumulation_texture_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width_, height_, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
reset_accumulation();
}
}
void RayTracer::reset_accumulation() {
frame_count_ = 0;
}
void RayTracer::set_config(const RayTracerConfig& config) {
bool bvh_changed = (config.use_bvh_ != config_.use_bvh_);
config_ = config;
reset_accumulation();
if (bvh_changed) {
if (config_.use_bvh_ && !bvh_) {
bvh_ = std::make_unique<BVH>();
bvh_built_ = false;
} else if (!config_.use_bvh_) {
bvh_.reset();
bvh_built_ = false;
}
}
}
void RayTracer::upload_scene_data_(const Scene& scene) {
// Upload materials
const auto& materials = scene.get_materials();
if (!materials.empty()) {
struct MaterialData {
Vec3 albedo;
float metallic;
Vec3 emission;
float roughness;
int type;
float ior;
Vec2 padding;
};
std::vector<MaterialData> material_data;
material_data.reserve(materials.size());
for (const auto& mat : materials) {
MaterialData data;
data.albedo = mat->get_albedo();
data.metallic = mat->get_metallic();
data.emission = mat->get_emission();
data.roughness = mat->get_roughness();
data.type = static_cast<int>(mat->get_type());
data.ior = mat->get_ior();
material_data.push_back(data);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
material_data.size() * sizeof(MaterialData),
material_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
}
// Upload lights
const auto& lights = scene.get_lights();
if (!lights.empty()) {
struct LightData {
Vec3 position;
int type;
Vec3 direction;
float intensity;
Vec3 color;
float range;
Vec2 spot_angles;
Vec2 padding;
};
std::vector<LightData> light_data;
light_data.reserve(lights.size());
for (const auto& light : lights) {
LightData data;
data.position = light->get_position();
data.type = static_cast<int>(light->get_type());
data.direction = light->get_direction();
data.intensity = light->get_intensity();
data.color = light->get_color();
data.range = light->get_range();
data.spot_angles = Vec2(light->get_inner_angle(), light->get_outer_angle());
light_data.push_back(data);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_);
glBufferData(GL_SHADER_STORAGE_BUFFER,
light_data.size() * sizeof(LightData),
light_data.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_);
}
}
void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) {
glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
}
void RayTracer::set_compute_shader(const Shader& shader) {
compute_shader_ = shader;
Logger::info("Compute shader set for RayTracer");
}
} // namespace are

205
src/core/renderer.cpp Normal file
View File

@ -0,0 +1,205 @@
#include "core/renderer.h"
#include "utils/logger.h"
#include <chrono>
#include <glad/glad.h>
namespace are {
Renderer::Renderer(const RendererConfig &config)
: config_(config)
, initialized_(false)
, frame_count_(0) {
}
Renderer::~Renderer() {
shutdown();
}
bool Renderer::initialize() {
if (initialized_) {
Logger::warning("Renderer already initialized");
return true;
}
Logger::info("Initializing Aurora Rendering Engine...");
// Initialize shader manager
shader_manager_ = std::make_unique<ShaderManager>();
if (!shader_manager_->initialize()) {
Logger::error("Failed to initialize shader manager");
return false;
}
// Initialize G-Buffer
gbuffer_ = std::make_unique<GBuffer>(config_.width_, config_.height_);
if (!gbuffer_->initialize()) {
Logger::error("Failed to initialize G-Buffer");
return false;
}
// Initialize ray tracer
RayTracerConfig rt_config;
rt_config.samples_per_pixel_ = config_.samples_per_pixel_;
rt_config.max_depth_ = config_.max_ray_depth_;
rt_config.enable_shadows_ = true;
rt_config.enable_reflections_ = true;
rt_config.enable_accumulation_ = config_.enable_accumulation_;
rt_config.use_bvh_ = true;
raytracer_ = std::make_unique<RayTracer>(config_.width_, config_.height_, rt_config);
if (!raytracer_->initialize()) {
Logger::error("Failed to initialize ray tracer");
return false;
}
// Pass compute shader to ray tracer
const Shader& rt_shader = shader_manager_->get_raytracing_shader();
if (!rt_shader.is_valid()) {
Logger::error("Ray tracing shader is invalid");
return false;
}
raytracer_->set_compute_shader(rt_shader);
// Initialize screen blit
screen_blit_ = std::make_unique<ScreenBlit>();
if (!screen_blit_->initialize()) {
Logger::error("Failed to initialize screen blit");
return false;
}
initialized_ = true;
Logger::info("Aurora Rendering Engine initialized successfully");
return true;
}
void Renderer::shutdown() {
if (!initialized_)
return;
Logger::info("Shutting down Aurora Rendering Engine...");
if (screen_blit_) {
screen_blit_->release();
screen_blit_.reset();
}
if (raytracer_) {
raytracer_->release();
raytracer_.reset();
}
if (gbuffer_) {
gbuffer_->release();
gbuffer_.reset();
}
if (shader_manager_) {
shader_manager_->release();
shader_manager_.reset();
}
initialized_ = false;
Logger::info("Aurora Rendering Engine shut down");
}
RenderStats Renderer::render(const Scene& scene, TextureHandle output_texture) {
RenderStats stats = {};
if (!initialized_) {
Logger::error("Renderer not initialized");
return stats;
}
// Start timing
auto start_time = std::chrono::high_resolution_clock::now();
// Phase 1: G-Buffer pass
auto gbuffer_start = std::chrono::high_resolution_clock::now();
const Shader& gbuffer_shader = shader_manager_->get_gbuffer_shader();
gbuffer_->render(scene, gbuffer_shader);
auto gbuffer_end = std::chrono::high_resolution_clock::now();
stats.gbuffer_time_ms_ = std::chrono::duration<float, std::milli>(gbuffer_end - gbuffer_start).count();
// Phase 2: Ray tracing pass
auto raytrace_start = std::chrono::high_resolution_clock::now();
// Create output texture if not provided
TextureHandle rt_output = output_texture;
bool created_temp_texture = false;
if (rt_output == 0) {
glGenTextures(1, &rt_output);
glBindTexture(GL_TEXTURE_2D, rt_output);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, config_.width_, config_.height_,
0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
created_temp_texture = true;
}
raytracer_->trace(scene, *gbuffer_, rt_output);
auto raytrace_end = std::chrono::high_resolution_clock::now();
stats.raytrace_time_ms_ = std::chrono::duration<float, std::milli>(raytrace_end - raytrace_start).count();
// Phase 3: Blit to screen if output is default framebuffer
if (created_temp_texture && output_texture == 0) {
screen_blit_->blit_fullscreen(rt_output);
glDeleteTextures(1, &rt_output);
}
// Calculate total frame time
auto end_time = std::chrono::high_resolution_clock::now();
stats.frame_time_ms_ = std::chrono::duration<float, std::milli>(end_time - start_time).count();
// Count triangles
const auto& meshes = scene.get_meshes();
for (const auto& mesh : meshes) {
stats.triangle_count_ += mesh->get_indices().size() / 3;
}
// Estimate ray count (very rough)
stats.ray_count_ = config_.width_ * config_.height_ * config_.samples_per_pixel_ * config_.max_ray_depth_;
frame_count_++;
return stats;
}
void Renderer::resize(uint width, uint height) {
if (width == config_.width_ && height == config_.height_)
return;
config_.width_ = width;
config_.height_ = height;
if (initialized_) {
gbuffer_->resize(width, height);
raytracer_->resize(width, height);
Logger::info("Renderer resized to " + std::to_string(width) + "x" + std::to_string(height));
}
}
void Renderer::set_config(const RendererConfig &config) {
bool size_changed = (config.width_ != config_.width_ || config.height_ != config_.height_);
config_ = config;
if (initialized_) {
if (size_changed) {
resize(config_.width_, config_.height_);
}
// Update ray tracer config
RayTracerConfig rt_config = raytracer_->get_config();
rt_config.samples_per_pixel_ = config_.samples_per_pixel_;
rt_config.max_depth_ = config_.max_ray_depth_;
rt_config.enable_accumulation_ = config_.enable_accumulation_;
raytracer_->set_config(rt_config);
}
}
} // namespace are

148
src/core/screen_blit.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "core/screen_blit.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
namespace {
const char* VERTEX_SHADER_SOURCE = R"(
#version 430 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texcoord;
out vec2 v_texcoord;
void main() {
v_texcoord = a_texcoord;
gl_Position = vec4(a_position, 0.0, 1.0);
}
)";
const char* FRAGMENT_SHADER_SOURCE = R"(
#version 430 core
in vec2 v_texcoord;
out vec4 frag_color;
uniform sampler2D u_texture;
void main() {
frag_color = texture(u_texture, v_texcoord);
}
)";
}
ScreenBlit::ScreenBlit()
: vao_(0)
, vbo_(0)
, initialized_(false) {
}
ScreenBlit::~ScreenBlit() {
release();
}
bool ScreenBlit::initialize() {
if (initialized_) {
Logger::warning("ScreenBlit already initialized");
return true;
}
// Compile shader
if (!shader_.compile(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) {
Logger::error("Failed to compile screen blit shader");
return false;
}
// Create fullscreen quad
create_quad_();
initialized_ = true;
Logger::info("ScreenBlit initialized successfully");
return true;
}
void ScreenBlit::release() {
if (!initialized_) return;
shader_.release();
if (vao_ != 0) {
glDeleteVertexArrays(1, &vao_);
vao_ = 0;
}
if (vbo_ != 0) {
glDeleteBuffers(1, &vbo_);
vbo_ = 0;
}
initialized_ = false;
}
void ScreenBlit::blit(TextureHandle texture, int x, int y, uint width, uint height) {
if (!initialized_) {
Logger::error("ScreenBlit not initialized");
return;
}
// Set viewport
glViewport(x, y, width, height);
// Disable depth test
glDisable(GL_DEPTH_TEST);
// Use shader
shader_.use();
shader_.set_int("u_texture", 0);
// Bind texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
// Draw quad
glBindVertexArray(vao_);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
// Re-enable depth test
glEnable(GL_DEPTH_TEST);
}
void ScreenBlit::blit_fullscreen(TextureHandle texture) {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
blit(texture, viewport[0], viewport[1], viewport[2], viewport[3]);
}
void ScreenBlit::create_quad_() {
// Fullscreen quad vertices (position + texcoord)
float vertices[] = {
// Position // TexCoord
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &vbo_);
glBindVertexArray(vao_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Position attribute
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
// TexCoord attribute
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glBindVertexArray(0);
}
} // namespace are

127
src/core/shader_manager.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "core/shader_manager.h"
#include "utils/logger.h"
namespace are {
ShaderManager::ShaderManager()
: initialized_(false) {
}
ShaderManager::~ShaderManager() {
release();
}
bool ShaderManager::initialize() {
if (initialized_) {
Logger::warning("ShaderManager already initialized");
return true;
}
Logger::info("Loading built-in shaders...");
if (!load_builtin_shaders_()) {
Logger::error("Failed to load built-in shaders");
return false;
}
initialized_ = true;
Logger::info("ShaderManager initialized successfully");
return true;
}
void ShaderManager::release() {
if (!initialized_) return;
gbuffer_shader_.release();
raytracing_shader_.release();
for (auto& pair : shader_cache_) {
pair.second.release();
}
shader_cache_.clear();
initialized_ = false;
Logger::info("ShaderManager released");
}
Shader ShaderManager::load_shader(const std::string& name,
const std::string& vertex_path,
const std::string& fragment_path) {
// Check cache
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
Logger::info("Shader '" + name + "' loaded from cache");
return it->second;
}
// Load shader
Shader shader;
if (!shader.load(vertex_path, fragment_path)) {
Logger::error("Failed to load shader '" + name + "'");
return Shader();
}
shader_cache_[name] = shader;
Logger::info("Shader '" + name + "' loaded successfully");
return shader;
}
Shader ShaderManager::load_compute_shader(const std::string& name,
const std::string& compute_path) {
// Check cache
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
Logger::info("Compute shader '" + name + "' loaded from cache");
return it->second;
}
// Load shader
Shader shader;
if (!shader.load_compute(compute_path)) {
Logger::error("Failed to load compute shader '" + name + "'");
return Shader();
}
shader_cache_[name] = shader;
Logger::info("Compute shader '" + name + "' loaded successfully");
return shader;
}
Shader ShaderManager::get_shader(const std::string& name) const {
auto it = shader_cache_.find(name);
if (it != shader_cache_.end()) {
return it->second;
}
Logger::warning("Shader '" + name + "' not found in cache");
return Shader();
}
bool ShaderManager::load_builtin_shaders_() {
// Load G-Buffer shader
if (!gbuffer_shader_.load("shaders/gbuffer.vert", "shaders/gbuffer.frag")) {
Logger::error("Failed to load G-Buffer shader");
return false;
}
shader_cache_["gbuffer"] = gbuffer_shader_;
// Load ray tracing compute shader
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
Logger::error("Failed to load ray tracing shader");
return false;
}
shader_cache_["raytracing"] = raytracing_shader_;
// Load ray tracing compute shader
Logger::info("Loading ray tracing compute shader...");
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
Logger::error("Failed to load ray tracing shader");
return false;
}
shader_cache_["raytracing"] = raytracing_shader_;
Logger::info("Ray tracing shader loaded successfully");
return true;
}
} // namespace are

114
src/resource/buffer.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "resource/buffer.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
namespace {
GLenum get_gl_buffer_type(BufferType type) {
switch (type) {
case BufferType::VERTEX_BUFFER: return GL_ARRAY_BUFFER;
case BufferType::INDEX_BUFFER: return GL_ELEMENT_ARRAY_BUFFER;
case BufferType::UNIFORM_BUFFER: return GL_UNIFORM_BUFFER;
case BufferType::SHADER_STORAGE_BUFFER: return GL_SHADER_STORAGE_BUFFER;
default: return GL_ARRAY_BUFFER;
}
}
GLenum get_gl_usage(BufferUsage usage) {
switch (usage) {
case BufferUsage::STATIC_DRAW: return GL_STATIC_DRAW;
case BufferUsage::DYNAMIC_DRAW: return GL_DYNAMIC_DRAW;
case BufferUsage::STREAM_DRAW: return GL_STREAM_DRAW;
default: return GL_STATIC_DRAW;
}
}
}
Buffer::Buffer()
: handle_(INVALID_HANDLE)
, type_(BufferType::VERTEX_BUFFER)
, size_(0)
, usage_(BufferUsage::STATIC_DRAW) {
}
Buffer::~Buffer() {
// Don't auto-release, let user control lifetime
}
bool Buffer::create(BufferType type, size_t size, const void* data, BufferUsage usage) {
if (handle_ != INVALID_HANDLE) {
Logger::warning("Buffer already created, releasing old buffer");
release();
}
type_ = type;
size_ = size;
usage_ = usage;
glGenBuffers(1, &handle_);
GLenum gl_type = get_gl_buffer_type(type);
GLenum gl_usage = get_gl_usage(usage);
glBindBuffer(gl_type, handle_);
glBufferData(gl_type, size, data, gl_usage);
glBindBuffer(gl_type, 0);
Logger::info("Buffer created successfully");
return true;
}
void Buffer::update(size_t offset, size_t size, const void* data) {
if (handle_ == INVALID_HANDLE) {
Logger::error("Cannot update invalid buffer");
return;
}
if (offset + size > size_) {
Logger::error("Buffer update out of bounds");
return;
}
GLenum gl_type = get_gl_buffer_type(type_);
glBindBuffer(gl_type, handle_);
glBufferSubData(gl_type, offset, size, data);
glBindBuffer(gl_type, 0);
}
void Buffer::bind() const {
if (handle_ == INVALID_HANDLE) {
Logger::warning("Attempting to bind invalid buffer");
return;
}
GLenum gl_type = get_gl_buffer_type(type_);
glBindBuffer(gl_type, handle_);
}
void Buffer::bind_base(uint binding_point) const {
if (handle_ == INVALID_HANDLE) {
Logger::warning("Attempting to bind invalid buffer");
return;
}
GLenum gl_type = get_gl_buffer_type(type_);
glBindBufferBase(gl_type, binding_point, handle_);
}
void Buffer::unbind() const {
GLenum gl_type = get_gl_buffer_type(type_);
glBindBuffer(gl_type, 0);
}
void Buffer::release() {
if (handle_ != INVALID_HANDLE) {
glDeleteBuffers(1, &handle_);
handle_ = INVALID_HANDLE;
}
size_ = 0;
}
} // namespace are

View File

@ -0,0 +1,187 @@
#include "resource/model_loader.h"
#include "utils/logger.h"
#include "resource/texture.h"
// Note: This is a simplified implementation without Assimp
// For full implementation, include Assimp and implement properly
namespace are {
bool ModelLoader::load(const std::string& path,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
bool flip_uvs) {
Logger::error("ModelLoader requires Assimp library (not implemented in this version)");
Logger::info("To implement: include <assimp/Importer.hpp>, <assimp/scene.h>, <assimp/postprocess.h>");
// Placeholder implementation
// TODO: Implement with Assimp
/*
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path,
aiProcess_Triangulate |
aiProcess_GenNormals |
aiProcess_CalcTangentSpace |
(flip_uvs ? aiProcess_FlipUVs : 0));
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
Logger::error("Failed to load model: " + std::string(importer.GetErrorString()));
return false;
}
std::string directory = path.substr(0, path.find_last_of('/'));
process_node_(scene->mRootNode, scene, meshes, materials, directory);
Logger::info("Model loaded: " + path);
return true;
*/
return false;
}
bool ModelLoader::load_and_upload(const std::string& path,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
bool flip_uvs) {
if (!load(path, meshes, materials, flip_uvs)) {
return false;
}
// Upload all meshes to GPU
for (auto& mesh : meshes) {
if (!mesh->upload_to_gpu()) {
Logger::error("Failed to upload mesh to GPU");
return false;
}
}
return true;
}
void ModelLoader::process_node_(void* node, void* scene,
std::vector<std::shared_ptr<Mesh>>& meshes,
std::vector<std::shared_ptr<Material>>& materials,
const std::string& directory) {
// TODO: Implement with Assimp
/*
aiNode* ai_node = static_cast<aiNode*>(node);
const aiScene* ai_scene = static_cast<const aiScene*>(scene);
// Process all meshes in this node
for (uint i = 0; i < ai_node->mNumMeshes; ++i) {
aiMesh* ai_mesh = ai_scene->mMeshes[ai_node->mMeshes[i]];
meshes.push_back(process_mesh_(ai_mesh, ai_scene, materials, directory));
}
// Process children recursively
for (uint i = 0; i < ai_node->mNumChildren; ++i) {
process_node_(ai_node->mChildren[i], ai_scene, meshes, materials, directory);
}
*/
}
std::shared_ptr<Mesh> ModelLoader::process_mesh_(void* mesh, void* scene,
std::vector<std::shared_ptr<Material>>& materials,
const std::string& directory) {
// TODO: Implement with Assimp
/*
aiMesh* ai_mesh = static_cast<aiMesh*>(mesh);
const aiScene* ai_scene = static_cast<const aiScene*>(scene);
std::vector<Vertex> vertices;
std::vector<uint> indices;
// Process vertices
for (uint i = 0; i < ai_mesh->mNumVertices; ++i) {
Vertex vertex;
vertex.position_ = Vec3(ai_mesh->mVertices[i].x,
ai_mesh->mVertices[i].y,
ai_mesh->mVertices[i].z);
if (ai_mesh->HasNormals()) {
vertex.normal_ = Vec3(ai_mesh->mNormals[i].x,
ai_mesh->mNormals[i].y,
ai_mesh->mNormals[i].z);
}
if (ai_mesh->mTextureCoords[0]) {
vertex.texcoord_ = Vec2(ai_mesh->mTextureCoords[0][i].x,
ai_mesh->mTextureCoords[0][i].y);
}
if (ai_mesh->HasTangentsAndBitangents()) {
vertex.tangent_ = Vec3(ai_mesh->mTangents[i].x,
ai_mesh->mTangents[i].y,
ai_mesh->mTangents[i].z);
}
vertices.push_back(vertex);
}
// Process indices
for (uint i = 0; i < ai_mesh->mNumFaces; ++i) {
aiFace face = ai_mesh->mFaces[i];
for (uint j = 0; j < face.mNumIndices; ++j) {
indices.push_back(face.mIndices[j]);
}
}
// Process material
uint material_id = materials.size();
if (ai_mesh->mMaterialIndex >= 0) {
aiMaterial* ai_material = ai_scene->mMaterials[ai_mesh->mMaterialIndex];
auto material = std::make_shared<Material>();
// Load diffuse color
aiColor3D color;
if (ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
material->set_albedo(Vec3(color.r, color.g, color.b));
}
// Load textures
load_material_textures_(ai_material, aiTextureType_DIFFUSE, material, directory);
load_material_textures_(ai_material, aiTextureType_NORMALS, material, directory);
materials.push_back(material);
}
auto mesh_obj = std::make_shared<Mesh>();
mesh_obj->set_vertices(vertices);
mesh_obj->set_indices(indices);
mesh_obj->set_material(material_id);
return mesh_obj;
*/
return nullptr;
}
void ModelLoader::load_material_textures_(void* material, int type,
std::shared_ptr<Material>& mat,
const std::string& directory) {
// TODO: Implement with Assimp
/*
aiMaterial* ai_material = static_cast<aiMaterial*>(material);
aiTextureType ai_type = static_cast<aiTextureType>(type);
for (uint i = 0; i < ai_material->GetTextureCount(ai_type); ++i) {
aiString str;
ai_material->GetTexture(ai_type, i, &str);
std::string filename = directory + "/" + std::string(str.C_Str());
auto texture = std::make_shared<Texture>();
if (texture->load_from_file(filename)) {
if (ai_type == aiTextureType_DIFFUSE) {
mat->set_albedo_texture(texture);
} else if (ai_type == aiTextureType_NORMALS) {
mat->set_normal_texture(texture);
}
}
}
*/
}
} // namespace are

197
src/resource/shader.cpp Normal file
View File

@ -0,0 +1,197 @@
#include "resource/shader.h"
#include "utils/logger.h"
#include "basic/math.h" // 修改为math.h
#include <glad/glad.h>
#include <fstream>
#include <sstream>
namespace are {
Shader::Shader()
: handle_(INVALID_HANDLE) {
}
Shader::~Shader() {
// Don't auto-release, let user control lifetime
}
bool Shader::load(const std::string& vertex_path, const std::string& fragment_path) {
std::string vertex_source = read_file_(vertex_path);
std::string fragment_source = read_file_(fragment_path);
if (vertex_source.empty() || fragment_source.empty()) {
Logger::error("Failed to read shader files");
return false;
}
return compile(vertex_source, fragment_source);
}
bool Shader::load_compute(const std::string& compute_path) {
std::string compute_source = read_file_(compute_path);
if (compute_source.empty()) {
Logger::error("Failed to read compute shader file");
return false;
}
return compile_compute(compute_source);
}
bool Shader::compile(const std::string& vertex_source, const std::string& fragment_source) {
uint vertex_shader = compile_shader_(vertex_source, GL_VERTEX_SHADER);
if (vertex_shader == 0) return false;
uint fragment_shader = compile_shader_(fragment_source, GL_FRAGMENT_SHADER);
if (fragment_shader == 0) {
glDeleteShader(vertex_shader);
return false;
}
uint shaders[] = { vertex_shader, fragment_shader };
bool success = link_program_(shaders, 2);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return success;
}
bool Shader::compile_compute(const std::string& compute_source) {
uint compute_shader = compile_shader_(compute_source, GL_COMPUTE_SHADER);
if (compute_shader == 0) return false;
uint shaders[] = { compute_shader };
bool success = link_program_(shaders, 1);
glDeleteShader(compute_shader);
return success;
}
void Shader::use() const { // 改为const
if (handle_ != INVALID_HANDLE) {
glUseProgram(handle_);
}
}
void Shader::release() {
if (handle_ != INVALID_HANDLE) {
glDeleteProgram(handle_);
handle_ = INVALID_HANDLE;
}
uniform_cache_.clear();
}
void Shader::set_bool(const std::string& name, bool value) const { // 新增
glUniform1i(get_uniform_location_(name), static_cast<int>(value));
}
void Shader::set_int(const std::string& name, int value) const { // 改为const
glUniform1i(get_uniform_location_(name), value);
}
void Shader::set_uint(const std::string& name, uint value) const { // 改为const
glUniform1ui(get_uniform_location_(name), value);
}
void Shader::set_float(const std::string& name, float value) const { // 改为const
glUniform1f(get_uniform_location_(name), value);
}
void Shader::set_vec2(const std::string& name, const Vec2& value) const { // 改为const
glUniform2fv(get_uniform_location_(name), 1, &value[0]);
}
void Shader::set_vec3(const std::string& name, const Vec3& value) const { // 改为const
glUniform3fv(get_uniform_location_(name), 1, &value[0]);
}
void Shader::set_vec4(const std::string& name, const Vec4& value) const { // 改为const
glUniform4fv(get_uniform_location_(name), 1, &value[0]);
}
void Shader::set_mat3(const std::string& name, const Mat3& value) const { // 改为const
glUniformMatrix3fv(get_uniform_location_(name), 1, GL_FALSE, &value[0][0]);
}
void Shader::set_mat4(const std::string& name, const Mat4& value) const { // 改为const
glUniformMatrix4fv(get_uniform_location_(name), 1, GL_FALSE, MathUtils::value_ptr(value));
}
int Shader::get_uniform_location_(const std::string& name) const { // 改为const
auto it = uniform_cache_.find(name);
if (it != uniform_cache_.end()) {
return it->second;
}
int location = glGetUniformLocation(handle_, name.c_str());
uniform_cache_[name] = location; // mutable允许修改
if (location == -1) {
Logger::warning("Uniform '" + name + "' not found in shader");
}
return location;
}
uint Shader::compile_shader_(const std::string& source, uint type) {
uint shader = glCreateShader(type);
const char* source_cstr = source.c_str();
glShaderSource(shader, 1, &source_cstr, nullptr);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char info_log[512];
glGetShaderInfoLog(shader, 512, nullptr, info_log);
std::string type_str = (type == GL_VERTEX_SHADER) ? "VERTEX" :
(type == GL_FRAGMENT_SHADER) ? "FRAGMENT" : "COMPUTE";
Logger::error("Shader compilation failed (" + type_str + "): " + std::string(info_log));
glDeleteShader(shader);
return 0;
}
return shader;
}
bool Shader::link_program_(const uint* shaders, uint count) {
handle_ = glCreateProgram();
for (uint i = 0; i < count; ++i) {
glAttachShader(handle_, shaders[i]);
}
glLinkProgram(handle_);
int success;
glGetProgramiv(handle_, GL_LINK_STATUS, &success);
if (!success) {
char info_log[512];
glGetProgramInfoLog(handle_, 512, nullptr, info_log);
Logger::error("Shader linking failed: " + std::string(info_log));
glDeleteProgram(handle_);
handle_ = INVALID_HANDLE;
return false;
}
return true;
}
std::string Shader::read_file_(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
Logger::error("Failed to open file: " + path);
return "";
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
} // namespace are

274
src/resource/texture.cpp Normal file
View File

@ -0,0 +1,274 @@
#include "resource/texture.h"
#include "utils/logger.h"
#include <glad/glad.h>
#include <stb_image.h>
namespace are {
namespace {
GLenum get_gl_internal_format(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return GL_R8;
case TextureFormat::RG8: return GL_RG8;
case TextureFormat::RGB8: return GL_RGB8;
case TextureFormat::RGBA8: return GL_RGBA8;
case TextureFormat::R16F: return GL_R16F;
case TextureFormat::RG16F: return GL_RG16F;
case TextureFormat::RGB16F: return GL_RGB16F;
case TextureFormat::RGBA16F: return GL_RGBA16F;
case TextureFormat::R32F: return GL_R32F;
case TextureFormat::RG32F: return GL_RG32F;
case TextureFormat::RGB32F: return GL_RGB32F;
case TextureFormat::RGBA32F: return GL_RGBA32F;
case TextureFormat::DEPTH24_STENCIL8: return GL_DEPTH24_STENCIL8;
default: return GL_RGBA8;
}
}
GLenum get_gl_format(TextureFormat format) {
switch (format) {
case TextureFormat::R8:
case TextureFormat::R16F:
case TextureFormat::R32F:
return GL_RED;
case TextureFormat::RG8:
case TextureFormat::RG16F:
case TextureFormat::RG32F:
return GL_RG;
case TextureFormat::RGB8:
case TextureFormat::RGB16F:
case TextureFormat::RGB32F:
return GL_RGB;
case TextureFormat::RGBA8:
case TextureFormat::RGBA16F:
case TextureFormat::RGBA32F:
return GL_RGBA;
case TextureFormat::DEPTH24_STENCIL8:
return GL_DEPTH_STENCIL;
default:
return GL_RGBA;
}
}
GLenum get_gl_type(TextureFormat format) {
switch (format) {
case TextureFormat::R8:
case TextureFormat::RG8:
case TextureFormat::RGB8:
case TextureFormat::RGBA8:
return GL_UNSIGNED_BYTE;
case TextureFormat::R16F:
case TextureFormat::RG16F:
case TextureFormat::RGB16F:
case TextureFormat::RGBA16F:
case TextureFormat::R32F:
case TextureFormat::RG32F:
case TextureFormat::RGB32F:
case TextureFormat::RGBA32F:
return GL_FLOAT;
case TextureFormat::DEPTH24_STENCIL8:
return GL_UNSIGNED_INT_24_8;
default:
return GL_UNSIGNED_BYTE;
}
}
GLenum get_gl_filter(TextureFilter filter) {
switch (filter) {
case TextureFilter::NEAREST: return GL_NEAREST;
case TextureFilter::LINEAR: return GL_LINEAR;
case TextureFilter::NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
case TextureFilter::LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
case TextureFilter::NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
case TextureFilter::LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
default: return GL_LINEAR;
}
}
GLenum get_gl_wrap(TextureWrap wrap) {
switch (wrap) {
case TextureWrap::REPEAT: return GL_REPEAT;
case TextureWrap::MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
case TextureWrap::CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
case TextureWrap::CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER;
default: return GL_REPEAT;
}
}
}
Texture::Texture()
: handle_(INVALID_HANDLE)
, width_(0)
, height_(0)
, format_(TextureFormat::RGBA8)
, has_mipmaps_(false) {
}
Texture::~Texture() {
// Don't auto-release, let user control lifetime
}
bool Texture::load_from_file(const std::string& path, bool generate_mipmaps) {
// Load image using stb_image
int width, height, channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
if (!data) {
Logger::error("Failed to load texture: " + path);
return false;
}
// Determine format based on channels
TextureFormat format;
switch (channels) {
case 1: format = TextureFormat::R8; break;
case 2: format = TextureFormat::RG8; break;
case 3: format = TextureFormat::RGB8; break;
case 4: format = TextureFormat::RGBA8; break;
default:
Logger::error("Unsupported channel count: " + std::to_string(channels));
stbi_image_free(data);
return false;
}
// Create texture
bool success = create(width, height, format);
if (!success) {
stbi_image_free(data);
return false;
}
// Upload data
success = upload(data, width, height, format);
stbi_image_free(data);
if (!success) {
return false;
}
// Generate mipmaps if requested
if (generate_mipmaps) {
this->generate_mipmaps();
}
Logger::info("Texture loaded successfully: " + path);
return true;
}
bool Texture::create(uint width, uint height, TextureFormat format) {
if (handle_ != INVALID_HANDLE) {
Logger::warning("Texture already created, releasing old texture");
release();
}
width_ = width;
height_ = height;
format_ = format;
glGenTextures(1, &handle_);
glBindTexture(GL_TEXTURE_2D, handle_);
GLenum internal_format = get_gl_internal_format(format);
GLenum gl_format = get_gl_format(format);
GLenum type = get_gl_type(format);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, gl_format, type, nullptr);
// Set default parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
bool Texture::upload(const void* data, uint width, uint height, TextureFormat format) {
if (handle_ == INVALID_HANDLE) {
Logger::error("Cannot upload to invalid texture");
return false;
}
if (width != width_ || height != height_ || format != format_) {
Logger::warning("Upload parameters differ from texture creation, recreating texture");
create(width, height, format);
}
glBindTexture(GL_TEXTURE_2D, handle_);
GLenum gl_format = get_gl_format(format);
GLenum type = get_gl_type(format);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl_format, type, data);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void Texture::set_filter(TextureFilter min_filter, TextureFilter mag_filter) {
if (handle_ == INVALID_HANDLE) {
Logger::error("Cannot set filter on invalid texture");
return;
}
glBindTexture(GL_TEXTURE_2D, handle_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_gl_filter(min_filter));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_gl_filter(mag_filter));
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::set_wrap(TextureWrap wrap_s, TextureWrap wrap_t) {
if (handle_ == INVALID_HANDLE) {
Logger::error("Cannot set wrap mode on invalid texture");
return;
}
glBindTexture(GL_TEXTURE_2D, handle_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, get_gl_wrap(wrap_s));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, get_gl_wrap(wrap_t));
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::generate_mipmaps() {
if (handle_ == INVALID_HANDLE) {
Logger::error("Cannot generate mipmaps for invalid texture");
return;
}
glBindTexture(GL_TEXTURE_2D, handle_);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
has_mipmaps_ = true;
}
void Texture::bind(uint unit) const {
if (handle_ == INVALID_HANDLE) {
Logger::warning("Attempting to bind invalid texture");
return;
}
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, handle_);
}
void Texture::unbind() const {
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::release() {
if (handle_ != INVALID_HANDLE) {
glDeleteTextures(1, &handle_);
handle_ = INVALID_HANDLE;
}
width_ = 0;
height_ = 0;
has_mipmaps_ = false;
}
} // namespace are

101
src/scene/camera.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "scene/camera.h"
#include "basic/math.h"
#include <glm/gtc/matrix_transform.hpp>
namespace are {
Camera::Camera()
: position_(0.0f, 0.0f, 5.0f)
, target_(0.0f, 0.0f, 0.0f)
, up_(0.0f, 1.0f, 0.0f)
, projection_type_(ProjectionType::PERSPECTIVE)
, fov_(glm::radians(45.0f))
, aspect_(16.0f / 9.0f)
, left_(-1.0f)
, right_(1.0f)
, bottom_(-1.0f)
, top_(1.0f)
, near_(0.1f)
, far_(100.0f)
, view_dirty_(true)
, projection_dirty_(true) {
}
Camera::~Camera() {
}
void Camera::set_perspective(float fov, float aspect, float near, float far) {
projection_type_ = ProjectionType::PERSPECTIVE;
fov_ = glm::radians(fov);
aspect_ = aspect;
near_ = near;
far_ = far;
projection_dirty_ = true;
}
void Camera::set_orthographic(float left, float right, float bottom, float top, float near, float far) {
projection_type_ = ProjectionType::ORTHOGRAPHIC;
left_ = left;
right_ = right;
bottom_ = bottom;
top_ = top;
near_ = near;
far_ = far;
projection_dirty_ = true;
}
void Camera::set_position(const Vec3& position) {
position_ = position;
view_dirty_ = true;
}
void Camera::set_target(const Vec3& target) {
target_ = target;
view_dirty_ = true;
}
void Camera::set_up(const Vec3& up) {
up_ = up;
view_dirty_ = true;
}
Mat4 Camera::get_view_matrix() const {
if (view_dirty_) {
view_matrix_ = MathUtils::look_at(position_, target_, up_);
view_dirty_ = false;
}
return view_matrix_;
}
Mat4 Camera::get_projection_matrix() const {
if (projection_dirty_) {
if (projection_type_ == ProjectionType::PERSPECTIVE) {
projection_matrix_ = MathUtils::perspective(fov_, aspect_, near_, far_);
} else {
projection_matrix_ = glm::ortho(left_, right_, bottom_, top_, near_, far_);
}
projection_dirty_ = false;
}
return projection_matrix_;
}
Mat4 Camera::get_view_projection_matrix() const {
return get_projection_matrix() * get_view_matrix();
}
Vec3 Camera::get_forward() const {
return MathUtils::normalize(target_ - position_);
}
Vec3 Camera::get_right() const {
Vec3 forward = get_forward();
return MathUtils::normalize(MathUtils::cross(forward, up_));
}
Vec3 Camera::get_up() const {
Vec3 forward = get_forward();
Vec3 right = get_right();
return MathUtils::cross(right, forward);
}
} // namespace are

49
src/scene/light.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "scene/light.h"
#include <glm/gtc/constants.hpp>
namespace are {
Light::Light()
: type_(LightType::POINT)
, position_(0.0f, 5.0f, 0.0f)
, direction_(0.0f, -1.0f, 0.0f)
, color_(1.0f, 1.0f, 1.0f)
, intensity_(1.0f)
, range_(10.0f)
, inner_angle_(glm::radians(30.0f))
, outer_angle_(glm::radians(45.0f)) {
}
Light::~Light() {
}
void Light::set_type(LightType type) {
type_ = type;
}
void Light::set_position(const Vec3& position) {
position_ = position;
}
void Light::set_direction(const Vec3& direction) {
direction_ = glm::normalize(direction);
}
void Light::set_color(const Vec3& color) {
color_ = color;
}
void Light::set_intensity(float intensity) {
intensity_ = intensity;
}
void Light::set_range(float range) {
range_ = range;
}
void Light::set_spot_angles(float inner_angle, float outer_angle) {
inner_angle_ = glm::radians(inner_angle);
outer_angle_ = glm::radians(outer_angle);
}
} // namespace are

51
src/scene/material.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "scene/material.h"
namespace are {
Material::Material()
: albedo_(1.0f, 1.0f, 1.0f)
, emission_(0.0f, 0.0f, 0.0f)
, metallic_(0.0f)
, roughness_(0.5f)
, ior_(1.5f)
, type_(MaterialType::DIFFUSE)
, albedo_texture_(nullptr)
, normal_texture_(nullptr) {
}
Material::~Material() {
}
void Material::set_albedo(const Vec3& albedo) {
albedo_ = albedo;
}
void Material::set_emission(const Vec3& emission) {
emission_ = emission;
}
void Material::set_metallic(float metallic) {
metallic_ = glm::clamp(metallic, 0.0f, 1.0f);
}
void Material::set_roughness(float roughness) {
roughness_ = glm::clamp(roughness, 0.0f, 1.0f);
}
void Material::set_ior(float ior) {
ior_ = ior;
}
void Material::set_type(MaterialType type) {
type_ = type;
}
void Material::set_albedo_texture(std::shared_ptr<Texture> texture) {
albedo_texture_ = texture;
}
void Material::set_normal_texture(std::shared_ptr<Texture> texture) {
normal_texture_ = texture;
}
} // namespace are

119
src/scene/mesh.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "scene/mesh.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
Mesh::Mesh()
: material_id_(0)
, transform_(1.0f)
, vao_(0)
, vbo_(0)
, ebo_(0)
, uploaded_(false) {
}
Mesh::~Mesh() {
release_gpu_resources();
}
void Mesh::set_vertices(const std::vector<Vertex>& vertices) {
vertices_ = vertices;
uploaded_ = false;
}
void Mesh::set_indices(const std::vector<uint>& indices) {
indices_ = indices;
uploaded_ = false;
}
void Mesh::set_material(uint material_id) {
material_id_ = material_id;
}
void Mesh::set_transform(const Mat4& transform) {
transform_ = transform;
}
bool Mesh::upload_to_gpu() {
if (uploaded_) {
Logger::warning("Mesh already uploaded to GPU");
return true;
}
if (vertices_.empty()) {
Logger::error("Cannot upload mesh: no vertices");
return false;
}
if (indices_.empty()) {
Logger::error("Cannot upload mesh: no indices");
return false;
}
// Generate VAO
glGenVertexArrays(1, &vao_);
glBindVertexArray(vao_);
// Generate and upload VBO
glGenBuffers(1, &vbo_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(Vertex),
vertices_.data(), GL_STATIC_DRAW);
// Generate and upload EBO
glGenBuffers(1, &ebo_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(uint),
indices_.data(), GL_STATIC_DRAW);
// Set vertex attributes
// Location 0: Position
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, position_));
// Location 1: Normal
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, normal_));
// Location 2: TexCoord
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, texcoord_));
// Location 3: Tangent
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, tangent_));
glBindVertexArray(0);
uploaded_ = true;
Logger::info("Mesh uploaded to GPU successfully");
return true;
}
void Mesh::release_gpu_resources() {
if (!uploaded_) return;
if (vao_ != 0) {
glDeleteVertexArrays(1, &vao_);
vao_ = 0;
}
if (vbo_ != 0) {
glDeleteBuffers(1, &vbo_);
vbo_ = 0;
}
if (ebo_ != 0) {
glDeleteBuffers(1, &ebo_);
ebo_ = 0;
}
uploaded_ = false;
}
} // namespace are

44
src/scene/scene.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "scene/scene.h"
namespace are {
Scene::Scene() {
// Create default camera
camera_ = std::make_shared<Camera>();
}
Scene::~Scene() {
clear();
}
uint Scene::add_mesh(std::shared_ptr<Mesh> mesh) {
meshes_.push_back(mesh);
return static_cast<uint>(meshes_.size() - 1);
}
uint Scene::add_material(std::shared_ptr<Material> material) {
materials_.push_back(material);
return static_cast<uint>(materials_.size() - 1);
}
uint Scene::add_light(std::shared_ptr<Light> light) {
lights_.push_back(light);
return static_cast<uint>(lights_.size() - 1);
}
void Scene::set_camera(std::shared_ptr<Camera> camera) {
camera_ = camera;
}
void Scene::clear() {
meshes_.clear();
materials_.clear();
lights_.clear();
}
void Scene::update(float delta_time) {
// Reserved for future animation/physics updates
(void)delta_time; // Suppress unused parameter warning
}
} // namespace are

144
src/utils/config.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "utils/config.h"
#include "utils/logger.h"
#include <fstream>
#include <sstream>
#include <algorithm>
namespace are {
// Static storage
static std::unordered_map<std::string, std::string> g_config_map;
// Helper function to trim whitespace
static std::string trim(const std::string& str) {
size_t first = str.find_first_not_of(" \t\r\n");
if (first == std::string::npos) return "";
size_t last = str.find_last_not_of(" \t\r\n");
return str.substr(first, last - first + 1);
}
bool Config::load(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
Logger::error("Failed to open config file: " + path);
return false;
}
g_config_map.clear();
std::string line;
std::string current_section;
while (std::getline(file, line)) {
line = trim(line);
// Skip empty lines and comments
if (line.empty() || line[0] == '#' || line[0] == ';') {
continue;
}
// Section header
if (line[0] == '[' && line.back() == ']') {
current_section = line.substr(1, line.length() - 2);
continue;
}
// Key-value pair
size_t pos = line.find('=');
if (pos != std::string::npos) {
std::string key = trim(line.substr(0, pos));
std::string value = trim(line.substr(pos + 1));
// Add section prefix if in a section
if (!current_section.empty()) {
key = current_section + "." + key;
}
g_config_map[key] = value;
}
}
Logger::info("Config loaded: " + path + " (" + std::to_string(g_config_map.size()) + " entries)");
return true;
}
bool Config::save(const std::string& path) {
std::ofstream file(path);
if (!file.is_open()) {
Logger::error("Failed to open config file for writing: " + path);
return false;
}
for (const auto& pair : g_config_map) {
file << pair.first << "=" << pair.second << std::endl;
}
Logger::info("Config saved: " + path);
return true;
}
std::string Config::get_string(const std::string& key, const std::string& default_value) {
auto it = g_config_map.find(key);
if (it != g_config_map.end()) {
return it->second;
}
return default_value;
}
int Config::get_int(const std::string& key, int default_value) {
auto it = g_config_map.find(key);
if (it != g_config_map.end()) {
try {
return std::stoi(it->second);
} catch (...) {
Logger::warning("Failed to parse int for key: " + key);
}
}
return default_value;
}
float Config::get_float(const std::string& key, float default_value) {
auto it = g_config_map.find(key);
if (it != g_config_map.end()) {
try {
return std::stof(it->second);
} catch (...) {
Logger::warning("Failed to parse float for key: " + key);
}
}
return default_value;
}
bool Config::get_bool(const std::string& key, bool default_value) {
auto it = g_config_map.find(key);
if (it != g_config_map.end()) {
std::string value = it->second;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if (value == "true" || value == "1" || value == "yes" || value == "on") {
return true;
}
if (value == "false" || value == "0" || value == "no" || value == "off") {
return false;
}
}
return default_value;
}
void Config::set_string(const std::string& key, const std::string& value) {
g_config_map[key] = value;
}
void Config::set_int(const std::string& key, int value) {
g_config_map[key] = std::to_string(value);
}
void Config::set_float(const std::string& key, float value) {
g_config_map[key] = std::to_string(value);
}
void Config::set_bool(const std::string& key, bool value) {
g_config_map[key] = value ? "true" : "false";
}
} // namespace are

103
src/utils/logger.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "utils/logger.h"
#include <iostream>
#include <fstream>
#include <ctime>
#include <iomanip>
#include <sstream>
namespace are {
// Static members
static LogLevel g_min_level = LogLevel::DEBUG;
static std::ofstream g_log_file;
static bool g_initialized = false;
bool Logger::initialize(const std::string& log_file) {
if (g_initialized) {
return true;
}
if (!log_file.empty()) {
g_log_file.open(log_file, std::ios::out | std::ios::app);
if (!g_log_file.is_open()) {
std::cerr << "Failed to open log file: " << log_file << std::endl;
return false;
}
}
g_initialized = true;
return true;
}
void Logger::shutdown() {
if (g_log_file.is_open()) {
g_log_file.close();
}
g_initialized = false;
}
static std::string get_current_time() {
auto now = std::time(nullptr);
auto tm = *std::localtime(&now);
std::ostringstream oss;
oss << std::put_time(&tm, "%H:%M:%S");
return oss.str();
}
static std::string level_to_string(LogLevel level) {
switch (level) {
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARN";
case LogLevel::ERROR: return "ERROR";
case LogLevel::FATAL: return "FATAL";
default: return "UNKNOWN";
}
}
void Logger::log(LogLevel level, const std::string& message) {
if (level < g_min_level) return;
std::string time_str = get_current_time();
std::string level_str = level_to_string(level);
std::string formatted = "[" + time_str + "] [" + level_str + "] " + message;
// Console output
if (level >= LogLevel::ERROR) {
std::cerr << formatted << std::endl;
} else {
std::cout << formatted << std::endl;
}
// File output
if (g_log_file.is_open()) {
g_log_file << formatted << std::endl;
g_log_file.flush();
}
}
void Logger::debug(const std::string& message) {
log(LogLevel::DEBUG, message);
}
void Logger::info(const std::string& message) {
log(LogLevel::INFO, message);
}
void Logger::warning(const std::string& message) {
log(LogLevel::WARNING, message);
}
void Logger::error(const std::string& message) {
log(LogLevel::ERROR, message);
}
void Logger::fatal(const std::string& message) {
log(LogLevel::FATAL, message);
}
void Logger::set_level(LogLevel level) {
g_min_level = level;
}
} // namespace are