Refactor:重写所有代码、重新设计架构,实现大体架构和G-Buffer渲染
parent
136677c456
commit
dbf5b8579c
|
|
@ -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}")
|
||||
Binary file not shown.
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
core
|
||||
scene
|
||||
resource
|
||||
utils
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 "=========================================="
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
// }
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue