Initial structure.
parent
96ffcd4edc
commit
136677c456
243
CMakeLists.txt
243
CMakeLists.txt
|
|
@ -1,243 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.15)
|
|
||||||
project(AuroraRenderingEngine VERSION 0.1.0 LANGUAGES CXX C)
|
|
||||||
|
|
||||||
# Set C++ standard
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
||||||
|
|
||||||
# Set C standard for GLAD
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
# Build options
|
|
||||||
option(ARE_BUILD_SHARED "Build shared library" OFF)
|
|
||||||
option(ARE_BUILD_EXAMPLES "Build example programs" ON)
|
|
||||||
option(ARE_ENABLE_PROFILING "Enable performance profiling" ON)
|
|
||||||
option(ARE_ENABLE_DEBUG_VIS "Enable debug visualization" ON)
|
|
||||||
|
|
||||||
# Set output directories
|
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
||||||
|
|
||||||
# Compiler flags
|
|
||||||
if(MSVC)
|
|
||||||
add_compile_options(/W4)
|
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
|
||||||
# Disable specific warnings that are too strict
|
|
||||||
add_compile_options(/wd4100) # unreferenced formal parameter
|
|
||||||
else()
|
|
||||||
add_compile_options(-Wall -Wextra -pedantic)
|
|
||||||
# Disable specific warnings
|
|
||||||
add_compile_options(-Wno-unused-parameter)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Find required packages
|
|
||||||
find_package(OpenGL REQUIRED)
|
|
||||||
find_package(glfw3 REQUIRED)
|
|
||||||
find_package(glm REQUIRED)
|
|
||||||
|
|
||||||
# Try to find spdlog (system installation)
|
|
||||||
find_package(spdlog QUIET)
|
|
||||||
|
|
||||||
if(NOT spdlog_FOUND)
|
|
||||||
message(STATUS "spdlog not found in system, using local version")
|
|
||||||
# Use local spdlog
|
|
||||||
set(SPDLOG_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/spdlog/include")
|
|
||||||
if(NOT EXISTS ${SPDLOG_INCLUDE_DIR})
|
|
||||||
message(FATAL_ERROR "spdlog not found. Please install spdlog or place it in lib/spdlog/")
|
|
||||||
endif()
|
|
||||||
add_library(spdlog INTERFACE)
|
|
||||||
target_include_directories(spdlog INTERFACE ${SPDLOG_INCLUDE_DIR})
|
|
||||||
# spdlog compile definitions
|
|
||||||
target_compile_definitions(spdlog INTERFACE SPDLOG_COMPILED_LIB)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Find OpenMP (optional)
|
|
||||||
find_package(OpenMP)
|
|
||||||
if(OpenMP_CXX_FOUND)
|
|
||||||
set(ARE_USE_OPENMP ON)
|
|
||||||
add_compile_definitions(ARE_USE_OPENMP)
|
|
||||||
message(STATUS "OpenMP found and enabled")
|
|
||||||
else()
|
|
||||||
message(STATUS "OpenMP not found, multithreading will use std::thread")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# GLAD library path
|
|
||||||
set(GLAD_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/glad")
|
|
||||||
set(GLAD_SOURCE_DIR "${CMAKE_SOURCE_DIR}/lib/glad")
|
|
||||||
|
|
||||||
if(NOT EXISTS ${GLAD_INCLUDE_DIR})
|
|
||||||
message(FATAL_ERROR "GLAD include directory not found: ${GLAD_INCLUDE_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS ${GLAD_SOURCE_DIR})
|
|
||||||
message(FATAL_ERROR "GLAD source directory not found: ${GLAD_SOURCE_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# STB library path
|
|
||||||
set(STB_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/stb")
|
|
||||||
|
|
||||||
if(NOT EXISTS ${STB_INCLUDE_DIR})
|
|
||||||
message(FATAL_ERROR "STB include directory not found: ${STB_INCLUDE_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Collect all source files from src/
|
|
||||||
file(GLOB_RECURSE ARE_SOURCES
|
|
||||||
"${CMAKE_SOURCE_DIR}/src/*.cpp"
|
|
||||||
"${CMAKE_SOURCE_DIR}/src/*.c"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Collect all GLAD source files
|
|
||||||
file(GLOB GLAD_SOURCES
|
|
||||||
"${GLAD_SOURCE_DIR}/*.c"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add GLAD sources to ARE sources
|
|
||||||
list(APPEND ARE_SOURCES ${GLAD_SOURCES})
|
|
||||||
|
|
||||||
# Collect all header files
|
|
||||||
file(GLOB_RECURSE ARE_HEADERS
|
|
||||||
"${CMAKE_SOURCE_DIR}/include/*.h"
|
|
||||||
"${CMAKE_SOURCE_DIR}/include/*.hpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create library
|
|
||||||
if(ARE_BUILD_SHARED)
|
|
||||||
add_library(are SHARED ${ARE_SOURCES} ${ARE_HEADERS})
|
|
||||||
target_compile_definitions(are PRIVATE ARE_BUILD_SHARED)
|
|
||||||
message(STATUS "Building shared library")
|
|
||||||
else()
|
|
||||||
add_library(are STATIC ${ARE_SOURCES} ${ARE_HEADERS})
|
|
||||||
message(STATUS "Building static library")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set target properties
|
|
||||||
set_target_properties(are PROPERTIES
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
|
||||||
PUBLIC_HEADER "${ARE_HEADERS}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Include directories
|
|
||||||
target_include_directories(are
|
|
||||||
PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
|
|
||||||
$<INSTALL_INTERFACE:include>
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_SOURCE_DIR}/src
|
|
||||||
${GLAD_INCLUDE_DIR}
|
|
||||||
${STB_INCLUDE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Link libraries
|
|
||||||
target_link_libraries(are
|
|
||||||
PUBLIC
|
|
||||||
OpenGL::GL
|
|
||||||
glfw
|
|
||||||
glm::glm
|
|
||||||
)
|
|
||||||
|
|
||||||
# Link spdlog
|
|
||||||
if(spdlog_FOUND)
|
|
||||||
target_link_libraries(are PUBLIC spdlog::spdlog)
|
|
||||||
else()
|
|
||||||
target_link_libraries(are PUBLIC spdlog)
|
|
||||||
target_include_directories(are PRIVATE ${SPDLOG_INCLUDE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Link OpenMP if available
|
|
||||||
if(OpenMP_CXX_FOUND)
|
|
||||||
target_link_libraries(are PUBLIC OpenMP::OpenMP_CXX)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Platform-specific libraries
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
target_link_libraries(are PUBLIC dl pthread)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Compile definitions
|
|
||||||
if(ARE_ENABLE_PROFILING)
|
|
||||||
target_compile_definitions(are PUBLIC ARE_ENABLE_PROFILING)
|
|
||||||
message(STATUS "Profiling enabled")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ARE_ENABLE_DEBUG_VIS)
|
|
||||||
target_compile_definitions(are PUBLIC ARE_ENABLE_DEBUG_VIS)
|
|
||||||
message(STATUS "Debug visualization enabled")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Build examples
|
|
||||||
if(ARE_BUILD_EXAMPLES)
|
|
||||||
# Define helper function for creating examples
|
|
||||||
function(add_are_example EXAMPLE_NAME)
|
|
||||||
add_executable(${EXAMPLE_NAME} ${ARGN})
|
|
||||||
target_link_libraries(${EXAMPLE_NAME} PRIVATE are)
|
|
||||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Add example subdirectories
|
|
||||||
add_subdirectory(examples/00_phase1_test)
|
|
||||||
add_subdirectory(examples/01_phase2_test)
|
|
||||||
add_subdirectory(examples/02_visual_test)
|
|
||||||
add_subdirectory(examples/02_phase3_test)
|
|
||||||
add_subdirectory(examples/03_phase4_test)
|
|
||||||
add_subdirectory(examples/04_phase5_test)
|
|
||||||
|
|
||||||
message(STATUS "Examples will be built")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Installation rules
|
|
||||||
install(TARGETS are
|
|
||||||
ARCHIVE DESTINATION lib
|
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
RUNTIME DESTINATION bin
|
|
||||||
PUBLIC_HEADER DESTINATION include/are
|
|
||||||
)
|
|
||||||
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/are
|
|
||||||
DESTINATION include
|
|
||||||
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/shaders
|
|
||||||
DESTINATION share/are/shaders
|
|
||||||
)
|
|
||||||
|
|
||||||
# Print configuration summary
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "========================================")
|
|
||||||
message(STATUS "Aurora Rendering Engine Configuration")
|
|
||||||
message(STATUS "========================================")
|
|
||||||
message(STATUS " Version: ${PROJECT_VERSION}")
|
|
||||||
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
|
||||||
message(STATUS " Library type: ${ARE_BUILD_SHARED}")
|
|
||||||
message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}")
|
|
||||||
message(STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "Features:")
|
|
||||||
message(STATUS " OpenMP support: ${ARE_USE_OPENMP}")
|
|
||||||
message(STATUS " Build examples: ${ARE_BUILD_EXAMPLES}")
|
|
||||||
message(STATUS " Enable profiling: ${ARE_ENABLE_PROFILING}")
|
|
||||||
message(STATUS " Enable debug vis: ${ARE_ENABLE_DEBUG_VIS}")
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "Dependencies:")
|
|
||||||
message(STATUS " OpenGL: ${OPENGL_LIBRARIES}")
|
|
||||||
message(STATUS " GLFW: Found")
|
|
||||||
message(STATUS " GLM: Found")
|
|
||||||
if(spdlog_FOUND)
|
|
||||||
message(STATUS " spdlog: Found (system)")
|
|
||||||
else()
|
|
||||||
message(STATUS " spdlog: Found (local)")
|
|
||||||
endif()
|
|
||||||
message(STATUS " GLAD: ${GLAD_INCLUDE_DIR}")
|
|
||||||
message(STATUS " STB: ${STB_INCLUDE_DIR}")
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "Output directories:")
|
|
||||||
message(STATUS " Executables: ${CMAKE_BINARY_DIR}/bin")
|
|
||||||
message(STATUS " Libraries: ${CMAKE_BINARY_DIR}/lib")
|
|
||||||
message(STATUS "========================================")
|
|
||||||
message(STATUS "")
|
|
||||||
4336
all_headers.md
4336
all_headers.md
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +0,0 @@
|
||||||
# Phase 1 test program
|
|
||||||
# Note: add_are_example is defined in parent CMakeLists.txt
|
|
||||||
|
|
||||||
add_are_example(phase1_test main.cpp)
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Phase 1 verification program
|
|
||||||
*
|
|
||||||
* Tests core, platform, and utils modules.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/are.h>
|
|
||||||
#include <are/utils/random.h>
|
|
||||||
#include <are/utils/file_utils.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
|
|
||||||
void test_core_modules() {
|
|
||||||
std::cout << "\n=== Testing Core Modules ===" << std::endl;
|
|
||||||
|
|
||||||
// Test config
|
|
||||||
are::AreConfig config;
|
|
||||||
config.window.width = 800;
|
|
||||||
config.window.height = 600;
|
|
||||||
config.window.title = "Phase 1 Test";
|
|
||||||
|
|
||||||
if (config.validate()) {
|
|
||||||
ARE_LOG_INFO("Config validation passed");
|
|
||||||
config.print();
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("Config validation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test profiler
|
|
||||||
ARE_PROFILE_BEGIN("test_section");
|
|
||||||
|
|
||||||
// Simulate some work
|
|
||||||
double sum = 0.0;
|
|
||||||
for (int i = 0; i < 1000000; ++i) {
|
|
||||||
sum += i * 0.001;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_PROFILE_END("test_section");
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Profiler test completed (sum = " + std::to_string(sum) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_platform_modules() {
|
|
||||||
std::cout << "\n=== Testing Platform Modules ===" << std::endl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create window
|
|
||||||
are::WindowConfig win_config;
|
|
||||||
win_config.width = 800;
|
|
||||||
win_config.height = 600;
|
|
||||||
win_config.title = "Phase 1 Platform Test";
|
|
||||||
|
|
||||||
are::Window window(win_config);
|
|
||||||
ARE_LOG_INFO("Window created successfully");
|
|
||||||
|
|
||||||
// Initialize OpenGL context
|
|
||||||
if (are::GLContext::initialize()) {
|
|
||||||
ARE_LOG_INFO("OpenGL context initialized");
|
|
||||||
are::GLContext::print_info();
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("Failed to initialize OpenGL context");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear screen to blue
|
|
||||||
glClearColor(0.2f, 0.3f, 0.8f, 1.0f);
|
|
||||||
|
|
||||||
// Simple render loop (5 seconds)
|
|
||||||
ARE_LOG_INFO("Running render loop for 5 seconds...");
|
|
||||||
|
|
||||||
double start_time = glfwGetTime();
|
|
||||||
int frame_count = 0;
|
|
||||||
|
|
||||||
while (!window.should_close() && (glfwGetTime() - start_time) < 5.0) {
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
window.swap_buffers();
|
|
||||||
window.poll_events();
|
|
||||||
|
|
||||||
frame_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
double elapsed = glfwGetTime() - start_time;
|
|
||||||
double fps = frame_count / elapsed;
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Rendered " + std::to_string(frame_count) + " frames in " +
|
|
||||||
std::to_string(elapsed) + " seconds");
|
|
||||||
ARE_LOG_INFO("Average FPS: " + std::to_string(fps));
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
ARE_LOG_ERROR("Platform test failed: " + std::string(e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_utils_modules() {
|
|
||||||
std::cout << "\n=== Testing Utils Modules ===" << std::endl;
|
|
||||||
|
|
||||||
// Test math utils
|
|
||||||
are::Vec3 a(1, 0, 0);
|
|
||||||
are::Vec3 b(0, 1, 0);
|
|
||||||
are::Vec3 c(0, 0, 1);
|
|
||||||
are::Vec3 p(0.3f, 0.3f, 0.4f);
|
|
||||||
|
|
||||||
are::Real u, v, w;
|
|
||||||
are::compute_barycentric(p, a, b, c, u, v, w);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Barycentric coordinates: u=" + std::to_string(u) +
|
|
||||||
", v=" + std::to_string(v) + ", w=" + std::to_string(w));
|
|
||||||
|
|
||||||
// Test random
|
|
||||||
are::RandomGenerator rng(12345);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Random float [0,1): " + std::to_string(rng.random_float()));
|
|
||||||
ARE_LOG_INFO("Random float [10,20): " + std::to_string(rng.random_float(10.0f, 20.0f)));
|
|
||||||
ARE_LOG_INFO("Random int [1,100]: " + std::to_string(rng.random_int(1, 100)));
|
|
||||||
|
|
||||||
are::Vec3 random_vec = rng.random_unit_vector();
|
|
||||||
ARE_LOG_INFO("Random unit vector: (" + std::to_string(random_vec.x) + ", " +
|
|
||||||
std::to_string(random_vec.y) + ", " + std::to_string(random_vec.z) + ")");
|
|
||||||
|
|
||||||
// Test file utils
|
|
||||||
std::string test_dir = "test_output";
|
|
||||||
if (are::create_directory(test_dir)) {
|
|
||||||
ARE_LOG_INFO("Created directory: " + test_dir);
|
|
||||||
|
|
||||||
std::string test_file = test_dir + "/test.txt";
|
|
||||||
std::string content = "Hello, Aurora Rendering Engine!";
|
|
||||||
|
|
||||||
if (are::write_string_to_file(test_file, content)) {
|
|
||||||
ARE_LOG_INFO("Wrote test file: " + test_file);
|
|
||||||
|
|
||||||
std::string read_content = are::read_file_to_string(test_file);
|
|
||||||
if (read_content == content) {
|
|
||||||
ARE_LOG_INFO("File read/write test passed");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("File content mismatch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test image I/O (create a simple test image)
|
|
||||||
are::ImageData test_image;
|
|
||||||
test_image.width_ = 256;
|
|
||||||
test_image.height_ = 256;
|
|
||||||
test_image.channels_ = 3;
|
|
||||||
test_image.data_.resize(256 * 256 * 3);
|
|
||||||
|
|
||||||
// Create gradient
|
|
||||||
for (int y = 0; y < 256; ++y) {
|
|
||||||
for (int x = 0; x < 256; ++x) {
|
|
||||||
int idx = (y * 256 + x) * 3;
|
|
||||||
test_image.data_[idx + 0] = static_cast<uint8_t>(x);
|
|
||||||
test_image.data_[idx + 1] = static_cast<uint8_t>(y);
|
|
||||||
test_image.data_[idx + 2] = static_cast<uint8_t>((x + y) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string image_file = test_dir + "/gradient.png";
|
|
||||||
if (are::save_image(image_file, test_image)) {
|
|
||||||
ARE_LOG_INFO("Saved test image: " + image_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// Initialize engine
|
|
||||||
if (!are::initialize()) {
|
|
||||||
std::cerr << "Failed to initialize Aurora Rendering Engine" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Starting Phase 1 verification tests...");
|
|
||||||
|
|
||||||
// Run tests
|
|
||||||
test_core_modules();
|
|
||||||
test_utils_modules();
|
|
||||||
test_platform_modules();
|
|
||||||
|
|
||||||
// Print profiler results
|
|
||||||
are::Profiler::print_results();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("All Phase 1 tests completed!");
|
|
||||||
|
|
||||||
// Shutdown engine
|
|
||||||
are::shutdown();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# examples/01_hello_triangle/CMakeLists.txt
|
|
||||||
add_are_example(hello_triangle main.cpp)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Phase 2 verification example
|
|
||||||
|
|
||||||
add_are_example(phase2_test
|
|
||||||
main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy to bin directory for easy execution
|
|
||||||
set_target_properties(phase2_test PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
@ -1,326 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Phase 2 verification program
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
#include <are/geometry/transform.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/directional_light.h>
|
|
||||||
#include <are/scene/point_light.h>
|
|
||||||
#include <are/scene/spot_light.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace are;
|
|
||||||
|
|
||||||
// Test result tracking
|
|
||||||
struct TestResult {
|
|
||||||
std::string name;
|
|
||||||
bool passed;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<TestResult> test_results;
|
|
||||||
|
|
||||||
void report_test(const std::string& name, bool passed, const std::string& message = "") {
|
|
||||||
test_results.push_back({name, passed, message});
|
|
||||||
if (passed) {
|
|
||||||
ARE_LOG_INFO("✓ " + name);
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("✗ " + name + ": " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 1: Vertex operations
|
|
||||||
void test_vertex() {
|
|
||||||
Vertex v1(Vec3(1, 2, 3));
|
|
||||||
Vertex v2(Vec3(4, 5, 6), Vec3(0, 1, 0));
|
|
||||||
Vertex v3 = Vertex::lerp(v1, v2, 0.5f);
|
|
||||||
|
|
||||||
bool passed = glm::length(v3.position_ - Vec3(2.5f, 3.5f, 4.5f)) < are_epsilon;
|
|
||||||
report_test("Vertex interpolation", passed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 2: AABB operations
|
|
||||||
void test_aabb() {
|
|
||||||
AABB aabb1(Vec3(-1, -1, -1), Vec3(1, 1, 1));
|
|
||||||
AABB aabb2(Vec3(0, 0, 0), Vec3(2, 2, 2));
|
|
||||||
|
|
||||||
bool test1 = aabb1.is_valid();
|
|
||||||
bool test2 = aabb1.contains(Vec3(0, 0, 0));
|
|
||||||
bool test3 = aabb1.intersects(aabb2);
|
|
||||||
bool test4 = aabb1.longest_axis() == 0; // All axes equal
|
|
||||||
|
|
||||||
AABB merged = AABB::merge(aabb1, aabb2);
|
|
||||||
bool test5 = merged.contains(Vec3(-1, -1, -1)) && merged.contains(Vec3(2, 2, 2));
|
|
||||||
|
|
||||||
report_test("AABB validity", test1);
|
|
||||||
report_test("AABB contains point", test2);
|
|
||||||
report_test("AABB intersection", test3);
|
|
||||||
report_test("AABB merge", test5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 3: Triangle operations
|
|
||||||
void test_triangle() {
|
|
||||||
Vertex v0(Vec3(0, 0, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v1(Vec3(1, 0, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1));
|
|
||||||
|
|
||||||
Triangle tri(v0, v1, v2);
|
|
||||||
|
|
||||||
Vec3 centroid = tri.centroid();
|
|
||||||
bool test1 = glm::length(centroid - Vec3(1.0f/3.0f, 1.0f/3.0f, 0.0f)) < are_epsilon;
|
|
||||||
|
|
||||||
Vec3 normal = tri.normal();
|
|
||||||
bool test2 = glm::length(normal - Vec3(0, 0, 1)) < are_epsilon;
|
|
||||||
|
|
||||||
Real area = tri.area();
|
|
||||||
bool test3 = std::abs(area - 0.5f) < are_epsilon;
|
|
||||||
|
|
||||||
AABB aabb = tri.compute_aabb();
|
|
||||||
bool test4 = aabb.contains(Vec3(0, 0, 0)) && aabb.contains(Vec3(1, 0, 0));
|
|
||||||
|
|
||||||
report_test("Triangle centroid", test1);
|
|
||||||
report_test("Triangle normal", test2);
|
|
||||||
report_test("Triangle area", test3);
|
|
||||||
report_test("Triangle AABB", test4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 4: Ray-Triangle intersection
|
|
||||||
void test_ray_triangle_intersection() {
|
|
||||||
Vertex v0(Vec3(0, 0, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v1(Vec3(1, 0, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1));
|
|
||||||
|
|
||||||
Triangle tri(v0, v1, v2);
|
|
||||||
|
|
||||||
// Ray hitting the triangle
|
|
||||||
Ray ray1(Vec3(0.25f, 0.25f, -1.0f), Vec3(0, 0, 1));
|
|
||||||
HitRecord hit1;
|
|
||||||
bool test1 = tri.intersect(ray1, hit1);
|
|
||||||
|
|
||||||
// Ray missing the triangle
|
|
||||||
Ray ray2(Vec3(2, 2, -1), Vec3(0, 0, 1));
|
|
||||||
HitRecord hit2;
|
|
||||||
bool test2 = !tri.intersect(ray2, hit2);
|
|
||||||
|
|
||||||
report_test("Ray-Triangle hit", test1);
|
|
||||||
report_test("Ray-Triangle miss", test2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 5: Transform operations
|
|
||||||
void test_transform() {
|
|
||||||
Transform t1 = Transform::translate(Vec3(1, 2, 3));
|
|
||||||
Transform t2 = Transform::rotate(Vec3(0, are_pi / 2, 0));
|
|
||||||
Transform t3 = Transform::scale(Vec3(2, 2, 2));
|
|
||||||
|
|
||||||
Vec3 point = Vec3(1, 0, 0);
|
|
||||||
Vec3 transformed = t1.transform_point(point);
|
|
||||||
bool test1 = glm::length(transformed - Vec3(2, 2, 3)) < are_epsilon;
|
|
||||||
|
|
||||||
Vec3 scaled = t3.transform_point(point);
|
|
||||||
bool test2 = glm::length(scaled - Vec3(2, 0, 0)) < are_epsilon;
|
|
||||||
|
|
||||||
report_test("Transform translation", test1);
|
|
||||||
report_test("Transform scale", test2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 6: Camera operations
|
|
||||||
void test_camera() {
|
|
||||||
Camera camera(Vec3(0, 0, 5), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, 16.0f / 9.0f, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
Vec3 forward = camera.get_forward();
|
|
||||||
bool test1 = glm::length(forward - Vec3(0, 0, -1)) < are_epsilon;
|
|
||||||
|
|
||||||
Vec3 origin, direction;
|
|
||||||
camera.generate_ray(0.5f, 0.5f, origin, direction);
|
|
||||||
bool test2 = glm::length(origin - Vec3(0, 0, 5)) < are_epsilon;
|
|
||||||
bool test3 = glm::length(direction - Vec3(0, 0, -1)) < 0.1f; // Approximate
|
|
||||||
|
|
||||||
report_test("Camera forward vector", test1);
|
|
||||||
report_test("Camera ray generation origin", test2);
|
|
||||||
report_test("Camera ray generation direction", test3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 7: Mesh operations
|
|
||||||
void test_mesh() {
|
|
||||||
std::vector<Vertex> vertices = {
|
|
||||||
Vertex(Vec3(0, 0, 0), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(1, 0, 0), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(0, 1, 0), Vec3(0, 0, 1))
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> indices = {0, 1, 2};
|
|
||||||
|
|
||||||
Mesh mesh(vertices, indices);
|
|
||||||
|
|
||||||
bool test1 = mesh.get_vertex_count() == 3;
|
|
||||||
bool test2 = mesh.get_triangle_count() == 1;
|
|
||||||
bool test3 = mesh.get_aabb().is_valid();
|
|
||||||
|
|
||||||
Vertex v0, v1, v2;
|
|
||||||
bool test4 = mesh.get_triangle(0, v0, v1, v2);
|
|
||||||
|
|
||||||
report_test("Mesh vertex count", test1);
|
|
||||||
report_test("Mesh triangle count", test2);
|
|
||||||
report_test("Mesh AABB", test3);
|
|
||||||
report_test("Mesh get triangle", test4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 8: Material operations
|
|
||||||
void test_material() {
|
|
||||||
Material mat;
|
|
||||||
mat.set_albedo(Vec3(0.8f, 0.2f, 0.1f));
|
|
||||||
mat.set_metallic(0.5f);
|
|
||||||
mat.set_roughness(0.3f);
|
|
||||||
mat.set_emissive(Vec3(1.0f, 0.5f, 0.0f));
|
|
||||||
|
|
||||||
bool test1 = glm::length(mat.get_albedo() - Vec3(0.8f, 0.2f, 0.1f)) < are_epsilon;
|
|
||||||
bool test2 = std::abs(mat.get_metallic() - 0.5f) < are_epsilon;
|
|
||||||
bool test3 = mat.is_emissive();
|
|
||||||
|
|
||||||
mat.set_albedo_map("textures/albedo.png");
|
|
||||||
bool test4 = mat.has_albedo_map();
|
|
||||||
|
|
||||||
report_test("Material albedo", test1);
|
|
||||||
report_test("Material metallic", test2);
|
|
||||||
report_test("Material emissive", test3);
|
|
||||||
report_test("Material texture map", test4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 9: Light operations
|
|
||||||
void test_lights() {
|
|
||||||
// Directional light
|
|
||||||
DirectionalLight dir_light(Vec3(0, -1, 0), Vec3(1, 1, 1), 1.0f);
|
|
||||||
bool test1 = dir_light.affects_point(Vec3(100, 100, 100));
|
|
||||||
|
|
||||||
// Point light
|
|
||||||
PointLight point_light(Vec3(0, 0, 0), Vec3(1, 1, 1), 1.0f, 10.0f);
|
|
||||||
bool test2 = point_light.affects_point(Vec3(5, 0, 0));
|
|
||||||
bool test3 = !point_light.affects_point(Vec3(20, 0, 0));
|
|
||||||
|
|
||||||
// Spot light
|
|
||||||
SpotLight spot_light(Vec3(0, 0, 0), Vec3(0, 0, -1), 30.0f, 45.0f);
|
|
||||||
bool test4 = spot_light.affects_point(Vec3(0, 0, -5));
|
|
||||||
|
|
||||||
report_test("Directional light affects all points", test1);
|
|
||||||
report_test("Point light range (inside)", test2);
|
|
||||||
report_test("Point light range (outside)", test3);
|
|
||||||
report_test("Spot light cone", test4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 10: SceneManager operations
|
|
||||||
void test_scene_manager() {
|
|
||||||
SceneManager scene;
|
|
||||||
|
|
||||||
// Add mesh
|
|
||||||
std::vector<Vertex> vertices = {
|
|
||||||
Vertex(Vec3(0, 0, 0)),
|
|
||||||
Vertex(Vec3(1, 0, 0)),
|
|
||||||
Vertex(Vec3(0, 1, 0))
|
|
||||||
};
|
|
||||||
std::vector<uint32_t> indices = {0, 1, 2};
|
|
||||||
Mesh mesh(vertices, indices);
|
|
||||||
|
|
||||||
MeshHandle mesh_handle = scene.add_mesh(mesh);
|
|
||||||
bool test1 = mesh_handle != are_invalid_handle;
|
|
||||||
bool test2 = scene.get_mesh_count() == 1;
|
|
||||||
|
|
||||||
// Add material
|
|
||||||
Material mat;
|
|
||||||
MaterialHandle mat_handle = scene.add_material(mat);
|
|
||||||
bool test3 = mat_handle != are_invalid_handle;
|
|
||||||
bool test4 = scene.get_material_count() == 1;
|
|
||||||
|
|
||||||
// Add light
|
|
||||||
auto light = std::make_shared<DirectionalLight>();
|
|
||||||
LightHandle light_handle = scene.add_light(light);
|
|
||||||
bool test5 = light_handle != are_invalid_handle;
|
|
||||||
bool test6 = scene.get_light_count() == 1;
|
|
||||||
|
|
||||||
// Test dirty flag
|
|
||||||
bool test7 = scene.is_dirty();
|
|
||||||
scene.clear_dirty();
|
|
||||||
bool test8 = !scene.is_dirty();
|
|
||||||
|
|
||||||
// Remove mesh
|
|
||||||
scene.remove_mesh(mesh_handle);
|
|
||||||
bool test9 = scene.get_mesh_count() == 0;
|
|
||||||
|
|
||||||
report_test("SceneManager add mesh", test1);
|
|
||||||
report_test("SceneManager mesh count", test2);
|
|
||||||
report_test("SceneManager add material", test3);
|
|
||||||
report_test("SceneManager material count", test4);
|
|
||||||
report_test("SceneManager add light", test5);
|
|
||||||
report_test("SceneManager light count", test6);
|
|
||||||
report_test("SceneManager dirty flag (set)", test7);
|
|
||||||
report_test("SceneManager dirty flag (clear)", test8);
|
|
||||||
report_test("SceneManager remove mesh", test9);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// Initialize logger
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Phase 2 Verification Program");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
// Run all tests
|
|
||||||
test_vertex();
|
|
||||||
test_aabb();
|
|
||||||
test_triangle();
|
|
||||||
test_ray_triangle_intersection();
|
|
||||||
test_transform();
|
|
||||||
test_camera();
|
|
||||||
test_mesh();
|
|
||||||
test_material();
|
|
||||||
test_lights();
|
|
||||||
test_scene_manager();
|
|
||||||
|
|
||||||
// Print summary
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Test Summary");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
int passed = 0;
|
|
||||||
int failed = 0;
|
|
||||||
|
|
||||||
for (const auto& result : test_results) {
|
|
||||||
if (result.passed) {
|
|
||||||
++passed;
|
|
||||||
} else {
|
|
||||||
++failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Total tests: " + std::to_string(test_results.size()));
|
|
||||||
ARE_LOG_INFO("Passed: " + std::to_string(passed));
|
|
||||||
ARE_LOG_INFO("Failed: " + std::to_string(failed));
|
|
||||||
|
|
||||||
if (failed == 0) {
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("✓ All Phase 2 tests passed!");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("========================================");
|
|
||||||
ARE_LOG_ERROR("✗ Some tests failed. Please review.");
|
|
||||||
ARE_LOG_ERROR("========================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::shutdown();
|
|
||||||
|
|
||||||
return failed == 0 ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# examples/02_cornell_box/CMakeLists.txt
|
|
||||||
add_are_example(cornell_box main.cpp)
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# Phase 3 verification example
|
|
||||||
|
|
||||||
add_are_example(phase3_test
|
|
||||||
main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy to bin directory for easy execution
|
|
||||||
set_target_properties(phase3_test PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy shaders to build directory
|
|
||||||
add_custom_command(TARGET phase3_test POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
${CMAKE_SOURCE_DIR}/shaders
|
|
||||||
${CMAKE_BINARY_DIR}/bin/shaders
|
|
||||||
COMMENT "Copying shaders to build directory"
|
|
||||||
)
|
|
||||||
|
|
@ -1,478 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Phase 3 verification program - G-Buffer rendering test
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/platform/gl_context.h>
|
|
||||||
#include <are/platform/window.h>
|
|
||||||
#include <are/rasterizer/gbuffer.h>
|
|
||||||
#include <are/rasterizer/rasterizer.h>
|
|
||||||
#include <are/rasterizer/shader_program.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <are/utils/file_utils.h>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include "../lib/glad/glad/glad.h"
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
|
|
||||||
using namespace are;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a simple cube mesh
|
|
||||||
*/
|
|
||||||
Mesh create_cube_mesh() {
|
|
||||||
std::vector<Vertex> vertices = {
|
|
||||||
// Front face
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 1)),
|
|
||||||
|
|
||||||
// Back face
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 1)),
|
|
||||||
|
|
||||||
// Top face
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(0, 1)),
|
|
||||||
|
|
||||||
// Bottom face
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(0, 1)),
|
|
||||||
|
|
||||||
// Right face
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 1)),
|
|
||||||
|
|
||||||
// Left face
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 1))
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> indices = {
|
|
||||||
// Front
|
|
||||||
0, 1, 2, 2, 3, 0,
|
|
||||||
// Back
|
|
||||||
4, 5, 6, 6, 7, 4,
|
|
||||||
// Top
|
|
||||||
8, 9, 10, 10, 11, 8,
|
|
||||||
// Bottom
|
|
||||||
12, 13, 14, 14, 15, 12,
|
|
||||||
// Right
|
|
||||||
16, 17, 18, 18, 19, 16,
|
|
||||||
// Left
|
|
||||||
20, 21, 22, 22, 23, 20
|
|
||||||
};
|
|
||||||
|
|
||||||
return Mesh(vertices, indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a simple triangle mesh (positioned in front of cube)
|
|
||||||
*/
|
|
||||||
Mesh create_triangle_mesh() {
|
|
||||||
std::vector<Vertex> vertices = {
|
|
||||||
Vertex(Vec3(-0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0, 0)), // Z = 1.0
|
|
||||||
Vertex(Vec3( 0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(1, 0)), // Z = 1.0
|
|
||||||
Vertex(Vec3( 0.0f, 0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0.5f, 1)) // Z = 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> indices = {0, 1, 2};
|
|
||||||
|
|
||||||
return Mesh(vertices, indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Fullscreen quad shader for G-Buffer visualization
|
|
||||||
*/
|
|
||||||
const char *fullscreen_vert_source = R"(
|
|
||||||
#version 430 core
|
|
||||||
layout(location = 0) in vec2 a_position;
|
|
||||||
layout(location = 1) in vec2 a_texcoord;
|
|
||||||
out vec2 v_texcoord;
|
|
||||||
void main() {
|
|
||||||
v_texcoord = a_texcoord;
|
|
||||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
const char *visualize_frag_source = R"(
|
|
||||||
#version 430 core
|
|
||||||
in vec2 v_texcoord;
|
|
||||||
out vec4 frag_color;
|
|
||||||
|
|
||||||
uniform sampler2D u_texture;
|
|
||||||
uniform int u_mode; // 0=position, 1=normal, 2=albedo, 3=material
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 value = texture(u_texture, v_texcoord);
|
|
||||||
|
|
||||||
if (u_mode == 0) {
|
|
||||||
// Position: normalize to [0,1] range for visualization
|
|
||||||
frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0);
|
|
||||||
} else if (u_mode == 1) {
|
|
||||||
// Normal: normalize to [0,1] range
|
|
||||||
frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0);
|
|
||||||
} else if (u_mode == 2) {
|
|
||||||
// Albedo: direct output
|
|
||||||
frag_color = vec4(value.rgb, 1.0);
|
|
||||||
} else if (u_mode == 3) {
|
|
||||||
// Material: roughness in R, AO in G
|
|
||||||
frag_color = vec4(value.r, value.g, 0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
frag_color = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create fullscreen quad VAO
|
|
||||||
*/
|
|
||||||
uint32_t create_fullscreen_quad() {
|
|
||||||
float vertices[] = {
|
|
||||||
// Position // Texcoord
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
|
||||||
1.0f, -1.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 1.0f, 1.0f, 1.0f,
|
|
||||||
-1.0f, 1.0f, 0.0f, 1.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t indices[] = { 0, 1, 2, 2, 3, 0 };
|
|
||||||
|
|
||||||
uint32_t vao, vbo, ebo;
|
|
||||||
|
|
||||||
glGenVertexArrays(1, &vao);
|
|
||||||
glBindVertexArray(vao);
|
|
||||||
|
|
||||||
glGenBuffers(1, &vbo);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glGenBuffers(1, &ebo);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
return vao;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// Initialize logger
|
|
||||||
Logger::init(LogLevel::ARE_LOG_DEBUG);
|
|
||||||
Profiler::init();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Phase 3 Verification Program");
|
|
||||||
ARE_LOG_INFO("G-Buffer Rendering Test");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
// Create window configuration
|
|
||||||
WindowConfig window_config;
|
|
||||||
window_config.width = 800;
|
|
||||||
window_config.height = 600;
|
|
||||||
window_config.title = "Phase 3 - G-Buffer Test";
|
|
||||||
window_config.vsync = true;
|
|
||||||
|
|
||||||
// Create window
|
|
||||||
Window window(window_config);
|
|
||||||
|
|
||||||
// Initialize OpenGL
|
|
||||||
if (!GLContext::initialize()) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to initialize OpenGL context");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLContext::print_info();
|
|
||||||
|
|
||||||
// Get shader directory (relative to executable)
|
|
||||||
std::string shader_dir = "shaders/";
|
|
||||||
|
|
||||||
// Create rasterizer
|
|
||||||
int fb_width, fb_height;
|
|
||||||
window.get_framebuffer_size(fb_width, fb_height);
|
|
||||||
Rasterizer rasterizer(fb_width, fb_height);
|
|
||||||
|
|
||||||
// Initialize shaders
|
|
||||||
// First, create G-Buffer shader manually for testing
|
|
||||||
ShaderProgram gbuffer_shader;
|
|
||||||
|
|
||||||
// Try to load from file first
|
|
||||||
bool shader_loaded = false;
|
|
||||||
if (file_exists(shader_dir + "gbuffer/gbuffer.vert") && file_exists(shader_dir + "gbuffer/gbuffer.frag")) {
|
|
||||||
if (gbuffer_shader.load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert") && gbuffer_shader.load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag") && gbuffer_shader.link()) {
|
|
||||||
shader_loaded = true;
|
|
||||||
ARE_LOG_INFO("Loaded G-Buffer shaders from files");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to embedded shaders
|
|
||||||
if (!shader_loaded) {
|
|
||||||
ARE_LOG_WARN("Shader files not found, using embedded shaders");
|
|
||||||
|
|
||||||
const char *gbuffer_vert = R"(
|
|
||||||
#version 430 core
|
|
||||||
layout(location = 0) in vec3 a_position;
|
|
||||||
layout(location = 1) in vec3 a_normal;
|
|
||||||
layout(location = 2) in vec2 a_texcoord;
|
|
||||||
layout(location = 3) in vec3 a_tangent;
|
|
||||||
|
|
||||||
uniform mat4 u_model;
|
|
||||||
uniform mat4 u_view;
|
|
||||||
uniform mat4 u_projection;
|
|
||||||
uniform mat3 u_normal_matrix;
|
|
||||||
|
|
||||||
out vec3 v_world_position;
|
|
||||||
out vec3 v_world_normal;
|
|
||||||
out vec2 v_texcoord;
|
|
||||||
out vec3 v_world_tangent;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 world_pos = u_model * vec4(a_position, 1.0);
|
|
||||||
v_world_position = world_pos.xyz;
|
|
||||||
v_world_normal = normalize(u_normal_matrix * a_normal);
|
|
||||||
v_world_tangent = normalize(u_normal_matrix * a_tangent);
|
|
||||||
v_texcoord = a_texcoord;
|
|
||||||
gl_Position = u_projection * u_view * world_pos;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
const char *gbuffer_frag = R"(
|
|
||||||
#version 430 core
|
|
||||||
in vec3 v_world_position;
|
|
||||||
in vec3 v_world_normal;
|
|
||||||
in vec2 v_texcoord;
|
|
||||||
in vec3 v_world_tangent;
|
|
||||||
|
|
||||||
uniform vec3 u_albedo;
|
|
||||||
uniform float u_metallic;
|
|
||||||
uniform float u_roughness;
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 g_position;
|
|
||||||
layout(location = 1) out vec3 g_normal;
|
|
||||||
layout(location = 2) out vec4 g_albedo_metallic;
|
|
||||||
layout(location = 3) out vec2 g_roughness_ao;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
g_position = v_world_position;
|
|
||||||
g_normal = normalize(v_world_normal);
|
|
||||||
g_albedo_metallic = vec4(u_albedo, u_metallic);
|
|
||||||
g_roughness_ao = vec2(u_roughness, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
if (!gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, gbuffer_vert) || !gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, gbuffer_frag) || !gbuffer_shader.link()) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to compile embedded G-Buffer shaders");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create visualization shader
|
|
||||||
ShaderProgram vis_shader;
|
|
||||||
if (!vis_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, fullscreen_vert_source) || !vis_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, visualize_frag_source) || !vis_shader.link()) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to compile visualization shaders");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create fullscreen quad
|
|
||||||
uint32_t fullscreen_quad_vao = create_fullscreen_quad();
|
|
||||||
|
|
||||||
// Create scene
|
|
||||||
SceneManager scene;
|
|
||||||
|
|
||||||
// Create materials
|
|
||||||
Material red_material;
|
|
||||||
red_material.set_albedo(Vec3(0.8f, 0.2f, 0.2f));
|
|
||||||
red_material.set_metallic(0.0f);
|
|
||||||
red_material.set_roughness(0.5f);
|
|
||||||
MaterialHandle red_mat_handle = scene.add_material(red_material);
|
|
||||||
|
|
||||||
Material green_material;
|
|
||||||
green_material.set_albedo(Vec3(0.2f, 0.8f, 0.2f));
|
|
||||||
green_material.set_metallic(0.5f);
|
|
||||||
green_material.set_roughness(0.3f);
|
|
||||||
MaterialHandle green_mat_handle = scene.add_material(green_material);
|
|
||||||
|
|
||||||
// Create meshes
|
|
||||||
Mesh cube = create_cube_mesh();
|
|
||||||
cube.set_material(red_mat_handle);
|
|
||||||
cube.compute_tangents();
|
|
||||||
|
|
||||||
Mesh triangle = create_triangle_mesh();
|
|
||||||
triangle.set_material(green_mat_handle);
|
|
||||||
triangle.compute_tangents();
|
|
||||||
|
|
||||||
// Upload meshes to GPU
|
|
||||||
rasterizer.upload_mesh(cube);
|
|
||||||
rasterizer.upload_mesh(triangle);
|
|
||||||
|
|
||||||
// Add meshes to scene
|
|
||||||
scene.add_mesh(cube);
|
|
||||||
scene.add_mesh(triangle);
|
|
||||||
|
|
||||||
// Create camera
|
|
||||||
Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, static_cast<float>(fb_width) / fb_height, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
// Visualization mode (0=position, 1=normal, 2=albedo, 3=material)
|
|
||||||
int vis_mode = 2; // Start with albedo
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Controls:");
|
|
||||||
ARE_LOG_INFO(" 1 - View Position buffer");
|
|
||||||
ARE_LOG_INFO(" 2 - View Normal buffer");
|
|
||||||
ARE_LOG_INFO(" 3 - View Albedo buffer");
|
|
||||||
ARE_LOG_INFO(" 4 - View Material buffer (Roughness/AO)");
|
|
||||||
ARE_LOG_INFO(" ESC - Exit");
|
|
||||||
|
|
||||||
// Main loop
|
|
||||||
float time = 0.0f;
|
|
||||||
while (!window.should_close()) {
|
|
||||||
ARE_PROFILE_SCOPE("Frame");
|
|
||||||
|
|
||||||
// Poll events
|
|
||||||
window.poll_events();
|
|
||||||
|
|
||||||
// Handle input
|
|
||||||
if (window.is_key_pressed(256)) { // ESC
|
|
||||||
window.set_should_close(true);
|
|
||||||
}
|
|
||||||
if (window.is_key_pressed(49))
|
|
||||||
vis_mode = 0; // 1 - Position
|
|
||||||
if (window.is_key_pressed(50))
|
|
||||||
vis_mode = 1; // 2 - Normal
|
|
||||||
if (window.is_key_pressed(51))
|
|
||||||
vis_mode = 2; // 3 - Albedo
|
|
||||||
if (window.is_key_pressed(52))
|
|
||||||
vis_mode = 3; // 4 - Material
|
|
||||||
|
|
||||||
// Update camera position (orbit around origin)
|
|
||||||
time += 0.016f;
|
|
||||||
float cam_x = std::sin(time * 0.5f) * 3.0f;
|
|
||||||
float cam_z = std::cos(time * 0.5f) * 3.0f;
|
|
||||||
camera.set_position(Vec3(cam_x, 1.5f, cam_z));
|
|
||||||
camera.set_target(Vec3(0, 0, 0));
|
|
||||||
|
|
||||||
// Render to G-Buffer
|
|
||||||
{
|
|
||||||
ARE_PROFILE_SCOPE("Render G-Buffer");
|
|
||||||
|
|
||||||
// Manually render since we're using our own shader
|
|
||||||
GBuffer &gbuffer = rasterizer.get_gbuffer();
|
|
||||||
gbuffer.bind();
|
|
||||||
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
|
|
||||||
gbuffer_shader.use();
|
|
||||||
gbuffer_shader.set_uniform("u_view", camera.get_view_matrix());
|
|
||||||
gbuffer_shader.set_uniform("u_projection", camera.get_projection_matrix());
|
|
||||||
|
|
||||||
// Render all meshes
|
|
||||||
const auto &meshes = scene.get_all_meshes();
|
|
||||||
const auto &materials = scene.get_all_materials();
|
|
||||||
|
|
||||||
for (const auto &mesh : meshes) {
|
|
||||||
if (!mesh.has_gpu_resources())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Mat4 model = Mat4(1.0f);
|
|
||||||
gbuffer_shader.set_uniform("u_model", model);
|
|
||||||
|
|
||||||
Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model)));
|
|
||||||
gbuffer_shader.set_uniform("u_normal_matrix", normal_matrix);
|
|
||||||
|
|
||||||
MaterialHandle mat_handle = mesh.get_material();
|
|
||||||
if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) {
|
|
||||||
const Material &mat = materials[mat_handle - 1];
|
|
||||||
gbuffer_shader.set_uniform("u_albedo", mat.get_albedo());
|
|
||||||
gbuffer_shader.set_uniform("u_metallic", mat.get_metallic());
|
|
||||||
gbuffer_shader.set_uniform("u_roughness", mat.get_roughness());
|
|
||||||
} else {
|
|
||||||
gbuffer_shader.set_uniform("u_albedo", Vec3(0.8f));
|
|
||||||
gbuffer_shader.set_uniform("u_metallic", 0.0f);
|
|
||||||
gbuffer_shader.set_uniform("u_roughness", 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(mesh.get_vao());
|
|
||||||
glDrawElements(GL_TRIANGLES,
|
|
||||||
static_cast<GLsizei>(mesh.get_index_count()),
|
|
||||||
GL_UNSIGNED_INT,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
gbuffer.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visualize G-Buffer
|
|
||||||
{
|
|
||||||
ARE_PROFILE_SCOPE("Visualize G-Buffer");
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
glViewport(0, 0, fb_width, fb_height);
|
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
vis_shader.use();
|
|
||||||
vis_shader.set_uniform("u_mode", vis_mode);
|
|
||||||
vis_shader.set_uniform("u_texture", 0);
|
|
||||||
|
|
||||||
// Bind appropriate G-Buffer texture
|
|
||||||
GBuffer &gbuffer = rasterizer.get_gbuffer();
|
|
||||||
gbuffer.bind_texture(vis_mode, 0);
|
|
||||||
|
|
||||||
glBindVertexArray(fullscreen_quad_vao);
|
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap buffers
|
|
||||||
window.swap_buffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
glDeleteVertexArrays(1, &fullscreen_quad_vao);
|
|
||||||
|
|
||||||
// Print profiling results
|
|
||||||
Profiler::print_results();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Phase 3 test completed successfully!");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
Profiler::shutdown();
|
|
||||||
Logger::shutdown();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Phase 2 visual verification example
|
|
||||||
|
|
||||||
add_are_example(visual_test
|
|
||||||
main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy to bin directory
|
|
||||||
set_target_properties(visual_test PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
@ -1,329 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Visual verification using software rasterization
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include "../lib/stb/stb_image_write.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace are;
|
|
||||||
|
|
||||||
// Simple framebuffer
|
|
||||||
struct Framebuffer {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
std::vector<uint8_t> pixels; // RGB format
|
|
||||||
|
|
||||||
Framebuffer(int w, int h) : width(w), height(h) {
|
|
||||||
pixels.resize(w * h * 3, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
|
|
||||||
if (x < 0 || x >= width || y < 0 || y >= height)
|
|
||||||
return;
|
|
||||||
int index = (y * width + x) * 3;
|
|
||||||
pixels[index + 0] = r;
|
|
||||||
pixels[index + 1] = g;
|
|
||||||
pixels[index + 2] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pixel(int x, int y, const Vec3 &color) {
|
|
||||||
uint8_t r = static_cast<uint8_t>(std::min(color.x * 255.0f, 255.0f));
|
|
||||||
uint8_t g = static_cast<uint8_t>(std::min(color.y * 255.0f, 255.0f));
|
|
||||||
uint8_t b = static_cast<uint8_t>(std::min(color.z * 255.0f, 255.0f));
|
|
||||||
set_pixel(x, y, r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool save(const std::string &filename) {
|
|
||||||
return stbi_write_png(filename.c_str(), width, height, 3,
|
|
||||||
pixels.data(), width * 3)
|
|
||||||
!= 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Simple shading function
|
|
||||||
Vec3 shade_hit(const HitRecord &hit, const Vec3 &light_dir) {
|
|
||||||
// Lambertian shading
|
|
||||||
float ndotl = std::max(0.0f, glm::dot(hit.normal_, light_dir));
|
|
||||||
|
|
||||||
// Base color based on normal (for visualization)
|
|
||||||
Vec3 base_color = (hit.normal_ + Vec3(1.0f)) * 0.5f;
|
|
||||||
|
|
||||||
// Apply lighting
|
|
||||||
Vec3 ambient = base_color * 0.2f;
|
|
||||||
Vec3 diffuse = base_color * ndotl * 0.8f;
|
|
||||||
|
|
||||||
return ambient + diffuse;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render a single triangle
|
|
||||||
void render_triangle(Framebuffer &fb, const Triangle &tri, Camera &camera) {
|
|
||||||
Vec3 light_dir = glm::normalize(Vec3(0.5f, 1.0f, 0.5f));
|
|
||||||
|
|
||||||
for (int y = 0; y < fb.height; ++y) {
|
|
||||||
for (int x = 0; x < fb.width; ++x) {
|
|
||||||
// Generate ray
|
|
||||||
float u = (x + 0.5f) / fb.width;
|
|
||||||
float v = (y + 0.5f) / fb.height;
|
|
||||||
|
|
||||||
Vec3 origin, direction;
|
|
||||||
camera.generate_ray(u, v, origin, direction);
|
|
||||||
Ray ray(origin, direction);
|
|
||||||
|
|
||||||
// Test intersection
|
|
||||||
HitRecord hit;
|
|
||||||
if (tri.intersect(ray, hit)) {
|
|
||||||
Vec3 color = shade_hit(hit, light_dir);
|
|
||||||
fb.set_pixel(x, y, color);
|
|
||||||
} else {
|
|
||||||
// Background gradient
|
|
||||||
Vec3 bg_color = Vec3(0.5f, 0.7f, 1.0f) * (1.0f - v) + Vec3(1.0f, 1.0f, 1.0f) * v;
|
|
||||||
fb.set_pixel(x, y, bg_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render multiple triangles (mesh)
|
|
||||||
void render_mesh(Framebuffer &fb, const Mesh &mesh, Camera &camera) {
|
|
||||||
Vec3 light_dir = glm::normalize(Vec3(0.5f, 1.0f, 0.5f));
|
|
||||||
|
|
||||||
for (int y = 0; y < fb.height; ++y) {
|
|
||||||
for (int x = 0; x < fb.width; ++x) {
|
|
||||||
// Generate ray
|
|
||||||
float u = (x + 0.5f) / fb.width;
|
|
||||||
float v = (y + 0.5f) / fb.height;
|
|
||||||
|
|
||||||
Vec3 origin, direction;
|
|
||||||
camera.generate_ray(u, v, origin, direction);
|
|
||||||
Ray ray(origin, direction);
|
|
||||||
|
|
||||||
// Test intersection with all triangles
|
|
||||||
HitRecord closest_hit;
|
|
||||||
closest_hit.t_ = ray.t_max_;
|
|
||||||
bool hit_any = false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mesh.get_triangle_count(); ++i) {
|
|
||||||
Vertex v0, v1, v2;
|
|
||||||
if (mesh.get_triangle(i, v0, v1, v2)) {
|
|
||||||
Triangle tri(v0, v1, v2);
|
|
||||||
HitRecord hit;
|
|
||||||
if (tri.intersect(ray, hit) && hit.t_ < closest_hit.t_) {
|
|
||||||
closest_hit = hit;
|
|
||||||
hit_any = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hit_any) {
|
|
||||||
Vec3 color = shade_hit(closest_hit, light_dir);
|
|
||||||
fb.set_pixel(x, y, color);
|
|
||||||
} else {
|
|
||||||
// Background gradient
|
|
||||||
Vec3 bg_color = Vec3(0.5f, 0.7f, 1.0f) * (1.0f - v) + Vec3(1.0f, 1.0f, 1.0f) * v;
|
|
||||||
fb.set_pixel(x, y, bg_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Phase 2 Visual Verification");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
const int width = 800;
|
|
||||||
const int height = 600;
|
|
||||||
|
|
||||||
// Test 1: Single triangle
|
|
||||||
{
|
|
||||||
ARE_LOG_INFO("Rendering single triangle...");
|
|
||||||
|
|
||||||
Framebuffer fb(width, height);
|
|
||||||
|
|
||||||
// Create triangle
|
|
||||||
Vertex v0(Vec3(-1, -1, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v1(Vec3(1, -1, 0), Vec3(0, 0, 1));
|
|
||||||
Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1));
|
|
||||||
Triangle tri(v0, v1, v2);
|
|
||||||
|
|
||||||
// Setup camera
|
|
||||||
Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
render_triangle(fb, tri, camera);
|
|
||||||
|
|
||||||
// Save
|
|
||||||
if (fb.save("output_triangle.png")) {
|
|
||||||
ARE_LOG_INFO("✓ Saved: output_triangle.png");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("✗ Failed to save output_triangle.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 2: Colored triangle (using normals)
|
|
||||||
{
|
|
||||||
ARE_LOG_INFO("Rendering colored triangle...");
|
|
||||||
|
|
||||||
Framebuffer fb(width, height);
|
|
||||||
|
|
||||||
// Create triangle with different normals for each vertex
|
|
||||||
Vertex v0(Vec3(-1, -1, 0), Vec3(1, 0, 0)); // Red
|
|
||||||
Vertex v1(Vec3(1, -1, 0), Vec3(0, 1, 0)); // Green
|
|
||||||
Vertex v2(Vec3(0, 1, 0), Vec3(0, 0, 1)); // Blue
|
|
||||||
Triangle tri(v0, v1, v2);
|
|
||||||
|
|
||||||
Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
render_triangle(fb, tri, camera);
|
|
||||||
|
|
||||||
if (fb.save("output_colored_triangle.png")) {
|
|
||||||
ARE_LOG_INFO("✓ Saved: output_colored_triangle.png");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("✗ Failed to save output_colored_triangle.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 3: Cube (mesh with multiple triangles)
|
|
||||||
{
|
|
||||||
ARE_LOG_INFO("Rendering cube...");
|
|
||||||
|
|
||||||
Framebuffer fb(width, height);
|
|
||||||
|
|
||||||
// Create cube vertices
|
|
||||||
std::vector<Vertex> vertices = {
|
|
||||||
// Front face
|
|
||||||
Vertex(Vec3(-1, -1, 1), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(1, -1, 1), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(1, 1, 1), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(-1, 1, 1), Vec3(0, 0, 1)),
|
|
||||||
// Back face
|
|
||||||
Vertex(Vec3(-1, -1, -1), Vec3(0, 0, -1)),
|
|
||||||
Vertex(Vec3(1, -1, -1), Vec3(0, 0, -1)),
|
|
||||||
Vertex(Vec3(1, 1, -1), Vec3(0, 0, -1)),
|
|
||||||
Vertex(Vec3(-1, 1, -1), Vec3(0, 0, -1)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create cube indices
|
|
||||||
std::vector<uint32_t> indices = {
|
|
||||||
// Front
|
|
||||||
0, 1, 2, 2, 3, 0,
|
|
||||||
// Right
|
|
||||||
1, 5, 6, 6, 2, 1,
|
|
||||||
// Back
|
|
||||||
5, 4, 7, 7, 6, 5,
|
|
||||||
// Left
|
|
||||||
4, 0, 3, 3, 7, 4,
|
|
||||||
// Top
|
|
||||||
3, 2, 6, 6, 7, 3,
|
|
||||||
// Bottom
|
|
||||||
4, 5, 1, 1, 0, 4
|
|
||||||
};
|
|
||||||
|
|
||||||
Mesh cube(vertices, indices);
|
|
||||||
|
|
||||||
// Setup camera (slightly angled view)
|
|
||||||
Camera camera(Vec3(3, 2, 4), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
render_mesh(fb, cube, camera);
|
|
||||||
|
|
||||||
if (fb.save("output_cube.png")) {
|
|
||||||
ARE_LOG_INFO("✓ Saved: output_cube.png");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("✗ Failed to save output_cube.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 4: Cornell Box (corrected)
|
|
||||||
{
|
|
||||||
ARE_LOG_INFO("Rendering Cornell Box...");
|
|
||||||
|
|
||||||
Framebuffer fb(width, height);
|
|
||||||
|
|
||||||
std::vector<Vertex> vertices;
|
|
||||||
std::vector<uint32_t> indices;
|
|
||||||
|
|
||||||
// Helper function to add a quad
|
|
||||||
auto add_quad = [&](const Vec3 &v0, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, const Vec3 &normal) {
|
|
||||||
unsigned int base = vertices.size();
|
|
||||||
vertices.push_back(Vertex(v0, normal));
|
|
||||||
vertices.push_back(Vertex(v1, normal));
|
|
||||||
vertices.push_back(Vertex(v2, normal));
|
|
||||||
vertices.push_back(Vertex(v3, normal));
|
|
||||||
indices.insert(indices.end(), { base + 0, base + 1, base + 2, base + 2, base + 3, base + 0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Floor (white)
|
|
||||||
add_quad(
|
|
||||||
Vec3(-2, -2, 2), Vec3(2, -2, 2),
|
|
||||||
Vec3(2, -2, -2), Vec3(-2, -2, -2),
|
|
||||||
Vec3(0, 1, 0));
|
|
||||||
|
|
||||||
// Ceiling (white)
|
|
||||||
add_quad(
|
|
||||||
Vec3(-2, 2, -2), Vec3(2, 2, -2),
|
|
||||||
Vec3(2, 2, 2), Vec3(-2, 2, 2),
|
|
||||||
Vec3(0, -1, 0));
|
|
||||||
|
|
||||||
// Back wall (white)
|
|
||||||
add_quad(
|
|
||||||
Vec3(-2, -2, -2), Vec3(2, -2, -2),
|
|
||||||
Vec3(2, 2, -2), Vec3(-2, 2, -2),
|
|
||||||
Vec3(0, 0, 1));
|
|
||||||
|
|
||||||
// Left wall (red)
|
|
||||||
add_quad(
|
|
||||||
Vec3(-2, -2, 2), Vec3(-2, -2, -2),
|
|
||||||
Vec3(-2, 2, -2), Vec3(-2, 2, 2),
|
|
||||||
Vec3(1, 0, 0));
|
|
||||||
|
|
||||||
// Right wall (green)
|
|
||||||
add_quad(
|
|
||||||
Vec3(2, -2, -2), Vec3(2, -2, 2),
|
|
||||||
Vec3(2, 2, 2), Vec3(2, 2, -2),
|
|
||||||
Vec3(-1, 0, 0));
|
|
||||||
|
|
||||||
Mesh cornell_box(vertices, indices);
|
|
||||||
|
|
||||||
Camera camera(Vec3(0, 0, 5), Vec3(0, 0, 0));
|
|
||||||
camera.set_perspective(45.0f, (float)width / height, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
render_mesh(fb, cornell_box, camera);
|
|
||||||
|
|
||||||
if (fb.save("output_cornell_box.png")) {
|
|
||||||
ARE_LOG_INFO("✓ Saved: output_cornell_box.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("✓ All images generated successfully!");
|
|
||||||
ARE_LOG_INFO("Check the following files:");
|
|
||||||
ARE_LOG_INFO(" - output_triangle.png");
|
|
||||||
ARE_LOG_INFO(" - output_colored_triangle.png");
|
|
||||||
ARE_LOG_INFO(" - output_cube.png");
|
|
||||||
ARE_LOG_INFO(" - output_cornell_box.png");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
Logger::shutdown();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# examples/03_material_showcase/CMakeLists.txt
|
|
||||||
add_are_example(material_showcase main.cpp)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# Phase 4 verification example
|
|
||||||
|
|
||||||
add_are_example(phase4_test
|
|
||||||
main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy to bin directory for easy execution
|
|
||||||
set_target_properties(phase4_test PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
@ -1,410 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Phase 4 verification program - BVH construction and traversal test
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/acceleration/bvh.h>
|
|
||||||
#include <are/acceleration/bvh_builder.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <random>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace are;
|
|
||||||
|
|
||||||
// Test result tracking
|
|
||||||
struct TestResult {
|
|
||||||
std::string name;
|
|
||||||
bool passed;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<TestResult> test_results;
|
|
||||||
|
|
||||||
void report_test(const std::string &name, bool passed, const std::string &message = "") {
|
|
||||||
test_results.push_back({ name, passed, message });
|
|
||||||
if (passed) {
|
|
||||||
ARE_LOG_INFO("✓ " + name);
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("✗ " + name + ": " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a simple scene with a few triangles
|
|
||||||
*/
|
|
||||||
std::vector<Triangle> create_simple_scene() {
|
|
||||||
std::vector<Triangle> triangles;
|
|
||||||
|
|
||||||
// Ground plane (2 triangles)
|
|
||||||
Vertex v0(Vec3(-5, 0, -5), Vec3(0, 1, 0));
|
|
||||||
Vertex v1(Vec3(5, 0, -5), Vec3(0, 1, 0));
|
|
||||||
Vertex v2(Vec3(5, 0, 5), Vec3(0, 1, 0));
|
|
||||||
Vertex v3(Vec3(-5, 0, 5), Vec3(0, 1, 0));
|
|
||||||
|
|
||||||
triangles.emplace_back(v0, v1, v2);
|
|
||||||
triangles.emplace_back(v0, v2, v3);
|
|
||||||
|
|
||||||
// Cube (12 triangles)
|
|
||||||
Vec3 cube_min(-1, 1, -1);
|
|
||||||
Vec3 cube_max(1, 3, 1);
|
|
||||||
|
|
||||||
// Front face
|
|
||||||
triangles.emplace_back(
|
|
||||||
Vertex(Vec3(cube_min.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(cube_max.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(cube_max.x, cube_max.y, cube_max.z), Vec3(0, 0, 1)));
|
|
||||||
triangles.emplace_back(
|
|
||||||
Vertex(Vec3(cube_min.x, cube_min.y, cube_max.z), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(cube_max.x, cube_max.y, cube_max.z), Vec3(0, 0, 1)),
|
|
||||||
Vertex(Vec3(cube_min.x, cube_max.y, cube_max.z), Vec3(0, 0, 1)));
|
|
||||||
|
|
||||||
// Add more faces... (simplified for brevity)
|
|
||||||
|
|
||||||
return triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a complex scene with many triangles
|
|
||||||
*/
|
|
||||||
std::vector<Triangle> create_complex_scene(int num_triangles) {
|
|
||||||
std::vector<Triangle> triangles;
|
|
||||||
triangles.reserve(num_triangles);
|
|
||||||
|
|
||||||
std::mt19937 rng(42);
|
|
||||||
std::uniform_real_distribution<float> dist(-10.0f, 10.0f);
|
|
||||||
|
|
||||||
for (int i = 0; i < num_triangles; ++i) {
|
|
||||||
Vec3 p0(dist(rng), dist(rng), dist(rng));
|
|
||||||
Vec3 p1 = p0 + Vec3(dist(rng) * 0.5f, dist(rng) * 0.5f, dist(rng) * 0.5f);
|
|
||||||
Vec3 p2 = p0 + Vec3(dist(rng) * 0.5f, dist(rng) * 0.5f, dist(rng) * 0.5f);
|
|
||||||
|
|
||||||
Vec3 normal = glm::normalize(glm::cross(p1 - p0, p2 - p0));
|
|
||||||
|
|
||||||
triangles.emplace_back(
|
|
||||||
Vertex(p0, normal),
|
|
||||||
Vertex(p1, normal),
|
|
||||||
Vertex(p2, normal));
|
|
||||||
}
|
|
||||||
|
|
||||||
return triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 1: BVH construction
|
|
||||||
*/
|
|
||||||
void test_bvh_construction() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
BVHBuildConfig config;
|
|
||||||
config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_MIDDLE;
|
|
||||||
config.max_leaf_size_ = 4;
|
|
||||||
|
|
||||||
bool success = bvh.build(triangles, config);
|
|
||||||
|
|
||||||
report_test("BVH construction (simple scene)", success);
|
|
||||||
report_test("BVH is built", bvh.is_built());
|
|
||||||
report_test("BVH has nodes", !bvh.get_nodes().empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 2: BVH construction with SAH
|
|
||||||
*/
|
|
||||||
void test_bvh_construction_sah() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
BVHBuildConfig config;
|
|
||||||
config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH;
|
|
||||||
config.max_leaf_size_ = 2;
|
|
||||||
|
|
||||||
bool success = bvh.build(triangles, config);
|
|
||||||
|
|
||||||
report_test("BVH construction with SAH", success);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 3: Ray-BVH intersection
|
|
||||||
*/
|
|
||||||
void test_ray_bvh_intersection() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
// Ray hitting the ground plane
|
|
||||||
Ray ray1(Vec3(0, 5, 0), Vec3(0, -1, 0));
|
|
||||||
HitRecord hit1;
|
|
||||||
bool test1 = bvh.intersect(ray1, hit1);
|
|
||||||
|
|
||||||
// Ray missing everything
|
|
||||||
Ray ray2(Vec3(100, 5, 100), Vec3(0, -1, 0));
|
|
||||||
HitRecord hit2;
|
|
||||||
bool test2 = !bvh.intersect(ray2, hit2);
|
|
||||||
|
|
||||||
report_test("Ray-BVH intersection (hit)", test1);
|
|
||||||
report_test("Ray-BVH intersection (miss)", test2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 4: BVH occlusion test
|
|
||||||
*/
|
|
||||||
void test_bvh_occlusion() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
// Ray with occlusion
|
|
||||||
Ray ray1(Vec3(0, 5, 0), Vec3(0, -1, 0));
|
|
||||||
bool test1 = bvh.intersect_any(ray1, 10.0f);
|
|
||||||
|
|
||||||
// Ray without occlusion
|
|
||||||
Ray ray2(Vec3(100, 5, 100), Vec3(0, -1, 0));
|
|
||||||
bool test2 = !bvh.intersect_any(ray2, 10.0f);
|
|
||||||
|
|
||||||
report_test("BVH occlusion test (occluded)", test1);
|
|
||||||
report_test("BVH occlusion test (not occluded)", test2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 5: BVH performance with complex scene
|
|
||||||
*/
|
|
||||||
void test_bvh_performance() {
|
|
||||||
const int num_triangles = 10000;
|
|
||||||
auto triangles = create_complex_scene(num_triangles);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Building BVH for " + std::to_string(num_triangles) + " triangles...");
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
BVHBuildConfig config;
|
|
||||||
config.split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH;
|
|
||||||
|
|
||||||
auto start_build = std::chrono::high_resolution_clock::now();
|
|
||||||
bool success = bvh.build(triangles, config);
|
|
||||||
auto end_build = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
double build_time = std::chrono::duration<double, std::milli>(end_build - start_build).count();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("BVH build time: " + std::to_string(build_time) + " ms");
|
|
||||||
|
|
||||||
// Test ray tracing performance
|
|
||||||
const int num_rays = 10000;
|
|
||||||
std::mt19937 rng(42);
|
|
||||||
std::uniform_real_distribution<float> dist(-10.0f, 10.0f);
|
|
||||||
|
|
||||||
int hit_count = 0;
|
|
||||||
|
|
||||||
auto start_trace = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
for (int i = 0; i < num_rays; ++i) {
|
|
||||||
Vec3 origin(dist(rng), dist(rng), dist(rng));
|
|
||||||
Vec3 direction = glm::normalize(Vec3(dist(rng), dist(rng), dist(rng)));
|
|
||||||
|
|
||||||
Ray ray(origin, direction);
|
|
||||||
HitRecord hit;
|
|
||||||
|
|
||||||
if (bvh.intersect(ray, hit)) {
|
|
||||||
hit_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end_trace = std::chrono::high_resolution_clock::now();
|
|
||||||
double trace_time = std::chrono::duration<double, std::milli>(end_trace - start_trace).count();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Ray tracing time: " + std::to_string(trace_time) + " ms for " + std::to_string(num_rays) + " rays");
|
|
||||||
ARE_LOG_INFO("Hit rate: " + std::to_string(hit_count) + "/" + std::to_string(num_rays) + " (" + std::to_string(100.0 * hit_count / num_rays) + "%)");
|
|
||||||
ARE_LOG_INFO("Average time per ray: " + std::to_string(trace_time / num_rays) + " ms");
|
|
||||||
|
|
||||||
report_test("BVH performance test", success && build_time < 5000.0); // Should build in < 5 seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 6: BVH memory usage
|
|
||||||
*/
|
|
||||||
void test_bvh_memory() {
|
|
||||||
auto triangles = create_complex_scene(1000);
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
size_t memory = bvh.get_memory_usage();
|
|
||||||
ARE_LOG_INFO("BVH memory usage: " + std::to_string(memory / 1024) + " KB");
|
|
||||||
|
|
||||||
report_test("BVH memory usage", memory > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 7: BVH clear and rebuild
|
|
||||||
*/
|
|
||||||
void test_bvh_clear_rebuild() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
bool test1 = bvh.is_built();
|
|
||||||
|
|
||||||
bvh.clear();
|
|
||||||
bool test2 = !bvh.is_built();
|
|
||||||
|
|
||||||
bvh.build(triangles);
|
|
||||||
bool test3 = bvh.is_built();
|
|
||||||
|
|
||||||
report_test("BVH clear and rebuild (initial build)", test1);
|
|
||||||
report_test("BVH clear and rebuild (after clear)", test2);
|
|
||||||
report_test("BVH clear and rebuild (rebuild)", test3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 8: BVH with empty scene
|
|
||||||
*/
|
|
||||||
void test_bvh_empty_scene() {
|
|
||||||
std::vector<Triangle> empty_triangles;
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bool success = bvh.build(empty_triangles);
|
|
||||||
|
|
||||||
report_test("BVH with empty scene", !success);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 9: BVH node structure
|
|
||||||
*/
|
|
||||||
void test_bvh_node_structure() {
|
|
||||||
auto triangles = create_simple_scene();
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
const auto &nodes = bvh.get_nodes();
|
|
||||||
|
|
||||||
bool test1 = !nodes.empty();
|
|
||||||
|
|
||||||
// Check root node
|
|
||||||
bool test2 = nodes[0].bounds_.is_valid();
|
|
||||||
|
|
||||||
// Count leaf and internal nodes
|
|
||||||
int leaf_count = 0;
|
|
||||||
int internal_count = 0;
|
|
||||||
|
|
||||||
for (const auto &node : nodes) {
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
leaf_count++;
|
|
||||||
} else {
|
|
||||||
internal_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test3 = leaf_count > 0;
|
|
||||||
bool test4 = internal_count >= 0;
|
|
||||||
|
|
||||||
ARE_LOG_INFO("BVH structure: " + std::to_string(nodes.size()) + " nodes (" + std::to_string(leaf_count) + " leaves, " + std::to_string(internal_count) + " internal)");
|
|
||||||
|
|
||||||
report_test("BVH node structure (has nodes)", test1);
|
|
||||||
report_test("BVH node structure (valid root)", test2);
|
|
||||||
report_test("BVH node structure (has leaves)", test3);
|
|
||||||
report_test("BVH node structure (node counts)", test4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Test 10: BVH traversal correctness
|
|
||||||
*/
|
|
||||||
void test_bvh_traversal_correctness() {
|
|
||||||
// Create a simple scene with known geometry
|
|
||||||
std::vector<Triangle> triangles;
|
|
||||||
|
|
||||||
// Single triangle at origin
|
|
||||||
Vertex v0(Vec3(-1, 0, -1), Vec3(0, 1, 0));
|
|
||||||
Vertex v1(Vec3(1, 0, -1), Vec3(0, 1, 0));
|
|
||||||
Vertex v2(Vec3(0, 0, 1), Vec3(0, 1, 0));
|
|
||||||
|
|
||||||
triangles.emplace_back(v0, v1, v2);
|
|
||||||
|
|
||||||
BVH bvh;
|
|
||||||
bvh.build(triangles);
|
|
||||||
|
|
||||||
// Ray hitting the triangle from above
|
|
||||||
Ray ray(Vec3(0, 5, 0), Vec3(0, -1, 0));
|
|
||||||
HitRecord hit;
|
|
||||||
|
|
||||||
bool intersected = bvh.intersect(ray, hit);
|
|
||||||
|
|
||||||
bool test1 = intersected;
|
|
||||||
bool test2 = hit.t_ > 0.0f && hit.t_ < 10.0f;
|
|
||||||
bool test3 = glm::length(hit.normal_ - Vec3(0, 1, 0)) < 0.01f;
|
|
||||||
|
|
||||||
report_test("BVH traversal correctness (intersection)", test1);
|
|
||||||
report_test("BVH traversal correctness (t value)", test2);
|
|
||||||
report_test("BVH traversal correctness (normal)", test3);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// Initialize logger and profiler
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
Profiler::init();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Phase 4 Verification Program");
|
|
||||||
ARE_LOG_INFO("BVH Construction and Traversal Test");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
// Run all tests
|
|
||||||
test_bvh_construction();
|
|
||||||
test_bvh_construction_sah();
|
|
||||||
test_ray_bvh_intersection();
|
|
||||||
test_bvh_occlusion();
|
|
||||||
test_bvh_performance();
|
|
||||||
test_bvh_memory();
|
|
||||||
test_bvh_clear_rebuild();
|
|
||||||
test_bvh_empty_scene();
|
|
||||||
test_bvh_node_structure();
|
|
||||||
test_bvh_traversal_correctness();
|
|
||||||
|
|
||||||
// Print summary
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("Test Summary");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
|
|
||||||
int passed = 0;
|
|
||||||
int failed = 0;
|
|
||||||
|
|
||||||
for (const auto &result : test_results) {
|
|
||||||
if (result.passed) {
|
|
||||||
++passed;
|
|
||||||
} else {
|
|
||||||
++failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Total tests: " + std::to_string(test_results.size()));
|
|
||||||
ARE_LOG_INFO("Passed: " + std::to_string(passed));
|
|
||||||
ARE_LOG_INFO("Failed: " + std::to_string(failed));
|
|
||||||
|
|
||||||
if (failed == 0) {
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
ARE_LOG_INFO("✓ All Phase 4 tests passed!");
|
|
||||||
ARE_LOG_INFO("========================================");
|
|
||||||
} else {
|
|
||||||
ARE_LOG_ERROR("========================================");
|
|
||||||
ARE_LOG_ERROR("✗ Some tests failed. Please review.");
|
|
||||||
ARE_LOG_ERROR("========================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print profiling results
|
|
||||||
Profiler::print_results();
|
|
||||||
|
|
||||||
Profiler::shutdown();
|
|
||||||
Logger::shutdown();
|
|
||||||
|
|
||||||
return failed == 0 ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
add_are_example(phase5_test
|
|
||||||
main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(phase5_test PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
@ -1,282 +0,0 @@
|
||||||
/**
|
|
||||||
* @file main.cpp
|
|
||||||
* @brief Phase 5 test: Hybrid GBuffer-driven CPU raytracing with primitive-id
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
#include <are/platform/window.h>
|
|
||||||
#include <are/platform/gl_context.h>
|
|
||||||
|
|
||||||
#include <are/rasterizer/rasterizer.h>
|
|
||||||
#include <are/rasterizer/gbuffer.h>
|
|
||||||
#include <are/rasterizer/shader_program.h>
|
|
||||||
|
|
||||||
#include <are/renderer/geometry_cache.h>
|
|
||||||
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/directional_light.h>
|
|
||||||
#include <are/scene/point_light.h>
|
|
||||||
|
|
||||||
#include <are/raytracer/cpu_raytracer.h>
|
|
||||||
|
|
||||||
#include "../lib/glad/glad/glad.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
using namespace are;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
uint32_t create_fullscreen_quad_vao() {
|
|
||||||
float vertices[] = {
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
|
||||||
1.0f, -1.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 1.0f, 1.0f, 1.0f,
|
|
||||||
-1.0f, 1.0f, 0.0f, 1.0f
|
|
||||||
};
|
|
||||||
uint32_t indices[] = { 0, 1, 2, 2, 3, 0 };
|
|
||||||
|
|
||||||
uint32_t vao = 0, vbo = 0, ebo = 0;
|
|
||||||
glGenVertexArrays(1, &vao);
|
|
||||||
glBindVertexArray(vao);
|
|
||||||
|
|
||||||
glGenBuffers(1, &vbo);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glGenBuffers(1, &ebo);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
return vao;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t create_output_texture_rgba16f(int width, int height) {
|
|
||||||
uint32_t tex = 0;
|
|
||||||
glGenTextures(1, &tex);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 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);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
try {
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
Profiler::init();
|
|
||||||
|
|
||||||
WindowConfig wc;
|
|
||||||
wc.width = 960;
|
|
||||||
wc.height = 540;
|
|
||||||
wc.title = "ARE Phase5 - Hybrid + PrimitiveID";
|
|
||||||
wc.vsync = true;
|
|
||||||
|
|
||||||
Window window(wc);
|
|
||||||
|
|
||||||
if (!GLContext::initialize()) {
|
|
||||||
ARE_LOG_CRITICAL("GLContext initialize failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fb_w = 0, fb_h = 0;
|
|
||||||
for (int i = 0; i < 240; ++i) {
|
|
||||||
window.poll_events();
|
|
||||||
window.get_framebuffer_size(fb_w, fb_h);
|
|
||||||
if (fb_w > 0 && fb_h > 0) break;
|
|
||||||
window.swap_buffers();
|
|
||||||
}
|
|
||||||
if (fb_w <= 0 || fb_h <= 0) {
|
|
||||||
ARE_LOG_CRITICAL("Invalid framebuffer size");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rasterizer rasterizer(fb_w, fb_h);
|
|
||||||
rasterizer.initialize_shaders("shaders/");
|
|
||||||
|
|
||||||
uint32_t output_tex = create_output_texture_rgba16f(fb_w, fb_h);
|
|
||||||
uint32_t quad_vao = create_fullscreen_quad_vao();
|
|
||||||
|
|
||||||
// Display shader (simple)
|
|
||||||
const char* vs = R"(
|
|
||||||
#version 430 core
|
|
||||||
layout(location = 0) in vec2 a_pos;
|
|
||||||
layout(location = 1) in vec2 a_uv;
|
|
||||||
out vec2 v_uv;
|
|
||||||
void main(){ v_uv=a_uv; gl_Position=vec4(a_pos,0,1); }
|
|
||||||
)";
|
|
||||||
const char* fs = R"(
|
|
||||||
#version 430 core
|
|
||||||
in vec2 v_uv;
|
|
||||||
out vec4 frag_color;
|
|
||||||
uniform sampler2D u_tex;
|
|
||||||
void main(){ frag_color = texture(u_tex, v_uv); }
|
|
||||||
)";
|
|
||||||
|
|
||||||
ShaderProgram display;
|
|
||||||
if (!display.compile_shader(ShaderType::ARE_SHADER_VERTEX, vs) ||
|
|
||||||
!display.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, fs) ||
|
|
||||||
!display.link()) {
|
|
||||||
ARE_LOG_CRITICAL("Display shader compile failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scene setup
|
|
||||||
SceneManager scene;
|
|
||||||
|
|
||||||
Material mat;
|
|
||||||
mat.set_albedo(Vec3(0.8f, 0.2f, 0.2f));
|
|
||||||
mat.set_roughness(0.6f);
|
|
||||||
MaterialHandle mat_h = scene.add_material(mat);
|
|
||||||
|
|
||||||
std::vector<Vertex> ground_v = {
|
|
||||||
Vertex(Vec3(-4, 0, -4), Vec3(0, 1, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3( 4, 0, -4), Vec3(0, 1, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3( 4, 0, 4), Vec3(0, 1, 0), Vec2(1, 1)),
|
|
||||||
Vertex(Vec3(-4, 0, 4), Vec3(0, 1, 0), Vec2(0, 1)),
|
|
||||||
};
|
|
||||||
std::vector<uint32_t> ground_i = { 0, 1, 2, 2, 3, 0 };
|
|
||||||
Mesh ground(ground_v, ground_i, mat_h);
|
|
||||||
ground.compute_tangents();
|
|
||||||
|
|
||||||
std::vector<Vertex> tri_v = {
|
|
||||||
Vertex(Vec3(-0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(0, 0)),
|
|
||||||
Vertex(Vec3( 0.8f, 0.2f, 0.0f), Vec3(0, 1, 0), Vec2(1, 0)),
|
|
||||||
Vertex(Vec3( 0.0f, 1.2f, 0.3f), Vec3(0, 1, 0), Vec2(0.5f, 1)),
|
|
||||||
};
|
|
||||||
std::vector<uint32_t> tri_i = { 0, 1, 2 };
|
|
||||||
Mesh tri_mesh(tri_v, tri_i, mat_h);
|
|
||||||
tri_mesh.compute_tangents();
|
|
||||||
|
|
||||||
// Upload meshes BEFORE adding (SceneManager stores copy)
|
|
||||||
rasterizer.upload_mesh(ground);
|
|
||||||
scene.add_mesh(ground);
|
|
||||||
|
|
||||||
rasterizer.upload_mesh(tri_mesh);
|
|
||||||
scene.add_mesh(tri_mesh);
|
|
||||||
|
|
||||||
auto sun = std::make_shared<DirectionalLight>(Vec3(-1, -1, -0.5f), Vec3(1.0f), 2.0f);
|
|
||||||
sun->set_cast_shadows(true);
|
|
||||||
scene.add_light(sun);
|
|
||||||
|
|
||||||
auto point = std::make_shared<PointLight>(Vec3(0, 2.5f, 1.5f), Vec3(1.0f, 0.95f, 0.8f), 10.0f, 10.0f);
|
|
||||||
point->set_cast_shadows(true);
|
|
||||||
scene.add_light(point);
|
|
||||||
|
|
||||||
Camera camera(Vec3(0.0f, 1.8f, 4.5f), Vec3(0.0f, 0.6f, 0.0f));
|
|
||||||
camera.set_perspective(45.0f, static_cast<Real>(fb_w) / static_cast<Real>(fb_h), 0.1f, 200.0f);
|
|
||||||
|
|
||||||
// Geometry cache: single source of truth
|
|
||||||
GeometryCache geom;
|
|
||||||
if (!geom.build_from_scene(scene)) {
|
|
||||||
ARE_LOG_CRITICAL("GeometryCache build failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provide triangle base offsets to rasterizer for primitive id output
|
|
||||||
rasterizer.set_triangle_base_provider([&](size_t mesh_index) {
|
|
||||||
return geom.get_mesh_triangle_base(mesh_index);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CPU ray tracer uses the same BVH (same triangle layout)
|
|
||||||
RayTracingConfig rtc;
|
|
||||||
rtc.backend = RayTracingBackend::ARE_RT_BACKEND_CPU;
|
|
||||||
rtc.spp = 1;
|
|
||||||
rtc.max_depth = 3;
|
|
||||||
rtc.enable_gi = false;
|
|
||||||
rtc.enable_ao = false;
|
|
||||||
rtc.ao_samples = 4;
|
|
||||||
rtc.ao_radius = 1.0f;
|
|
||||||
|
|
||||||
CPURayTracer tracer(rtc);
|
|
||||||
tracer.update_bvh(geom.get_bvh());
|
|
||||||
|
|
||||||
bool request_render = true;
|
|
||||||
|
|
||||||
while (!window.should_close()) {
|
|
||||||
window.poll_events();
|
|
||||||
|
|
||||||
if (window.is_key_pressed(256)) {
|
|
||||||
window.set_should_close(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int new_fb_w = 0, new_fb_h = 0;
|
|
||||||
window.get_framebuffer_size(new_fb_w, new_fb_h);
|
|
||||||
if (new_fb_w <= 0 || new_fb_h <= 0) {
|
|
||||||
window.swap_buffers();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_fb_w != fb_w || new_fb_h != fb_h) {
|
|
||||||
fb_w = new_fb_w;
|
|
||||||
fb_h = new_fb_h;
|
|
||||||
|
|
||||||
rasterizer.resize(fb_w, fb_h);
|
|
||||||
glDeleteTextures(1, &output_tex);
|
|
||||||
output_tex = create_output_texture_rgba16f(fb_w, fb_h);
|
|
||||||
camera.set_aspect_ratio(static_cast<Real>(fb_w) / static_cast<Real>(fb_h));
|
|
||||||
|
|
||||||
request_render = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request_render) {
|
|
||||||
rasterizer.render_gbuffer(scene, camera);
|
|
||||||
tracer.render(scene, camera, &rasterizer.get_gbuffer(), output_tex);
|
|
||||||
request_render = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
glViewport(0, 0, fb_w, fb_h);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
display.use();
|
|
||||||
display.set_uniform("u_tex", 0);
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, output_tex);
|
|
||||||
|
|
||||||
glBindVertexArray(quad_vao);
|
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
window.swap_buffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
glDeleteTextures(1, &output_tex);
|
|
||||||
glDeleteVertexArrays(1, &quad_vao);
|
|
||||||
|
|
||||||
Profiler::shutdown();
|
|
||||||
Logger::shutdown();
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
ARE_LOG_CRITICAL(std::string("Unhandled exception: ") + e.what());
|
|
||||||
Logger::shutdown();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# examples/04_performance_test/CMakeLists.txt
|
|
||||||
add_are_example(performance_test main.cpp)
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# examples/CMakeLists.txt
|
|
||||||
|
|
||||||
# Helper function to create example targets
|
|
||||||
function(add_are_example EXAMPLE_NAME)
|
|
||||||
add_executable(${EXAMPLE_NAME} ${ARGN})
|
|
||||||
target_link_libraries(${EXAMPLE_NAME} PRIVATE are)
|
|
||||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Add subdirectories for each example
|
|
||||||
add_subdirectory(01_hello_triangle)
|
|
||||||
add_subdirectory(02_cornell_box)
|
|
||||||
add_subdirectory(03_material_showcase)
|
|
||||||
add_subdirectory(04_performance_test)
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh.h
|
|
||||||
* @brief BVH interface and traversal
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
|
|
||||||
#include <are/acceleration/bvh_builder.h>
|
|
||||||
#include <are/acceleration/bvh_node.h>
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class BVH
|
|
||||||
* @brief Bounding Volume Hierarchy for ray tracing acceleration
|
|
||||||
*/
|
|
||||||
class BVH {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
BVH();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~BVH();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Build BVH from triangle list
|
|
||||||
* @param triangles Triangle list
|
|
||||||
* @param config Build configuration
|
|
||||||
* @return true if build succeeded
|
|
||||||
*/
|
|
||||||
bool build(const std::vector<Triangle> &triangles,
|
|
||||||
const BVHBuildConfig &config = BVHBuildConfig());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Traverse BVH and find closest intersection
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param hit Output hit record
|
|
||||||
* @return true if intersection found
|
|
||||||
*/
|
|
||||||
bool intersect(const Ray &ray, HitRecord &hit) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Fast occlusion test (any hit)
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
* @return true if any intersection found
|
|
||||||
*/
|
|
||||||
bool intersect_any(const Ray &ray, Real t_max) const;
|
|
||||||
/**
|
|
||||||
* @brief Fast occlusion test (any hit), with ignored triangle id
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
* @param ignore_triangle_index Triangle index to ignore (e.g. self primitive id)
|
|
||||||
* @return true if any intersection found (excluding ignored triangle)
|
|
||||||
*/
|
|
||||||
bool intersect_any(const Ray &ray, Real t_max, uint32_t ignore_triangle_index) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if BVH is built
|
|
||||||
* @return true if built
|
|
||||||
*/
|
|
||||||
bool is_built() const {
|
|
||||||
return !nodes_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get BVH nodes (for GPU upload)
|
|
||||||
* @return Node array
|
|
||||||
*/
|
|
||||||
const std::vector<BVHNode> &get_nodes() const {
|
|
||||||
return nodes_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get primitive indices
|
|
||||||
* @return Index array
|
|
||||||
*/
|
|
||||||
const std::vector<uint32_t> &get_primitive_indices() const {
|
|
||||||
return primitive_indices_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangles
|
|
||||||
* @return Triangle array
|
|
||||||
*/
|
|
||||||
const std::vector<Triangle> &get_triangles() const {
|
|
||||||
return triangles_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get memory usage in bytes
|
|
||||||
* @return Memory usage
|
|
||||||
*/
|
|
||||||
size_t get_memory_usage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear BVH data
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Recursive traversal (kept for reference)
|
|
||||||
bool intersect_recursive(uint32_t node_index, const Ray &ray, HitRecord &hit) const;
|
|
||||||
bool intersect_any_recursive(uint32_t node_index, const Ray &ray, Real t_max) const;
|
|
||||||
|
|
||||||
// Optimized iterative traversal
|
|
||||||
bool intersect_iterative(const Ray &ray, HitRecord &hit) const;
|
|
||||||
bool intersect_any_iterative(const Ray &ray, Real t_max) const;
|
|
||||||
|
|
||||||
// Fast intersection helpers
|
|
||||||
inline bool intersect_aabb_fast(const AABB &bounds, const Ray &ray,
|
|
||||||
const Vec3 &inv_dir, Real t_max,
|
|
||||||
Real &t_min_out, Real &t_max_out) const;
|
|
||||||
inline bool intersect_triangle_fast(const Triangle &triangle, const Ray &ray,
|
|
||||||
Real t_max, HitRecord &hit) const;
|
|
||||||
|
|
||||||
std::vector<BVHNode> nodes_; ///< BVH nodes
|
|
||||||
std::vector<uint32_t> primitive_indices_; ///< Primitive index array
|
|
||||||
std::vector<Triangle> triangles_; ///< Triangle data
|
|
||||||
uint32_t root_index_; ///< Root node index
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh_builder.h
|
|
||||||
* @brief BVH construction algorithms
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/acceleration/bvh_node.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum BVHSplitMethod
|
|
||||||
* @brief BVH splitting strategies
|
|
||||||
*/
|
|
||||||
enum class BVHSplitMethod {
|
|
||||||
ARE_BVH_SPLIT_MIDDLE, ///< Split at midpoint
|
|
||||||
ARE_BVH_SPLIT_SAH ///< Surface Area Heuristic
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct BVHBuildConfig
|
|
||||||
* @brief Configuration for BVH construction
|
|
||||||
*/
|
|
||||||
struct BVHBuildConfig {
|
|
||||||
BVHSplitMethod split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH;
|
|
||||||
int max_leaf_size_ = 4; ///< Maximum triangles per leaf
|
|
||||||
int max_depth_ = 64; ///< Maximum tree depth
|
|
||||||
bool use_multithreading_ = true; ///< Use parallel construction
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class BVHBuilder
|
|
||||||
* @brief Constructs BVH from triangle list
|
|
||||||
*/
|
|
||||||
class BVHBuilder {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Build configuration
|
|
||||||
*/
|
|
||||||
explicit BVHBuilder(const BVHBuildConfig& config = BVHBuildConfig());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Build BVH from triangles
|
|
||||||
* @param triangles Triangle list
|
|
||||||
* @param nodes Output node list
|
|
||||||
* @param primitive_indices Output primitive index list
|
|
||||||
* @return Root node index
|
|
||||||
*/
|
|
||||||
uint32_t build(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get build statistics
|
|
||||||
* @param node_count Output node count
|
|
||||||
* @param leaf_count Output leaf count
|
|
||||||
* @param max_depth Output maximum depth reached
|
|
||||||
*/
|
|
||||||
void get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct BuildEntry {
|
|
||||||
uint32_t parent_;
|
|
||||||
uint32_t start_;
|
|
||||||
uint32_t end_;
|
|
||||||
int depth_;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t build_recursive(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices,
|
|
||||||
uint32_t start, uint32_t end, int depth);
|
|
||||||
|
|
||||||
int find_best_split_axis(const std::vector<Triangle>& triangles,
|
|
||||||
const std::vector<uint32_t>& indices,
|
|
||||||
uint32_t start, uint32_t end);
|
|
||||||
|
|
||||||
Real compute_sah_cost(const AABB& bounds, uint32_t count);
|
|
||||||
|
|
||||||
BVHBuildConfig config_;
|
|
||||||
size_t node_count_;
|
|
||||||
size_t leaf_count_;
|
|
||||||
int max_depth_reached_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh_node.h
|
|
||||||
* @brief BVH node structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct BVHNode
|
|
||||||
* @brief Node in Bounding Volume Hierarchy
|
|
||||||
*
|
|
||||||
* Uses a compact representation for efficient GPU transfer.
|
|
||||||
*/
|
|
||||||
struct BVHNode {
|
|
||||||
AABB bounds_; ///< Node bounding box
|
|
||||||
|
|
||||||
union {
|
|
||||||
uint32_t left_child_; ///< Left child index (internal node)
|
|
||||||
uint32_t first_primitive_; ///< First primitive index (leaf node)
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
uint32_t right_child_; ///< Right child index (internal node)
|
|
||||||
uint32_t primitive_count_; ///< Number of primitives (leaf node)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if node is a leaf
|
|
||||||
* @return true if leaf node
|
|
||||||
*/
|
|
||||||
bool is_leaf() const {
|
|
||||||
return primitive_count_ > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get node surface area (for SAH)
|
|
||||||
* @return Surface area
|
|
||||||
*/
|
|
||||||
Real surface_area() const {
|
|
||||||
return bounds_.surface_area();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
/**
|
|
||||||
* @file are.h
|
|
||||||
* @brief Aurora Rendering Engine - Main header file
|
|
||||||
*
|
|
||||||
* This header includes all public interfaces of the Aurora Rendering Engine.
|
|
||||||
* Users only need to include this single header to access all functionality.
|
|
||||||
*
|
|
||||||
* @author Ternary_Operator
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ARE_H
|
|
||||||
#define ARE_INCLUDE_ARE_H
|
|
||||||
|
|
||||||
// Core modules
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
// Platform modules
|
|
||||||
#include <are/platform/window.h>
|
|
||||||
#include <are/platform/gl_context.h>
|
|
||||||
|
|
||||||
// Geometry modules
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
#include <are/geometry/transform.h>
|
|
||||||
|
|
||||||
// Scene modules
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
#include <are/scene/directional_light.h>
|
|
||||||
#include <are/scene/point_light.h>
|
|
||||||
#include <are/scene/spot_light.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
|
|
||||||
// Acceleration modules
|
|
||||||
#include <are/acceleration/bvh.h>
|
|
||||||
|
|
||||||
// Renderer modules
|
|
||||||
#include <are/renderer/renderer.h>
|
|
||||||
#include <are/renderer/render_stats.h>
|
|
||||||
|
|
||||||
// Utility modules
|
|
||||||
#include <are/utils/image_io.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @namespace are
|
|
||||||
* @brief Main namespace for Aurora Rendering Engine
|
|
||||||
*/
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the version string of the engine
|
|
||||||
* @return Version string in format "major.minor.patch"
|
|
||||||
*/
|
|
||||||
const char* get_version();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the Aurora Rendering Engine
|
|
||||||
* @return true if initialization succeeded, false otherwise
|
|
||||||
*/
|
|
||||||
bool initialize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shutdown the Aurora Rendering Engine
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ARE_H
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/**
|
|
||||||
* @file config.h
|
|
||||||
* @brief Configuration system for the rendering engine
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_CORE_CONFIG_H
|
|
||||||
#define ARE_INCLUDE_CORE_CONFIG_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct WindowConfig
|
|
||||||
* @brief Configuration for window creation
|
|
||||||
*/
|
|
||||||
struct WindowConfig {
|
|
||||||
int width = 1280; ///< Window width in pixels
|
|
||||||
int height = 720; ///< Window height in pixels
|
|
||||||
std::string title = "Aurora Rendering Engine"; ///< Window title
|
|
||||||
bool resizable = true; ///< Whether window is resizable
|
|
||||||
bool vsync = true; ///< Enable vertical synchronization
|
|
||||||
int samples = 1; ///< MSAA samples (1 = disabled)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct RayTracingConfig
|
|
||||||
* @brief Configuration for ray tracing
|
|
||||||
*/
|
|
||||||
struct RayTracingConfig {
|
|
||||||
RayTracingBackend backend = RayTracingBackend::ARE_RT_BACKEND_COMPUTE_SHADER;
|
|
||||||
int spp = 64; ///< Samples per pixel
|
|
||||||
int max_depth = 8; ///< Maximum ray bounce depth
|
|
||||||
bool enable_gi = true; ///< Enable global illumination
|
|
||||||
bool enable_ao = true; ///< Enable ambient occlusion
|
|
||||||
bool enable_soft_shadows = false; ///< Enable soft shadows
|
|
||||||
int ao_samples = 16; ///< AO sample count
|
|
||||||
Real ao_radius = 1.0f; ///< AO sampling radius
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct RenderConfig
|
|
||||||
* @brief General rendering configuration
|
|
||||||
*/
|
|
||||||
struct RenderConfig {
|
|
||||||
ToneMappingOperator tonemap_op = ToneMappingOperator::ARE_TONEMAP_ACES;
|
|
||||||
Real exposure = 1.0f; ///< Exposure value for tone mapping
|
|
||||||
bool use_hdr = true; ///< Use HDR rendering pipeline
|
|
||||||
GBufferVisualizationMode gbuffer_vis_mode = GBufferVisualizationMode::ARE_GBUFFER_VIS_NONE;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct PerformanceConfig
|
|
||||||
* @brief Performance-related configuration
|
|
||||||
*/
|
|
||||||
struct PerformanceConfig {
|
|
||||||
int num_threads = 0; ///< Number of threads (0 = auto-detect)
|
|
||||||
bool enable_bvh_multithreading = true; ///< Use multithreading for BVH construction
|
|
||||||
bool enable_profiling = false; ///< Enable performance profiling
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct PathConfig
|
|
||||||
* @brief File path configuration
|
|
||||||
*/
|
|
||||||
struct PathConfig {
|
|
||||||
std::string shader_dir = "shaders/"; ///< Directory containing shader files
|
|
||||||
std::string texture_dir = "textures/"; ///< Default texture directory
|
|
||||||
std::string output_dir = "output/"; ///< Default output directory
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class AreConfig
|
|
||||||
* @brief Main configuration class for Aurora Rendering Engine
|
|
||||||
*/
|
|
||||||
class AreConfig {
|
|
||||||
public:
|
|
||||||
WindowConfig window; ///< Window configuration
|
|
||||||
RayTracingConfig ray_tracing; ///< Ray tracing configuration
|
|
||||||
RenderConfig render; ///< Rendering configuration
|
|
||||||
PerformanceConfig performance; ///< Performance configuration
|
|
||||||
PathConfig paths; ///< Path configuration
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructor with default values
|
|
||||||
*/
|
|
||||||
AreConfig() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Validate configuration values
|
|
||||||
* @return true if configuration is valid, false otherwise
|
|
||||||
*/
|
|
||||||
bool validate() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print configuration to console
|
|
||||||
*/
|
|
||||||
void print() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_CORE_CONFIG_H
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
/**
|
|
||||||
* @file logger.h
|
|
||||||
* @brief Logging system for the rendering engine
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_CORE_LOGGER_H
|
|
||||||
#define ARE_INCLUDE_CORE_LOGGER_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum LogLevel
|
|
||||||
* @brief Logging severity levels
|
|
||||||
*/
|
|
||||||
enum class LogLevel {
|
|
||||||
ARE_LOG_TRACE,
|
|
||||||
ARE_LOG_DEBUG,
|
|
||||||
ARE_LOG_INFO,
|
|
||||||
ARE_LOG_WARN,
|
|
||||||
ARE_LOG_ERROR,
|
|
||||||
ARE_LOG_CRITICAL
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Logger
|
|
||||||
* @brief Thread-safe logging system
|
|
||||||
*
|
|
||||||
* This class provides a simple interface for logging messages with different
|
|
||||||
* severity levels. It wraps spdlog for actual logging functionality.
|
|
||||||
*/
|
|
||||||
class Logger {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Initialize the logging system
|
|
||||||
* @param min_level Minimum log level to display
|
|
||||||
*/
|
|
||||||
static void init(LogLevel min_level = LogLevel::ARE_LOG_INFO);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shutdown the logging system
|
|
||||||
*/
|
|
||||||
static void shutdown();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Log a message with file/function/line information
|
|
||||||
* @param level Log severity level
|
|
||||||
* @param file Source file name
|
|
||||||
* @param func Function name
|
|
||||||
* @param line Line number
|
|
||||||
* @param message Log message
|
|
||||||
*/
|
|
||||||
static void log(LogLevel level, const char* file, const char* func,
|
|
||||||
int line, const std::string& message);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set minimum log level
|
|
||||||
* @param level Minimum log level to display
|
|
||||||
*/
|
|
||||||
static void set_level(LogLevel level);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::shared_ptr<void> logger_impl_; ///< Internal logger implementation
|
|
||||||
static bool initialized_; ///< Initialization flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
// Logging macros
|
|
||||||
#define ARE_LOG_TRACE(msg) are::Logger::log(are::LogLevel::ARE_LOG_TRACE, __FILE__, __func__, __LINE__, msg)
|
|
||||||
#define ARE_LOG_DEBUG(msg) are::Logger::log(are::LogLevel::ARE_LOG_DEBUG, __FILE__, __func__, __LINE__, msg)
|
|
||||||
#define ARE_LOG_INFO(msg) are::Logger::log(are::LogLevel::ARE_LOG_INFO, __FILE__, __func__, __LINE__, msg)
|
|
||||||
#define ARE_LOG_WARN(msg) are::Logger::log(are::LogLevel::ARE_LOG_WARN, __FILE__, __func__, __LINE__, msg)
|
|
||||||
#define ARE_LOG_ERROR(msg) are::Logger::log(are::LogLevel::ARE_LOG_ERROR, __FILE__, __func__, __LINE__, msg)
|
|
||||||
#define ARE_LOG_CRITICAL(msg) are::Logger::log(are::LogLevel::ARE_LOG_CRITICAL, __FILE__, __func__, __LINE__, msg)
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_CORE_LOGGER_H
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
/**
|
|
||||||
* @file profiler.h
|
|
||||||
* @brief Performance profiling utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_CORE_PROFILER_H
|
|
||||||
#define ARE_INCLUDE_CORE_PROFILER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct ProfileResult
|
|
||||||
* @brief Result of a profiling measurement
|
|
||||||
*/
|
|
||||||
struct ProfileResult {
|
|
||||||
std::string name_; ///< Profile section name
|
|
||||||
double duration_ms_; ///< Duration in milliseconds
|
|
||||||
uint64_t call_count_; ///< Number of times called
|
|
||||||
double avg_duration_ms_; ///< Average duration per call
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Profiler
|
|
||||||
* @brief Simple performance profiler
|
|
||||||
*
|
|
||||||
* This class provides basic timing functionality for performance analysis.
|
|
||||||
* It is only active when ARE_ENABLE_PROFILING is defined.
|
|
||||||
*/
|
|
||||||
class Profiler {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Initialize the profiler
|
|
||||||
*/
|
|
||||||
static void init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shutdown the profiler and print results
|
|
||||||
*/
|
|
||||||
static void shutdown();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Begin a profiling section
|
|
||||||
* @param name Section name
|
|
||||||
*/
|
|
||||||
static void begin(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief End a profiling section
|
|
||||||
* @param name Section name
|
|
||||||
*/
|
|
||||||
static void end(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get profiling results
|
|
||||||
* @return Map of section names to profile results
|
|
||||||
*/
|
|
||||||
static const std::unordered_map<std::string, ProfileResult>& get_results();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reset all profiling data
|
|
||||||
*/
|
|
||||||
static void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print profiling results to console
|
|
||||||
*/
|
|
||||||
static void print_results();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct SectionData {
|
|
||||||
std::chrono::high_resolution_clock::time_point start_time_;
|
|
||||||
double total_duration_ms_ = 0.0;
|
|
||||||
uint64_t call_count_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unordered_map<std::string, SectionData> sections_;
|
|
||||||
static std::unordered_map<std::string, ProfileResult> results_;
|
|
||||||
static bool enabled_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ScopedProfiler
|
|
||||||
* @brief RAII-style profiler for automatic timing
|
|
||||||
*/
|
|
||||||
class ScopedProfiler {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor - begins profiling
|
|
||||||
* @param name Section name
|
|
||||||
*/
|
|
||||||
explicit ScopedProfiler(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor - ends profiling
|
|
||||||
*/
|
|
||||||
~ScopedProfiler();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string name_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
// Profiling macros
|
|
||||||
#ifdef ARE_ENABLE_PROFILING
|
|
||||||
#define ARE_PROFILE_BEGIN(name) are::Profiler::begin(name)
|
|
||||||
#define ARE_PROFILE_END(name) are::Profiler::end(name)
|
|
||||||
#define ARE_PROFILE_SCOPE(name) are::ScopedProfiler are_profiler_##__LINE__(name)
|
|
||||||
#define ARE_PROFILE_FUNCTION() ARE_PROFILE_SCOPE(__func__)
|
|
||||||
#else
|
|
||||||
#define ARE_PROFILE_BEGIN(name) ((void)0)
|
|
||||||
#define ARE_PROFILE_END(name) ((void)0)
|
|
||||||
#define ARE_PROFILE_SCOPE(name) ((void)0)
|
|
||||||
#define ARE_PROFILE_FUNCTION() ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_CORE_PROFILER_H
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
* @file types.h
|
|
||||||
* @brief Basic type definitions and constants
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_CORE_TYPES_H
|
|
||||||
#define ARE_INCLUDE_CORE_TYPES_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Floating point precision
|
|
||||||
using Real = float;
|
|
||||||
|
|
||||||
// Common vector types
|
|
||||||
using Vec2 = glm::vec2;
|
|
||||||
using Vec3 = glm::vec3;
|
|
||||||
using Vec4 = glm::vec4;
|
|
||||||
|
|
||||||
using Vec2i = glm::ivec2;
|
|
||||||
using Vec3i = glm::ivec3;
|
|
||||||
using Vec4i = glm::ivec4;
|
|
||||||
|
|
||||||
// Matrix types
|
|
||||||
using Mat3 = glm::mat3;
|
|
||||||
using Mat4 = glm::mat4;
|
|
||||||
|
|
||||||
// Color type (RGBA)
|
|
||||||
using Color = Vec4;
|
|
||||||
|
|
||||||
// Handle types for resource management
|
|
||||||
using MeshHandle = uint32_t;
|
|
||||||
using MaterialHandle = uint32_t;
|
|
||||||
using LightHandle = uint32_t;
|
|
||||||
using TextureHandle = uint32_t;
|
|
||||||
|
|
||||||
// Invalid handle constant
|
|
||||||
constexpr uint32_t are_invalid_handle = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
// Mathematical constants
|
|
||||||
constexpr Real are_pi = 3.14159265358979323846f;
|
|
||||||
constexpr Real are_two_pi = 6.28318530717958647692f;
|
|
||||||
constexpr Real are_inv_pi = 0.31830988618379067154f;
|
|
||||||
constexpr Real are_inv_two_pi = 0.15915494309189533577f;
|
|
||||||
constexpr Real are_epsilon = 1e-6f;
|
|
||||||
|
|
||||||
// Ray tracing backend types
|
|
||||||
enum class RayTracingBackend {
|
|
||||||
ARE_RT_BACKEND_CPU,
|
|
||||||
ARE_RT_BACKEND_COMPUTE_SHADER
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tone mapping operators
|
|
||||||
enum class ToneMappingOperator {
|
|
||||||
ARE_TONEMAP_NONE,
|
|
||||||
ARE_TONEMAP_REINHARD,
|
|
||||||
ARE_TONEMAP_ACES
|
|
||||||
};
|
|
||||||
|
|
||||||
// G-Buffer visualization modes
|
|
||||||
enum class GBufferVisualizationMode {
|
|
||||||
ARE_GBUFFER_VIS_NONE,
|
|
||||||
ARE_GBUFFER_VIS_POSITION,
|
|
||||||
ARE_GBUFFER_VIS_NORMAL,
|
|
||||||
ARE_GBUFFER_VIS_ALBEDO,
|
|
||||||
ARE_GBUFFER_VIS_METALLIC,
|
|
||||||
ARE_GBUFFER_VIS_ROUGHNESS,
|
|
||||||
ARE_GBUFFER_VIS_DEPTH
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_CORE_TYPES_H
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
/**
|
|
||||||
* @file aabb.h
|
|
||||||
* @brief Axis-Aligned Bounding Box implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_GEOMETRY_AABB_H
|
|
||||||
#define ARE_INCLUDE_GEOMETRY_AABB_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
struct Ray;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class AABB
|
|
||||||
* @brief Axis-Aligned Bounding Box for spatial queries
|
|
||||||
*
|
|
||||||
* Used for BVH construction and ray intersection acceleration.
|
|
||||||
*/
|
|
||||||
class AABB {
|
|
||||||
public:
|
|
||||||
Vec3 min_; ///< Minimum corner
|
|
||||||
Vec3 max_; ///< Maximum corner
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor - creates invalid AABB
|
|
||||||
*/
|
|
||||||
AABB();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct AABB from min and max corners
|
|
||||||
* @param min Minimum corner
|
|
||||||
* @param max Maximum corner
|
|
||||||
*/
|
|
||||||
AABB(const Vec3& min, const Vec3& max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct AABB containing a single point
|
|
||||||
* @param point Point to contain
|
|
||||||
*/
|
|
||||||
explicit AABB(const Vec3& point);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if AABB is valid (min <= max)
|
|
||||||
* @return true if valid
|
|
||||||
*/
|
|
||||||
bool is_valid() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get center of AABB
|
|
||||||
* @return Center point
|
|
||||||
*/
|
|
||||||
Vec3 center() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get size (extent) of AABB
|
|
||||||
* @return Size vector
|
|
||||||
*/
|
|
||||||
Vec3 size() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get surface area of AABB
|
|
||||||
* @return Surface area
|
|
||||||
*/
|
|
||||||
Real surface_area() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get volume of AABB
|
|
||||||
* @return Volume
|
|
||||||
*/
|
|
||||||
Real volume() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get longest axis index (0=x, 1=y, 2=z)
|
|
||||||
* @return Axis index
|
|
||||||
*/
|
|
||||||
int longest_axis() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Expand AABB to include a point
|
|
||||||
* @param point Point to include
|
|
||||||
*/
|
|
||||||
void expand(const Vec3& point);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Expand AABB to include another AABB
|
|
||||||
* @param other AABB to include
|
|
||||||
*/
|
|
||||||
void expand(const AABB& other);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if AABB contains a point
|
|
||||||
* @param point Point to check
|
|
||||||
* @return true if point is inside AABB
|
|
||||||
*/
|
|
||||||
bool contains(const Vec3& point) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if AABB intersects another AABB
|
|
||||||
* @param other AABB to check
|
|
||||||
* @return true if AABBs intersect
|
|
||||||
*/
|
|
||||||
bool intersects(const AABB& other) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Ray-AABB intersection test
|
|
||||||
* @param ray Ray to test
|
|
||||||
* @param t_min Minimum t value (output)
|
|
||||||
* @param t_max Maximum t value (output)
|
|
||||||
* @return true if ray intersects AABB
|
|
||||||
*/
|
|
||||||
bool intersect_ray(const Ray& ray, Real& t_min, Real& t_max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Merge two AABBs
|
|
||||||
* @param a First AABB
|
|
||||||
* @param b Second AABB
|
|
||||||
* @return Merged AABB containing both
|
|
||||||
*/
|
|
||||||
static AABB merge(const AABB& a, const AABB& b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create invalid AABB (for initialization)
|
|
||||||
* @return Invalid AABB
|
|
||||||
*/
|
|
||||||
static AABB invalid();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_GEOMETRY_AABB_H
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
/**
|
|
||||||
* @file transform.h
|
|
||||||
* @brief Transformation matrix utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_GEOMETRY_TRANSFORM_H
|
|
||||||
#define ARE_INCLUDE_GEOMETRY_TRANSFORM_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Transform
|
|
||||||
* @brief 3D transformation (position, rotation, scale)
|
|
||||||
*
|
|
||||||
* Provides convenient interface for building transformation matrices.
|
|
||||||
*/
|
|
||||||
class Transform {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor (identity transform)
|
|
||||||
*/
|
|
||||||
Transform();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct from position, rotation, and scale
|
|
||||||
* @param position Translation
|
|
||||||
* @param rotation Rotation (Euler angles in radians)
|
|
||||||
* @param scale Scale factors
|
|
||||||
*/
|
|
||||||
Transform(const Vec3& position, const Vec3& rotation, const Vec3& scale);
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
void set_rotation(const Vec3& rotation);
|
|
||||||
void set_scale(const Vec3& scale);
|
|
||||||
void set_scale(Real uniform_scale);
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
const Vec3& get_rotation() const { return rotation_; }
|
|
||||||
const Vec3& get_scale() const { return scale_; }
|
|
||||||
|
|
||||||
// Matrix operations
|
|
||||||
Mat4 get_matrix() const;
|
|
||||||
Mat4 get_inverse_matrix() const;
|
|
||||||
Mat3 get_normal_matrix() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Transform a point
|
|
||||||
* @param point Point to transform
|
|
||||||
* @return Transformed point
|
|
||||||
*/
|
|
||||||
Vec3 transform_point(const Vec3& point) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Transform a direction (ignores translation)
|
|
||||||
* @param direction Direction to transform
|
|
||||||
* @return Transformed direction
|
|
||||||
*/
|
|
||||||
Vec3 transform_direction(const Vec3& direction) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Transform a normal (uses inverse transpose)
|
|
||||||
* @param normal Normal to transform
|
|
||||||
* @return Transformed normal
|
|
||||||
*/
|
|
||||||
Vec3 transform_normal(const Vec3& normal) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Combine two transforms
|
|
||||||
* @param other Other transform
|
|
||||||
* @return Combined transform
|
|
||||||
*/
|
|
||||||
Transform operator*(const Transform& other) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create identity transform
|
|
||||||
* @return Identity transform
|
|
||||||
*/
|
|
||||||
static Transform identity();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create translation transform
|
|
||||||
* @param translation Translation vector
|
|
||||||
* @return Translation transform
|
|
||||||
*/
|
|
||||||
static Transform translate(const Vec3& translation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create rotation transform
|
|
||||||
* @param rotation Rotation (Euler angles in radians)
|
|
||||||
* @return Rotation transform
|
|
||||||
*/
|
|
||||||
static Transform rotate(const Vec3& rotation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create scale transform
|
|
||||||
* @param scale Scale factors
|
|
||||||
* @return Scale transform
|
|
||||||
*/
|
|
||||||
static Transform scale(const Vec3& scale);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void mark_dirty();
|
|
||||||
void update_matrix() const;
|
|
||||||
|
|
||||||
Vec3 position_; ///< Translation
|
|
||||||
Vec3 rotation_; ///< Rotation (Euler angles)
|
|
||||||
Vec3 scale_; ///< Scale factors
|
|
||||||
|
|
||||||
mutable Mat4 matrix_; ///< Cached transformation matrix
|
|
||||||
mutable Mat4 inverse_matrix_; ///< Cached inverse matrix
|
|
||||||
mutable bool dirty_; ///< Matrix needs update
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_GEOMETRY_TRANSFORM_H
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
/**
|
|
||||||
* @file triangle.h
|
|
||||||
* @brief Triangle primitive definition
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_GEOMETRY_TRIANGLE_H
|
|
||||||
#define ARE_INCLUDE_GEOMETRY_TRIANGLE_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
struct Ray;
|
|
||||||
struct HitRecord;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct Triangle
|
|
||||||
* @brief Triangle primitive for ray tracing
|
|
||||||
*
|
|
||||||
* Stores three vertices and provides intersection testing.
|
|
||||||
*/
|
|
||||||
struct Triangle {
|
|
||||||
Vertex v0_; ///< First vertex
|
|
||||||
Vertex v1_; ///< Second vertex
|
|
||||||
Vertex v2_; ///< Third vertex
|
|
||||||
MaterialHandle material_; ///< Material handle
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
Triangle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct triangle from three vertices
|
|
||||||
* @param v0 First vertex
|
|
||||||
* @param v1 Second vertex
|
|
||||||
* @param v2 Third vertex
|
|
||||||
* @param material Material handle
|
|
||||||
*/
|
|
||||||
Triangle(const Vertex& v0, const Vertex& v1, const Vertex& v2,
|
|
||||||
MaterialHandle material = are_invalid_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangle centroid
|
|
||||||
* @return Centroid position
|
|
||||||
*/
|
|
||||||
Vec3 centroid() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangle normal (geometric normal)
|
|
||||||
* @return Normal vector (normalized)
|
|
||||||
*/
|
|
||||||
Vec3 normal() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangle area
|
|
||||||
* @return Area
|
|
||||||
*/
|
|
||||||
Real area() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute axis-aligned bounding box
|
|
||||||
* @return AABB containing the triangle
|
|
||||||
*/
|
|
||||||
AABB compute_aabb() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Ray-triangle intersection test (Möller-Trumbore algorithm)
|
|
||||||
* @param ray Ray to test
|
|
||||||
* @param hit Output hit record
|
|
||||||
* @return true if intersection occurred
|
|
||||||
*/
|
|
||||||
bool intersect(const Ray& ray, HitRecord& hit) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Fast ray-triangle intersection test (no hit record)
|
|
||||||
* @param ray Ray to test
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
* @return true if intersection occurred
|
|
||||||
*/
|
|
||||||
bool intersect_fast(const Ray& ray, Real t_max) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_GEOMETRY_TRIANGLE_H
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
/**
|
|
||||||
* @file vertex.h
|
|
||||||
* @brief Vertex data structure definition
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_GEOMETRY_VERTEX_H
|
|
||||||
#define ARE_INCLUDE_GEOMETRY_VERTEX_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct Vertex
|
|
||||||
* @brief Standard vertex structure for mesh data
|
|
||||||
*
|
|
||||||
* Contains position, normal, texture coordinates, and tangent information
|
|
||||||
* for PBR rendering pipeline.
|
|
||||||
*/
|
|
||||||
struct Vertex {
|
|
||||||
Vec3 position_;///< Vertex position in object space
|
|
||||||
Vec3 normal_; ///< Vertex normal (normalized)
|
|
||||||
Vec2 texcoord_; ///< Texture coordinates (UV)
|
|
||||||
Vec3 tangent_; ///< Tangent vector for normal mapping
|
|
||||||
|
|
||||||
Vertex() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct vertex with position only
|
|
||||||
* @param pos Position
|
|
||||||
*/
|
|
||||||
explicit Vertex(const Vec3& pos);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct vertex with position and normal
|
|
||||||
* @param pos Position
|
|
||||||
* @param norm Normal
|
|
||||||
*/
|
|
||||||
Vertex(const Vec3& pos, const Vec3& norm);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct vertex with position, normal, and texcoord
|
|
||||||
* @param pos Position
|
|
||||||
* @param norm Normal
|
|
||||||
* @param uv Texture coordinates
|
|
||||||
*/
|
|
||||||
Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct vertex with all attributes
|
|
||||||
* @param pos Position
|
|
||||||
* @param norm Normal
|
|
||||||
* @param uv Texture coordinates
|
|
||||||
* @param tan Tangent
|
|
||||||
*/
|
|
||||||
Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv, const Vec3& tan);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Interpolate between two vertices
|
|
||||||
* @param a First vertex
|
|
||||||
* @param b Second vertex
|
|
||||||
* @param t Interpolation factor [0, 1]
|
|
||||||
* @return Interpolated vertex
|
|
||||||
*/
|
|
||||||
static Vertex lerp(const Vertex& a, const Vertex& b, Real t);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get vertex attribute stride for OpenGL
|
|
||||||
* @return Size of Vertex structure in bytes
|
|
||||||
*/
|
|
||||||
constexpr size_t get_vertex_stride() {
|
|
||||||
return sizeof(Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get offset of position attribute
|
|
||||||
* @return Offset in bytes
|
|
||||||
*/
|
|
||||||
constexpr size_t get_position_offset() {
|
|
||||||
return offsetof(Vertex, position_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get offset of normal attribute
|
|
||||||
* @return Offset in bytes
|
|
||||||
*/
|
|
||||||
constexpr size_t get_normal_offset() {
|
|
||||||
return offsetof(Vertex, normal_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get offset of texcoord attribute
|
|
||||||
* @return Offset in bytes
|
|
||||||
*/
|
|
||||||
constexpr size_t get_texcoord_offset() {
|
|
||||||
return offsetof(Vertex, texcoord_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get offset of tangent attribute
|
|
||||||
* @return Offset in bytes
|
|
||||||
*/
|
|
||||||
constexpr size_t get_tangent_offset() {
|
|
||||||
return offsetof(Vertex, tangent_);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_GEOMETRY_VERTEX_H
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
/**
|
|
||||||
* @file gl_context.h
|
|
||||||
* @brief OpenGL context management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
#define ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class GLContext
|
|
||||||
* @brief OpenGL context initialization and management
|
|
||||||
*
|
|
||||||
* Handles GLAD initialization and provides OpenGL utility functions.
|
|
||||||
*/
|
|
||||||
class GLContext {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Initialize OpenGL context (load function pointers)
|
|
||||||
* @return true if initialization succeeded
|
|
||||||
*/
|
|
||||||
static bool initialize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if context is initialized
|
|
||||||
* @return true if initialized
|
|
||||||
*/
|
|
||||||
static bool is_initialized();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL version string
|
|
||||||
* @return Version string
|
|
||||||
*/
|
|
||||||
static std::string get_version();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL renderer string
|
|
||||||
* @return Renderer string
|
|
||||||
*/
|
|
||||||
static std::string get_renderer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL vendor string
|
|
||||||
* @return Vendor string
|
|
||||||
*/
|
|
||||||
static std::string get_vendor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if OpenGL extension is supported
|
|
||||||
* @param extension Extension name
|
|
||||||
* @return true if supported
|
|
||||||
*/
|
|
||||||
static bool is_extension_supported(const std::string& extension);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print OpenGL information to console
|
|
||||||
*/
|
|
||||||
static void print_info();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check for OpenGL errors
|
|
||||||
* @param file Source file
|
|
||||||
* @param line Line number
|
|
||||||
* @return true if error occurred
|
|
||||||
*/
|
|
||||||
static bool check_error(const char* file, int line);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all OpenGL errors
|
|
||||||
*/
|
|
||||||
static void clear_errors();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool initialized_; ///< Initialization flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
// OpenGL error checking macro
|
|
||||||
#ifdef ARE_ENABLE_DEBUG_VIS
|
|
||||||
#define ARE_GL_CHECK() are::GLContext::check_error(__FILE__, __LINE__)
|
|
||||||
#else
|
|
||||||
#define ARE_GL_CHECK() ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
/**
|
|
||||||
* @file window.h
|
|
||||||
* @brief Window management using GLFW
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
#define ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Forward declare GLFW types to avoid including GLFW in header
|
|
||||||
struct GLFWwindow;
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Window
|
|
||||||
* @brief GLFW window wrapper
|
|
||||||
*
|
|
||||||
* Manages window creation, input handling, and OpenGL context.
|
|
||||||
*/
|
|
||||||
class Window {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Window configuration
|
|
||||||
*/
|
|
||||||
explicit Window(const WindowConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~Window();
|
|
||||||
|
|
||||||
// Window control
|
|
||||||
bool should_close() const;
|
|
||||||
void set_should_close(bool should_close);
|
|
||||||
void swap_buffers();
|
|
||||||
void poll_events();
|
|
||||||
|
|
||||||
// Window properties
|
|
||||||
int get_width() const;
|
|
||||||
int get_height() const;
|
|
||||||
Real get_aspect_ratio() const;
|
|
||||||
const std::string& get_title() const;
|
|
||||||
|
|
||||||
void set_title(const std::string& title);
|
|
||||||
void set_size(int width, int height);
|
|
||||||
|
|
||||||
// Framebuffer size (may differ from window size on high-DPI displays)
|
|
||||||
void get_framebuffer_size(int& width, int& height) const;
|
|
||||||
|
|
||||||
// VSync control
|
|
||||||
void set_vsync(bool enabled);
|
|
||||||
bool get_vsync() const;
|
|
||||||
|
|
||||||
// Input queries (basic support)
|
|
||||||
bool is_key_pressed(int key) const;
|
|
||||||
bool is_mouse_button_pressed(int button) const;
|
|
||||||
void get_cursor_pos(double& x, double& y) const;
|
|
||||||
|
|
||||||
// Internal
|
|
||||||
GLFWwindow* get_native_window() const { return window_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize_glfw();
|
|
||||||
void create_window();
|
|
||||||
void setup_callbacks();
|
|
||||||
|
|
||||||
static void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
|
||||||
static void error_callback(int error, const char* description);
|
|
||||||
|
|
||||||
GLFWwindow* window_; ///< GLFW window handle
|
|
||||||
WindowConfig config_; ///< Window configuration
|
|
||||||
bool vsync_enabled_; ///< VSync state
|
|
||||||
|
|
||||||
static int instance_count_; ///< Number of Window instances
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
/**
|
|
||||||
* @file gbuffer.h
|
|
||||||
* @brief G-Buffer management for deferred rendering
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class GBuffer
|
|
||||||
* @brief G-Buffer for deferred rendering
|
|
||||||
*
|
|
||||||
* Attachment layout:
|
|
||||||
* 0: position (RGB16F)
|
|
||||||
* 1: normal (RGB16F)
|
|
||||||
* 2: albedo+metallic (RGBA8)
|
|
||||||
* 3: roughness+ao (RG8)
|
|
||||||
* 4: primitive id (R32UI)
|
|
||||||
* Depth: depth texture (DEPTH_COMPONENT24)
|
|
||||||
*/
|
|
||||||
class GBuffer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param width Buffer width
|
|
||||||
* @param height Buffer height
|
|
||||||
*/
|
|
||||||
GBuffer(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~GBuffer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Resize G-Buffer
|
|
||||||
* @param width New width
|
|
||||||
* @param height New height
|
|
||||||
*/
|
|
||||||
void resize(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Bind G-Buffer for rendering
|
|
||||||
*/
|
|
||||||
void bind();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unbind G-Buffer
|
|
||||||
*/
|
|
||||||
void unbind();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all buffers
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Bind texture for reading
|
|
||||||
* @param index Texture index
|
|
||||||
* @param texture_unit Texture unit to bind to
|
|
||||||
*/
|
|
||||||
void bind_texture(int index, int texture_unit);
|
|
||||||
|
|
||||||
// Texture getters
|
|
||||||
uint32_t get_position_texture() const { return position_texture_; }
|
|
||||||
uint32_t get_normal_texture() const { return normal_texture_; }
|
|
||||||
uint32_t get_albedo_texture() const { return albedo_texture_; }
|
|
||||||
uint32_t get_material_texture() const { return material_texture_; }
|
|
||||||
uint32_t get_depth_texture() const { return depth_texture_; }
|
|
||||||
uint32_t get_primitive_id_texture() const { return primitive_id_texture_; }
|
|
||||||
|
|
||||||
// Dimensions
|
|
||||||
int get_width() const { return width_; }
|
|
||||||
int get_height() const { return height_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read pixel data from G-Buffer
|
|
||||||
*
|
|
||||||
* Index mapping:
|
|
||||||
* - 0: position (RGB16F) -> GL_RGB/GL_FLOAT
|
|
||||||
* - 1: normal (RGB16F) -> GL_RGB/GL_FLOAT
|
|
||||||
* - 2: albedo_metallic (RGBA8) -> GL_RGBA/GL_UNSIGNED_BYTE
|
|
||||||
* - 3: material (RG8) -> GL_RG/GL_UNSIGNED_BYTE
|
|
||||||
* - 4: depth (DEPTH_COMPONENT24) -> GL_DEPTH_COMPONENT/GL_FLOAT
|
|
||||||
* - 5: primitive id (R32UI) -> GL_RED_INTEGER/GL_UNSIGNED_INT
|
|
||||||
*
|
|
||||||
* @param index Buffer index
|
|
||||||
* @param data Output data pointer
|
|
||||||
*/
|
|
||||||
void read_pixels(int index, void* data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void create_textures();
|
|
||||||
void delete_textures();
|
|
||||||
void create_framebuffer();
|
|
||||||
|
|
||||||
uint32_t fbo_; ///< Framebuffer object
|
|
||||||
uint32_t rbo_depth_; ///< Legacy depth renderbuffer (unused)
|
|
||||||
|
|
||||||
uint32_t position_texture_;
|
|
||||||
uint32_t normal_texture_;
|
|
||||||
uint32_t albedo_texture_;
|
|
||||||
uint32_t material_texture_;
|
|
||||||
uint32_t depth_texture_;
|
|
||||||
uint32_t primitive_id_texture_;
|
|
||||||
|
|
||||||
int width_;
|
|
||||||
int height_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
/**
|
|
||||||
* @file rasterizer.h
|
|
||||||
* @brief Rasterization pipeline for G-Buffer generation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
class GBuffer;
|
|
||||||
class ShaderProgram;
|
|
||||||
class SceneManager;
|
|
||||||
class Camera;
|
|
||||||
class Mesh;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct RasterizerState
|
|
||||||
* @brief Rasterizer fixed-function state (configurable)
|
|
||||||
*/
|
|
||||||
struct RasterizerState {
|
|
||||||
bool enable_depth_test = true;
|
|
||||||
bool enable_cull_face = false;
|
|
||||||
uint32_t cull_face_mode = 0x0405; // GL_BACK
|
|
||||||
uint32_t front_face = 0x0901; // GL_CCW
|
|
||||||
};
|
|
||||||
|
|
||||||
class Rasterizer {
|
|
||||||
public:
|
|
||||||
Rasterizer(int width, int height);
|
|
||||||
~Rasterizer();
|
|
||||||
|
|
||||||
void resize(int width, int height);
|
|
||||||
|
|
||||||
void render_gbuffer(const SceneManager& scene, const Camera& camera);
|
|
||||||
|
|
||||||
GBuffer& get_gbuffer();
|
|
||||||
const GBuffer& get_gbuffer() const;
|
|
||||||
|
|
||||||
void upload_mesh(Mesh& mesh);
|
|
||||||
void delete_mesh(Mesh& mesh);
|
|
||||||
|
|
||||||
void initialize_shaders(const std::string& shader_dir);
|
|
||||||
|
|
||||||
void set_triangle_base_provider(std::function<uint32_t(size_t)> provider);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set rasterizer fixed-function state
|
|
||||||
* @param state State
|
|
||||||
*/
|
|
||||||
void set_state(const RasterizerState& state);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setup_mesh_buffers(Mesh& mesh);
|
|
||||||
|
|
||||||
std::unique_ptr<GBuffer> gbuffer_;
|
|
||||||
std::unique_ptr<ShaderProgram> gbuffer_shader_;
|
|
||||||
|
|
||||||
std::function<uint32_t(size_t)> triangle_base_provider_;
|
|
||||||
RasterizerState state_;
|
|
||||||
|
|
||||||
int width_;
|
|
||||||
int height_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
* @file shader_program.h
|
|
||||||
* @brief OpenGL shader program wrapper
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
enum class ShaderType {
|
|
||||||
ARE_SHADER_VERTEX,
|
|
||||||
ARE_SHADER_FRAGMENT,
|
|
||||||
ARE_SHADER_COMPUTE
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderProgram {
|
|
||||||
public:
|
|
||||||
ShaderProgram();
|
|
||||||
~ShaderProgram();
|
|
||||||
|
|
||||||
bool load_shader(ShaderType type, const std::string& filepath);
|
|
||||||
bool compile_shader(ShaderType type, const std::string& source);
|
|
||||||
bool link();
|
|
||||||
|
|
||||||
void use() const;
|
|
||||||
|
|
||||||
bool is_valid() const { return program_ != 0 && linked_; }
|
|
||||||
uint32_t get_program() const { return program_; }
|
|
||||||
|
|
||||||
void set_uniform(const std::string& name, int value);
|
|
||||||
void set_uniform(const std::string& name, uint32_t value); ///< NEW
|
|
||||||
void set_uniform(const std::string& name, float value);
|
|
||||||
void set_uniform(const std::string& name, const Vec2& value);
|
|
||||||
void set_uniform(const std::string& name, const Vec3& value);
|
|
||||||
void set_uniform(const std::string& name, const Vec4& value);
|
|
||||||
void set_uniform(const std::string& name, const Mat3& value);
|
|
||||||
void set_uniform(const std::string& name, const Mat4& value);
|
|
||||||
|
|
||||||
int get_uniform_location(const std::string& name);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool check_compile_errors(uint32_t shader, ShaderType type);
|
|
||||||
bool check_link_errors();
|
|
||||||
|
|
||||||
uint32_t program_;
|
|
||||||
uint32_t vertex_shader_;
|
|
||||||
uint32_t fragment_shader_;
|
|
||||||
uint32_t compute_shader_;
|
|
||||||
bool linked_;
|
|
||||||
std::unordered_map<std::string, int> uniform_cache_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
/**
|
|
||||||
* @file compute_raytracer.h
|
|
||||||
* @brief GPU compute shader ray tracing implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H
|
|
||||||
|
|
||||||
#include <are/raytracer/raytracer.h>
|
|
||||||
#include <are/rasterizer/shader_program.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ComputeRayTracer
|
|
||||||
* @brief GPU-based ray tracing using compute shaders
|
|
||||||
*/
|
|
||||||
class ComputeRayTracer : public RayTracer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Ray tracing configuration
|
|
||||||
*/
|
|
||||||
explicit ComputeRayTracer(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~ComputeRayTracer() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene using compute shader ray tracing
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
* @param gbuffer G-Buffer (optional)
|
|
||||||
* @param output Output texture ID
|
|
||||||
*/
|
|
||||||
void render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update BVH
|
|
||||||
* @param bvh BVH reference
|
|
||||||
*/
|
|
||||||
void update_bvh(const BVH& bvh) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize_compute_shader(const std::string& shader_dir);
|
|
||||||
void upload_scene_data(const SceneManager& scene);
|
|
||||||
void upload_bvh_data(const BVH& bvh);
|
|
||||||
void upload_camera_data(const Camera& camera);
|
|
||||||
|
|
||||||
std::unique_ptr<ShaderProgram> compute_shader_; ///< Ray tracing compute shader
|
|
||||||
|
|
||||||
// GPU buffers (SSBOs)
|
|
||||||
uint32_t bvh_buffer_; ///< BVH nodes buffer
|
|
||||||
uint32_t triangle_buffer_; ///< Triangle data buffer
|
|
||||||
uint32_t material_buffer_; ///< Material data buffer
|
|
||||||
uint32_t light_buffer_; ///< Light data buffer
|
|
||||||
|
|
||||||
bool buffers_initialized_; ///< Buffer initialization flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_COMPUTE_RAYTRACER_H
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
/**
|
|
||||||
* @file cpu_raytracer.h
|
|
||||||
* @brief CPU-based ray tracing implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
|
|
||||||
#include <are/raytracer/raytracer.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class CPURayTracer
|
|
||||||
* @brief CPU-based ray tracing implementation
|
|
||||||
*
|
|
||||||
* Uses multithreading for parallel ray tracing on CPU.
|
|
||||||
*/
|
|
||||||
class CPURayTracer : public RayTracer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Ray tracing configuration
|
|
||||||
*/
|
|
||||||
explicit CPURayTracer(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~CPURayTracer() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene using CPU ray tracing
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
* @param gbuffer G-Buffer (optional)
|
|
||||||
* @param output Output texture ID
|
|
||||||
*/
|
|
||||||
void render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update BVH
|
|
||||||
* @param bvh BVH reference
|
|
||||||
*/
|
|
||||||
void update_bvh(const BVH& bvh) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Trace a single ray
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param depth Current recursion depth
|
|
||||||
* @return Ray color
|
|
||||||
*/
|
|
||||||
Vec3 trace_ray(const Ray& ray, int depth);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shade hit point
|
|
||||||
* @param hit Hit record
|
|
||||||
* @param ray Incident ray
|
|
||||||
* @param depth Current recursion depth
|
|
||||||
* @return Shaded color
|
|
||||||
*/
|
|
||||||
Vec3 shade(const HitRecord& hit, const Ray& ray, int depth);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute direct lighting
|
|
||||||
* @param hit Hit record
|
|
||||||
* @return Direct lighting contribution
|
|
||||||
*/
|
|
||||||
Vec3 compute_direct_lighting(const HitRecord& hit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute ambient occlusion
|
|
||||||
* @param hit Hit record
|
|
||||||
* @return AO factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real compute_ambient_occlusion(const HitRecord& hit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check shadow ray
|
|
||||||
* @param origin Shadow ray origin
|
|
||||||
* @param direction Shadow ray direction
|
|
||||||
* @param max_distance Maximum distance
|
|
||||||
* @return true if in shadow
|
|
||||||
*/
|
|
||||||
bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance, uint32_t ignore_triangle);
|
|
||||||
|
|
||||||
const BVH* bvh_; ///< BVH reference
|
|
||||||
const SceneManager* scene_; ///< Scene reference
|
|
||||||
std::vector<Vec3> framebuffer_; ///< CPU framebuffer (HDR)
|
|
||||||
int width_; ///< Framebuffer width
|
|
||||||
int height_; ///< Framebuffer height
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/**
|
|
||||||
* @file hit_record.h
|
|
||||||
* @brief Ray-surface intersection record
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct HitRecord
|
|
||||||
* @brief Information about ray-surface intersection
|
|
||||||
*/
|
|
||||||
struct HitRecord {
|
|
||||||
Vec3 position_; ///< Hit position in world space
|
|
||||||
Vec3 normal_; ///< Surface normal at hit point
|
|
||||||
Vec2 texcoord_; ///< Texture coordinates at hit point
|
|
||||||
Vec3 tangent_; ///< Tangent vector at hit point
|
|
||||||
Real t_; ///< Ray parameter at hit point
|
|
||||||
MaterialHandle material_; ///< Material at hit point
|
|
||||||
uint32_t triangle_index_; ///< Triangle index that was hit
|
|
||||||
bool front_face_; ///< Whether ray hit front face
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
HitRecord();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set face normal based on ray direction
|
|
||||||
* @param ray_direction Ray direction
|
|
||||||
* @param outward_normal Outward-facing normal
|
|
||||||
*/
|
|
||||||
void set_face_normal(const Vec3& ray_direction, const Vec3& outward_normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if hit record is valid
|
|
||||||
* @return true if hit occurred
|
|
||||||
*/
|
|
||||||
bool is_valid() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/**
|
|
||||||
* @file ray.h
|
|
||||||
* @brief Ray structure for ray tracing
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct Ray
|
|
||||||
* @brief Ray representation for ray tracing
|
|
||||||
*/
|
|
||||||
struct Ray {
|
|
||||||
Vec3 origin_; ///< Ray origin
|
|
||||||
Vec3 direction_; ///< Ray direction (normalized)
|
|
||||||
Real t_min_; ///< Minimum t value
|
|
||||||
Real t_max_; ///< Maximum t value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
Ray();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct ray with origin and direction
|
|
||||||
* @param origin Ray origin
|
|
||||||
* @param direction Ray direction (will be normalized)
|
|
||||||
* @param t_min Minimum t value
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
*/
|
|
||||||
Ray(const Vec3& origin, const Vec3& direction,
|
|
||||||
Real t_min = are_epsilon, Real t_max = 1e30f);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Evaluate ray at parameter t
|
|
||||||
* @param t Parameter value
|
|
||||||
* @return Point on ray
|
|
||||||
*/
|
|
||||||
Vec3 at(Real t) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if t is within valid range
|
|
||||||
* @param t Parameter value
|
|
||||||
* @return true if t is valid
|
|
||||||
*/
|
|
||||||
bool is_valid_t(Real t) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
* @file raytracer.h
|
|
||||||
* @brief Ray tracing interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class SceneManager;
|
|
||||||
class Camera;
|
|
||||||
class GBuffer;
|
|
||||||
class BVH;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class RayTracer
|
|
||||||
* @brief Abstract ray tracing interface
|
|
||||||
*
|
|
||||||
* Base class for CPU and GPU ray tracing implementations.
|
|
||||||
*/
|
|
||||||
class RayTracer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Ray tracing configuration
|
|
||||||
*/
|
|
||||||
explicit RayTracer(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Virtual destructor
|
|
||||||
*/
|
|
||||||
virtual ~RayTracer() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene using ray tracing
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
* @param gbuffer G-Buffer (optional, for hybrid rendering)
|
|
||||||
* @param output Output texture ID
|
|
||||||
*/
|
|
||||||
virtual void render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update BVH
|
|
||||||
* @param bvh BVH reference
|
|
||||||
*/
|
|
||||||
virtual void update_bvh(const BVH& bvh) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set configuration
|
|
||||||
* @param config New configuration
|
|
||||||
*/
|
|
||||||
virtual void set_config(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get configuration
|
|
||||||
* @return Current configuration
|
|
||||||
*/
|
|
||||||
const RayTracingConfig& get_config() const { return config_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
RayTracingConfig config_; ///< Ray tracing configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/**
|
|
||||||
* @file geometry_cache.h
|
|
||||||
* @brief Frame-level geometry cache for consistent BVH and GBuffer primitive ids
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
|
||||||
#define ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/acceleration/bvh.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class GeometryCache
|
|
||||||
* @brief Builds a global triangle list and BVH from a SceneManager snapshot.
|
|
||||||
*
|
|
||||||
* Provides a single source of truth for:
|
|
||||||
* - Global triangle array layout (used by BVH and RayTracer)
|
|
||||||
* - Mesh -> triangle base mapping (used by Rasterizer for primitive id output)
|
|
||||||
*/
|
|
||||||
class GeometryCache {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Build cache from scene
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param bvh_config BVH build config
|
|
||||||
* @return true if succeeded
|
|
||||||
*/
|
|
||||||
bool build_from_scene(const SceneManager& scene,
|
|
||||||
const BVHBuildConfig& bvh_config = BVHBuildConfig());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get BVH reference
|
|
||||||
* @return BVH
|
|
||||||
*/
|
|
||||||
const BVH& get_bvh() const { return bvh_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get global triangles
|
|
||||||
* @return Triangle array
|
|
||||||
*/
|
|
||||||
const std::vector<Triangle>& get_triangles() const { return triangles_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangle base for mesh by index into scene.get_all_meshes()
|
|
||||||
* @param mesh_index Mesh index in scene.get_all_meshes()
|
|
||||||
* @return Base triangle id
|
|
||||||
*/
|
|
||||||
uint32_t get_mesh_triangle_base(size_t mesh_index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Triangle> triangles_;
|
|
||||||
std::vector<uint32_t> mesh_triangle_base_;
|
|
||||||
BVH bvh_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RENDERER_GEOMETRY_CACHE_H
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
* @file render_context.h
|
|
||||||
* @brief Rendering context and state management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H
|
|
||||||
#define ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct RenderContext
|
|
||||||
* @brief Rendering context information
|
|
||||||
*
|
|
||||||
* Contains current rendering state and frame information.
|
|
||||||
*/
|
|
||||||
struct RenderContext {
|
|
||||||
int frame_number_; ///< Current frame number
|
|
||||||
double time_; ///< Total elapsed time in seconds
|
|
||||||
double delta_time_; ///< Time since last frame
|
|
||||||
|
|
||||||
int viewport_width_; ///< Viewport width
|
|
||||||
int viewport_height_; ///< Viewport height
|
|
||||||
|
|
||||||
RayTracingBackend current_backend_; ///< Current ray tracing backend
|
|
||||||
bool scene_dirty_; ///< Scene needs BVH rebuild
|
|
||||||
bool camera_moved_; ///< Camera moved this frame
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
RenderContext();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reset context
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update frame timing
|
|
||||||
* @param current_time Current time in seconds
|
|
||||||
*/
|
|
||||||
void update_timing(double current_time);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RENDERER_RENDER_CONTEXT_H
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
* @file render_stats.h
|
|
||||||
* @brief Rendering statistics tracking
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RENDERER_RENDER_STATS_H
|
|
||||||
#define ARE_INCLUDE_RENDERER_RENDER_STATS_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct RenderStats
|
|
||||||
* @brief Statistics for rendering performance analysis
|
|
||||||
*/
|
|
||||||
struct RenderStats {
|
|
||||||
// Frame timing
|
|
||||||
double frame_time_ms_; ///< Total frame time in milliseconds
|
|
||||||
double rasterization_time_ms_; ///< Rasterization time
|
|
||||||
double ray_tracing_time_ms_; ///< Ray tracing time
|
|
||||||
double bvh_build_time_ms_; ///< BVH construction time
|
|
||||||
double present_time_ms_; ///< Present/swap time
|
|
||||||
|
|
||||||
// Scene statistics
|
|
||||||
uint32_t mesh_count_; ///< Number of meshes
|
|
||||||
uint32_t triangle_count_; ///< Total triangle count
|
|
||||||
uint32_t light_count_; ///< Number of lights
|
|
||||||
uint32_t material_count_; ///< Number of materials
|
|
||||||
|
|
||||||
// Ray tracing statistics
|
|
||||||
uint64_t primary_rays_; ///< Number of primary rays
|
|
||||||
uint64_t secondary_rays_; ///< Number of secondary rays
|
|
||||||
uint64_t shadow_rays_; ///< Number of shadow rays
|
|
||||||
uint64_t bvh_traversals_; ///< BVH traversal count
|
|
||||||
uint64_t triangle_tests_; ///< Triangle intersection tests
|
|
||||||
|
|
||||||
// Memory statistics
|
|
||||||
size_t vertex_memory_bytes_; ///< Vertex buffer memory
|
|
||||||
size_t index_memory_bytes_; ///< Index buffer memory
|
|
||||||
size_t texture_memory_bytes_; ///< Texture memory
|
|
||||||
size_t bvh_memory_bytes_; ///< BVH memory
|
|
||||||
|
|
||||||
// FPS
|
|
||||||
double fps_; ///< Frames per second
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reset all statistics to zero
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print statistics to console
|
|
||||||
*/
|
|
||||||
void print() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update FPS based on frame time
|
|
||||||
*/
|
|
||||||
void update_fps();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RENDERER_RENDER_STATS_H
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
/**
|
|
||||||
* @file renderer.h
|
|
||||||
* @brief Main renderer interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RENDERER_RENDERER_H
|
|
||||||
#define ARE_INCLUDE_RENDERER_RENDERER_H
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
#include <are/renderer/render_stats.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class Window;
|
|
||||||
class SceneManager;
|
|
||||||
class Rasterizer;
|
|
||||||
class RayTracer;
|
|
||||||
class TextureManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Renderer
|
|
||||||
* @brief Main rendering interface for Aurora Rendering Engine
|
|
||||||
*
|
|
||||||
* This class provides the primary API for rendering scenes using
|
|
||||||
* hybrid rasterization and ray tracing techniques.
|
|
||||||
*/
|
|
||||||
class Renderer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Rendering configuration
|
|
||||||
*/
|
|
||||||
explicit Renderer(const AreConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~Renderer();
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
void set_config(const AreConfig& config);
|
|
||||||
const AreConfig& get_config() const;
|
|
||||||
|
|
||||||
// Camera management
|
|
||||||
void set_camera(const Camera& camera);
|
|
||||||
Camera& get_camera();
|
|
||||||
const Camera& get_camera() const;
|
|
||||||
|
|
||||||
// Scene management
|
|
||||||
MeshHandle add_mesh(const Mesh& mesh);
|
|
||||||
MaterialHandle add_material(const Material& material);
|
|
||||||
LightHandle add_light(const std::shared_ptr<Light>& light);
|
|
||||||
|
|
||||||
void remove_mesh(MeshHandle handle);
|
|
||||||
void remove_material(MaterialHandle handle);
|
|
||||||
void remove_light(LightHandle handle);
|
|
||||||
|
|
||||||
void update_mesh(MeshHandle handle, const Mesh& mesh);
|
|
||||||
void update_material(MaterialHandle handle, const Material& material);
|
|
||||||
|
|
||||||
void clear_scene();
|
|
||||||
|
|
||||||
// Ray tracing backend control
|
|
||||||
void set_ray_tracing_backend(RayTracingBackend backend);
|
|
||||||
RayTracingBackend get_ray_tracing_backend() const;
|
|
||||||
|
|
||||||
// Rendering
|
|
||||||
void begin_frame();
|
|
||||||
void render();
|
|
||||||
void end_frame();
|
|
||||||
void present();
|
|
||||||
|
|
||||||
// Frame capture
|
|
||||||
void capture_frame_ldr(uint8_t** pixels, int* width, int* height);
|
|
||||||
void capture_frame_hdr(float** pixels, int* width, int* height);
|
|
||||||
void save_frame(const std::string& filename, const uint8_t* pixels,
|
|
||||||
int width, int height);
|
|
||||||
|
|
||||||
// Window control
|
|
||||||
bool should_close() const;
|
|
||||||
void set_should_close(bool should_close);
|
|
||||||
|
|
||||||
// Statistics
|
|
||||||
const RenderStats& get_stats() const;
|
|
||||||
void reset_stats();
|
|
||||||
|
|
||||||
// Debug visualization
|
|
||||||
void set_gbuffer_visualization_mode(GBufferVisualizationMode mode);
|
|
||||||
GBufferVisualizationMode get_gbuffer_visualization_mode() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize_subsystems();
|
|
||||||
void shutdown_subsystems();
|
|
||||||
void rebuild_bvh_if_needed();
|
|
||||||
void check_scene_dirty();
|
|
||||||
|
|
||||||
AreConfig config_; ///< Current configuration
|
|
||||||
|
|
||||||
std::unique_ptr<Window> window_; ///< Window management
|
|
||||||
std::unique_ptr<SceneManager> scene_manager_; ///< Scene data management
|
|
||||||
std::unique_ptr<Rasterizer> rasterizer_; ///< Rasterization pipeline
|
|
||||||
std::unique_ptr<RayTracer> raytracer_; ///< Ray tracing pipeline
|
|
||||||
std::unique_ptr<TextureManager> texture_manager_; ///< Texture management
|
|
||||||
|
|
||||||
Camera camera_; ///< Active camera
|
|
||||||
RenderStats stats_; ///< Rendering statistics
|
|
||||||
|
|
||||||
bool scene_dirty_; ///< Scene needs BVH rebuild
|
|
||||||
bool initialized_; ///< Initialization flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RENDERER_RENDERER_H
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* @file camera.h
|
|
||||||
* @brief Camera class for view and projection management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_CAMERA_H
|
|
||||||
#define ARE_INCLUDE_SCENE_CAMERA_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Camera
|
|
||||||
* @brief Perspective camera for rendering
|
|
||||||
*
|
|
||||||
* Manages view and projection matrices, and provides ray generation
|
|
||||||
* for ray tracing.
|
|
||||||
*/
|
|
||||||
class Camera {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
Camera();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct camera with position and target
|
|
||||||
* @param position Camera position
|
|
||||||
* @param target Look-at target
|
|
||||||
* @param up Up vector
|
|
||||||
*/
|
|
||||||
Camera(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0));
|
|
||||||
|
|
||||||
// Position and orientation setters
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
void set_target(const Vec3& target);
|
|
||||||
void set_up(const Vec3& up);
|
|
||||||
void look_at(const Vec3& position, const Vec3& target, const Vec3& up = Vec3(0, 1, 0));
|
|
||||||
|
|
||||||
// Projection setters
|
|
||||||
void set_fov(Real fov_degrees);
|
|
||||||
void set_aspect_ratio(Real aspect);
|
|
||||||
void set_near_plane(Real near);
|
|
||||||
void set_far_plane(Real far);
|
|
||||||
void set_perspective(Real fov_degrees, Real aspect, Real near, Real far);
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
const Vec3& get_target() const { return target_; }
|
|
||||||
const Vec3& get_up() const { return up_; }
|
|
||||||
Real get_fov() const { return fov_; }
|
|
||||||
Real get_aspect_ratio() const { return aspect_ratio_; }
|
|
||||||
Real get_near_plane() const { return near_plane_; }
|
|
||||||
Real get_far_plane() const { return far_plane_; }
|
|
||||||
|
|
||||||
// Direction vectors
|
|
||||||
Vec3 get_forward() const;
|
|
||||||
Vec3 get_right() const;
|
|
||||||
|
|
||||||
// Matrix getters
|
|
||||||
const Mat4& get_view_matrix() const;
|
|
||||||
const Mat4& get_projection_matrix() const;
|
|
||||||
Mat4 get_view_projection_matrix() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate ray for given pixel coordinates
|
|
||||||
* @param u Normalized x coordinate [0, 1]
|
|
||||||
* @param v Normalized y coordinate [0, 1]
|
|
||||||
* @param origin Output ray origin
|
|
||||||
* @param direction Output ray direction (normalized)
|
|
||||||
*/
|
|
||||||
void generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if camera parameters have changed
|
|
||||||
* @return true if matrices need recalculation
|
|
||||||
*/
|
|
||||||
bool is_dirty() const { return dirty_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Mark camera as clean after matrix update
|
|
||||||
*/
|
|
||||||
void clear_dirty() { dirty_ = false; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void update_view_matrix() const;
|
|
||||||
void update_projection_matrix() const;
|
|
||||||
|
|
||||||
Vec3 position_; ///< Camera position
|
|
||||||
Vec3 target_; ///< Look-at target
|
|
||||||
Vec3 up_; ///< Up vector
|
|
||||||
|
|
||||||
Real fov_; ///< Field of view in degrees
|
|
||||||
Real aspect_ratio_; ///< Aspect ratio (width/height)
|
|
||||||
Real near_plane_; ///< Near clipping plane
|
|
||||||
Real far_plane_; ///< Far clipping plane
|
|
||||||
|
|
||||||
mutable Mat4 view_matrix_; ///< Cached view matrix
|
|
||||||
mutable Mat4 projection_matrix_; ///< Cached projection matrix
|
|
||||||
mutable bool view_dirty_; ///< View matrix needs update
|
|
||||||
mutable bool projection_dirty_; ///< Projection matrix needs update
|
|
||||||
bool dirty_; ///< Any parameter changed
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_CAMERA_H
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/**
|
|
||||||
* @file directional_light.h
|
|
||||||
* @brief Directional light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class DirectionalLight
|
|
||||||
* @brief Directional light source (sun-like)
|
|
||||||
*
|
|
||||||
* Represents an infinitely distant light source with parallel rays.
|
|
||||||
*/
|
|
||||||
class DirectionalLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
DirectionalLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with direction and color
|
|
||||||
* @param direction Light direction (will be normalized)
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
*/
|
|
||||||
DirectionalLight(const Vec3& direction, const Vec3& color = Vec3(1.0f),
|
|
||||||
Real intensity = 1.0f);
|
|
||||||
|
|
||||||
// Direction
|
|
||||||
void set_direction(const Vec3& direction);
|
|
||||||
const Vec3& get_direction() const { return direction_; }
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 direction_; ///< Light direction (normalized)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/**
|
|
||||||
* @file light.h
|
|
||||||
* @brief Base light class and common light utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum LightType
|
|
||||||
* @brief Types of light sources
|
|
||||||
*/
|
|
||||||
enum class LightType {
|
|
||||||
ARE_LIGHT_DIRECTIONAL,
|
|
||||||
ARE_LIGHT_POINT,
|
|
||||||
ARE_LIGHT_SPOT
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct LightData
|
|
||||||
* @brief Packed light data for GPU transfer
|
|
||||||
*
|
|
||||||
* This structure is designed to be efficiently transferred to GPU
|
|
||||||
* via SSBO or UBO.
|
|
||||||
*/
|
|
||||||
struct LightData {
|
|
||||||
Vec4 position_type_; ///< xyz: position, w: light type
|
|
||||||
Vec4 direction_range_; ///< xyz: direction, w: range
|
|
||||||
Vec4 color_intensity_; ///< xyz: color, w: intensity
|
|
||||||
Vec4 params_; ///< Light-specific parameters
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Light
|
|
||||||
* @brief Base class for all light types
|
|
||||||
*/
|
|
||||||
class Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param type Light type
|
|
||||||
*/
|
|
||||||
explicit Light(LightType type);
|
|
||||||
|
|
||||||
virtual ~Light() = default;
|
|
||||||
|
|
||||||
// Common properties
|
|
||||||
void set_color(const Vec3& color);
|
|
||||||
void set_intensity(Real intensity);
|
|
||||||
void set_cast_shadows(bool cast);
|
|
||||||
|
|
||||||
const Vec3& get_color() const { return color_; }
|
|
||||||
Real get_intensity() const { return intensity_; }
|
|
||||||
bool get_cast_shadows() const { return cast_shadows_; }
|
|
||||||
LightType get_type() const { return type_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pack light data for GPU transfer
|
|
||||||
* @return Packed light data
|
|
||||||
*/
|
|
||||||
virtual LightData pack() const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if light affects a point
|
|
||||||
* @param point World position
|
|
||||||
* @return true if light can affect the point
|
|
||||||
*/
|
|
||||||
virtual bool affects_point(const Vec3& point) const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
LightType type_; ///< Light type
|
|
||||||
Vec3 color_; ///< Light color (RGB)
|
|
||||||
Real intensity_; ///< Light intensity
|
|
||||||
bool cast_shadows_; ///< Whether light casts shadows
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_LIGHT_H
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
/**
|
|
||||||
* @file material.h
|
|
||||||
* @brief PBR material definition
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_MATERIAL_H
|
|
||||||
#define ARE_INCLUDE_SCENE_MATERIAL_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Material
|
|
||||||
* @brief Physically-based rendering material
|
|
||||||
*
|
|
||||||
* Supports standard PBR workflow with metallic-roughness model.
|
|
||||||
*/
|
|
||||||
class Material {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor - creates default white material
|
|
||||||
*/
|
|
||||||
Material();
|
|
||||||
|
|
||||||
// Albedo (base color)
|
|
||||||
void set_albedo(const Vec3& albedo);
|
|
||||||
void set_albedo_map(const std::string& path);
|
|
||||||
const Vec3& get_albedo() const { return albedo_; }
|
|
||||||
const std::string& get_albedo_map() const { return albedo_map_; }
|
|
||||||
bool has_albedo_map() const { return !albedo_map_.empty(); }
|
|
||||||
|
|
||||||
// Metallic
|
|
||||||
void set_metallic(Real metallic);
|
|
||||||
void set_metallic_map(const std::string& path);
|
|
||||||
Real get_metallic() const { return metallic_; }
|
|
||||||
const std::string& get_metallic_map() const { return metallic_map_; }
|
|
||||||
bool has_metallic_map() const { return !metallic_map_.empty(); }
|
|
||||||
|
|
||||||
// Roughness
|
|
||||||
void set_roughness(Real roughness);
|
|
||||||
void set_roughness_map(const std::string& path);
|
|
||||||
Real get_roughness() const { return roughness_; }
|
|
||||||
const std::string& get_roughness_map() const { return roughness_map_; }
|
|
||||||
bool has_roughness_map() const { return !roughness_map_.empty(); }
|
|
||||||
|
|
||||||
// Normal map
|
|
||||||
void set_normal_map(const std::string& path);
|
|
||||||
const std::string& get_normal_map() const { return normal_map_; }
|
|
||||||
bool has_normal_map() const { return !normal_map_.empty(); }
|
|
||||||
|
|
||||||
// Ambient occlusion
|
|
||||||
void set_ao_map(const std::string& path);
|
|
||||||
const std::string& get_ao_map() const { return ao_map_; }
|
|
||||||
bool has_ao_map() const { return !ao_map_.empty(); }
|
|
||||||
|
|
||||||
// Emissive
|
|
||||||
void set_emissive(const Vec3& emissive);
|
|
||||||
void set_emissive_map(const std::string& path);
|
|
||||||
const Vec3& get_emissive() const { return emissive_; }
|
|
||||||
const std::string& get_emissive_map() const { return emissive_map_; }
|
|
||||||
bool has_emissive_map() const { return !emissive_map_.empty(); }
|
|
||||||
bool is_emissive() const;
|
|
||||||
|
|
||||||
// Texture handles (set by TextureManager)
|
|
||||||
void set_albedo_texture_handle(TextureHandle handle) { albedo_tex_handle_ = handle; }
|
|
||||||
void set_metallic_texture_handle(TextureHandle handle) { metallic_tex_handle_ = handle; }
|
|
||||||
void set_roughness_texture_handle(TextureHandle handle) { roughness_tex_handle_ = handle; }
|
|
||||||
void set_normal_texture_handle(TextureHandle handle) { normal_tex_handle_ = handle; }
|
|
||||||
void set_ao_texture_handle(TextureHandle handle) { ao_tex_handle_ = handle; }
|
|
||||||
void set_emissive_texture_handle(TextureHandle handle) { emissive_tex_handle_ = handle; }
|
|
||||||
|
|
||||||
TextureHandle get_albedo_texture_handle() const { return albedo_tex_handle_; }
|
|
||||||
TextureHandle get_metallic_texture_handle() const { return metallic_tex_handle_; }
|
|
||||||
TextureHandle get_roughness_texture_handle() const { return roughness_tex_handle_; }
|
|
||||||
TextureHandle get_normal_texture_handle() const { return normal_tex_handle_; }
|
|
||||||
TextureHandle get_ao_texture_handle() const { return ao_tex_handle_; }
|
|
||||||
TextureHandle get_emissive_texture_handle() const { return emissive_tex_handle_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Base values
|
|
||||||
Vec3 albedo_;///< Base color (RGB)
|
|
||||||
Real metallic_; ///< Metallic factor [0, 1]
|
|
||||||
Real roughness_; ///< Roughness factor [0, 1]
|
|
||||||
Vec3 emissive_; ///< Emissive color (RGB)
|
|
||||||
|
|
||||||
// Texture paths
|
|
||||||
std::string albedo_map_;
|
|
||||||
std::string metallic_map_;
|
|
||||||
std::string roughness_map_;
|
|
||||||
std::string normal_map_;
|
|
||||||
std::string ao_map_;
|
|
||||||
std::string emissive_map_;
|
|
||||||
|
|
||||||
// Texture handles (GPU resources)
|
|
||||||
TextureHandle albedo_tex_handle_;
|
|
||||||
TextureHandle metallic_tex_handle_;
|
|
||||||
TextureHandle roughness_tex_handle_;
|
|
||||||
TextureHandle normal_tex_handle_;
|
|
||||||
TextureHandle ao_tex_handle_;
|
|
||||||
TextureHandle emissive_tex_handle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_MATERIAL_H
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mesh.h
|
|
||||||
* @brief Mesh class for geometry storage
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_MESH_H
|
|
||||||
#define ARE_INCLUDE_SCENE_MESH_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Mesh
|
|
||||||
* @brief Triangle mesh container
|
|
||||||
*
|
|
||||||
* Stores vertex and index data for a triangle mesh.
|
|
||||||
* Supports automatic AABB computation.
|
|
||||||
*/
|
|
||||||
class Mesh {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor - creates empty mesh
|
|
||||||
*/
|
|
||||||
Mesh();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct mesh from vertex and index data
|
|
||||||
* @param vertices Vertex array
|
|
||||||
* @param indices Index array (triangles)
|
|
||||||
* @param material_id Material handle
|
|
||||||
*/
|
|
||||||
Mesh(const std::vector<Vertex>& vertices,
|
|
||||||
const std::vector<uint32_t>& indices,
|
|
||||||
MaterialHandle material_id = are_invalid_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct mesh from raw arrays
|
|
||||||
* @param vertices Vertex array pointer
|
|
||||||
* @param vertex_count Number of vertices
|
|
||||||
* @param indices Index array pointer
|
|
||||||
* @param index_count Number of indices
|
|
||||||
* @param material_id Material handle
|
|
||||||
*/
|
|
||||||
Mesh(const Vertex* vertices, size_t vertex_count,
|
|
||||||
const uint32_t* indices, size_t index_count,
|
|
||||||
MaterialHandle material_id = are_invalid_handle);
|
|
||||||
|
|
||||||
// Data setters
|
|
||||||
void set_vertices(const std::vector<Vertex>& vertices);
|
|
||||||
void set_indices(const std::vector<uint32_t>& indices);
|
|
||||||
void set_material(MaterialHandle material_id);
|
|
||||||
|
|
||||||
// Data getters
|
|
||||||
const std::vector<Vertex>& get_vertices() const { return vertices_; }
|
|
||||||
const std::vector<uint32_t>& get_indices() const { return indices_; }
|
|
||||||
MaterialHandle get_material() const { return material_id_; }
|
|
||||||
|
|
||||||
// Geometry queries
|
|
||||||
size_t get_vertex_count() const { return vertices_.size(); }
|
|
||||||
size_t get_index_count() const { return indices_.size(); }
|
|
||||||
size_t get_triangle_count() const { return indices_.size() / 3; }
|
|
||||||
bool is_empty() const { return vertices_.empty() || indices_.empty(); }
|
|
||||||
|
|
||||||
// AABB
|
|
||||||
const AABB& get_aabb() const { return aabb_; }
|
|
||||||
void compute_aabb();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute tangent vectors for normal mapping
|
|
||||||
*/
|
|
||||||
void compute_tangents();/**
|
|
||||||
* @brief Get triangle vertices by index
|
|
||||||
* @param triangle_index Triangle index
|
|
||||||
* @param v0 Output first vertex
|
|
||||||
* @param v1 Output second vertex
|
|
||||||
* @param v2 Output third vertex
|
|
||||||
* @return true if triangle exists
|
|
||||||
*/
|
|
||||||
bool get_triangle(size_t triangle_index, Vertex& v0, Vertex& v1, Vertex& v2) const;
|
|
||||||
|
|
||||||
// GPU resource handles (set by Renderer)
|
|
||||||
void set_vao(uint32_t vao) { vao_ = vao; }
|
|
||||||
void set_vbo(uint32_t vbo) { vbo_ = vbo; }
|
|
||||||
void set_ebo(uint32_t ebo) { ebo_ = ebo; }
|
|
||||||
uint32_t get_vao() const { return vao_; }
|
|
||||||
uint32_t get_vbo() const { return vbo_; }
|
|
||||||
uint32_t get_ebo() const { return ebo_; }
|
|
||||||
bool has_gpu_resources() const { return vao_ != 0; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Vertex> vertices_; ///< Vertex data
|
|
||||||
std::vector<uint32_t> indices_; ///< Index data (triangles)
|
|
||||||
MaterialHandle material_id_; ///< Associated material
|
|
||||||
AABB aabb_; ///< Bounding box
|
|
||||||
|
|
||||||
// GPU resources
|
|
||||||
uint32_t vao_; ///< Vertex Array Object
|
|
||||||
uint32_t vbo_; ///< Vertex Buffer Object
|
|
||||||
uint32_t ebo_; ///< Element Buffer Object
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_MESH_H
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/**
|
|
||||||
* @file point_light.h
|
|
||||||
* @brief Point light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class PointLight
|
|
||||||
* @brief Point light source
|
|
||||||
*
|
|
||||||
* Emits light equally in all directions from a single point.
|
|
||||||
*/
|
|
||||||
class PointLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
PointLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with position and color
|
|
||||||
* @param position Light position
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
* @param range Light range (attenuation distance)
|
|
||||||
*/
|
|
||||||
PointLight(const Vec3& position, const Vec3& color = Vec3(1.0f),
|
|
||||||
Real intensity = 1.0f, Real range = 10.0f);
|
|
||||||
|
|
||||||
// Position
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
|
|
||||||
// Range (attenuation)
|
|
||||||
void set_range(Real range);
|
|
||||||
Real get_range() const { return range_; }
|
|
||||||
|
|
||||||
// Attenuation parameters
|
|
||||||
void set_attenuation(Real constant, Real linear, Real quadratic);
|
|
||||||
Real get_constant_attenuation() const { return attenuation_constant_; }
|
|
||||||
Real get_linear_attenuation() const { return attenuation_linear_; }
|
|
||||||
Real get_quadratic_attenuation() const { return attenuation_quadratic_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate attenuation at given distance
|
|
||||||
* @param distance Distance from light
|
|
||||||
* @return Attenuation factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real calculate_attenuation(Real distance) const;
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 position_; ///< Light position
|
|
||||||
Real range_; ///< Light range
|
|
||||||
Real attenuation_constant_; ///< Constant attenuation factor
|
|
||||||
Real attenuation_linear_; ///< Linear attenuation factor
|
|
||||||
Real attenuation_quadratic_; ///< Quadratic attenuation factor
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
/**
|
|
||||||
* @file scene_manager.h
|
|
||||||
* @brief Scene data management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_SCENE_MANAGER_H
|
|
||||||
#define ARE_INCLUDE_SCENE_SCENE_MANAGER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class SceneManager
|
|
||||||
* @brief Manages all scene objects (meshes, materials, lights)
|
|
||||||
*
|
|
||||||
* Provides handle-based access to scene resources and tracks
|
|
||||||
* scene modifications for BVH rebuilding.
|
|
||||||
*/
|
|
||||||
class SceneManager {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
SceneManager();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~SceneManager();
|
|
||||||
|
|
||||||
// Mesh management
|
|
||||||
MeshHandle add_mesh(const Mesh& mesh);
|
|
||||||
void remove_mesh(MeshHandle handle);
|
|
||||||
void update_mesh(MeshHandle handle, const Mesh& mesh);
|
|
||||||
Mesh* get_mesh(MeshHandle handle);
|
|
||||||
const Mesh* get_mesh(MeshHandle handle) const;
|
|
||||||
const std::vector<Mesh>& get_all_meshes() const { return meshes_; }
|
|
||||||
|
|
||||||
// Material management
|
|
||||||
MaterialHandle add_material(const Material& material);
|
|
||||||
void remove_material(MaterialHandle handle);
|
|
||||||
void update_material(MaterialHandle handle, const Material& material);
|
|
||||||
Material* get_material(MaterialHandle handle);
|
|
||||||
const Material* get_material(MaterialHandle handle) const;
|
|
||||||
const std::vector<Material>& get_all_materials() const { return materials_; }
|
|
||||||
|
|
||||||
// Light management
|
|
||||||
LightHandle add_light(const std::shared_ptr<Light>& light);
|
|
||||||
void remove_light(LightHandle handle);
|
|
||||||
std::shared_ptr<Light> get_light(LightHandle handle);
|
|
||||||
const std::vector<std::shared_ptr<Light>>& get_all_lights() const { return lights_; }
|
|
||||||
|
|
||||||
// Scene queries
|
|
||||||
size_t get_mesh_count() const { return meshes_.size(); }
|
|
||||||
size_t get_material_count() const { return materials_.size(); }
|
|
||||||
size_t get_light_count() const { return lights_.size(); }
|
|
||||||
size_t get_total_triangle_count() const;
|
|
||||||
|
|
||||||
// Scene state
|
|
||||||
bool is_dirty() const { return dirty_; }
|
|
||||||
void mark_dirty() { dirty_ = true; }
|
|
||||||
void clear_dirty() { dirty_ = false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all scene data
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Validate all handles and remove invalid entries
|
|
||||||
*/
|
|
||||||
void compact();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Mesh> meshes_; ///< Mesh storage
|
|
||||||
std::vector<Material> materials_; ///< Material storage
|
|
||||||
std::vector<std::shared_ptr<Light>> lights_; ///< Light storage
|
|
||||||
|
|
||||||
std::unordered_map<MeshHandle, size_t> mesh_handle_map_;
|
|
||||||
std::unordered_map<MaterialHandle, size_t> material_handle_map_;
|
|
||||||
std::unordered_map<LightHandle, size_t> light_handle_map_;
|
|
||||||
|
|
||||||
MeshHandle next_mesh_handle_;
|
|
||||||
MaterialHandle next_material_handle_;
|
|
||||||
LightHandle next_light_handle_;
|
|
||||||
|
|
||||||
bool dirty_; ///< Scene modified flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_SCENE_MANAGER_H
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
/**
|
|
||||||
* @file spot_light.h
|
|
||||||
* @brief Spot light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class SpotLight
|
|
||||||
* @brief Spot light source
|
|
||||||
*
|
|
||||||
* Emits light in a cone from a single point.
|
|
||||||
*/
|
|
||||||
class SpotLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
SpotLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with position, direction, and angles
|
|
||||||
* @param position Light position
|
|
||||||
* @param direction Light direction
|
|
||||||
* @param inner_angle Inner cone angle in degrees
|
|
||||||
* @param outer_angle Outer cone angle in degrees
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
*/
|
|
||||||
SpotLight(const Vec3& position, const Vec3& direction,Real inner_angle, Real outer_angle,
|
|
||||||
const Vec3& color = Vec3(1.0f), Real intensity = 1.0f);
|
|
||||||
|
|
||||||
// Position and direction
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
void set_direction(const Vec3& direction);
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
const Vec3& get_direction() const { return direction_; }
|
|
||||||
|
|
||||||
// Cone angles (in degrees)
|
|
||||||
void set_inner_angle(Real angle);
|
|
||||||
void set_outer_angle(Real angle);
|
|
||||||
Real get_inner_angle() const { return inner_angle_; }
|
|
||||||
Real get_outer_angle() const { return outer_angle_; }
|
|
||||||
|
|
||||||
// Range
|
|
||||||
void set_range(Real range);
|
|
||||||
Real get_range() const { return range_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate spotlight intensity at given direction
|
|
||||||
* @param to_point Direction from light to point (normalized)
|
|
||||||
* @return Spotlight factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real calculate_spot_factor(const Vec3& to_point) const;
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 position_; ///< Light position
|
|
||||||
Vec3 direction_; ///< Light direction (normalized)
|
|
||||||
Real inner_angle_; ///< Inner cone angle (degrees)
|
|
||||||
Real outer_angle_; ///< Outer cone angle (degrees)
|
|
||||||
Real range_; ///< Light range
|
|
||||||
Real cos_inner_; ///< Cosine of inner angle (cache
|
|
||||||
Real cos_outer_; ///< Cosine of outer angle (cached)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
* @file sampler.h
|
|
||||||
* @brief Texture sampling utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_TEXTURE_SAMPLER_H
|
|
||||||
#define ARE_INCLUDE_TEXTURE_SAMPLER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/texture/texture.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Sampler
|
|
||||||
* @brief Texture sampling utilities for CPU ray tracing
|
|
||||||
*
|
|
||||||
* Provides bilinear filtering and wrapping modes for CPU-side texture access.
|
|
||||||
*/
|
|
||||||
class Sampler {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Sample texture at UV coordinates
|
|
||||||
* @param texture Texture to sample
|
|
||||||
* @param uv UV coordinates
|
|
||||||
* @return Sampled color (RGBA)
|
|
||||||
*/
|
|
||||||
static Vec4 sample(const Texture& texture, const Vec2& uv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sample texture with bilinear filtering
|
|
||||||
* @param texture Texture to sample
|
|
||||||
* @param uv UV coordinates
|
|
||||||
* @return Sampled color (RGBA)
|
|
||||||
*/
|
|
||||||
static Vec4 sample_bilinear(const Texture& texture, const Vec2& uv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sample texture at nearest pixel
|
|
||||||
* @param texture Texture to sample
|
|
||||||
* @param uv UV coordinates
|
|
||||||
* @return Sampled color (RGBA)
|
|
||||||
*/
|
|
||||||
static Vec4 sample_nearest(const Texture& texture, const Vec2& uv);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static Vec2 apply_wrap(const Vec2& uv, TextureWrap wrap);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_TEXTURE_SAMPLER_H
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
/**
|
|
||||||
* @file texture.h
|
|
||||||
* @brief Texture resource management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_TEXTURE_TEXTURE_H
|
|
||||||
#define ARE_INCLUDE_TEXTURE_TEXTURE_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum TextureFormat
|
|
||||||
* @brief Texture internal formats
|
|
||||||
*/
|
|
||||||
enum class TextureFormat {
|
|
||||||
ARE_TEXTURE_R8,
|
|
||||||
ARE_TEXTURE_RG8,
|
|
||||||
ARE_TEXTURE_RGB8,
|
|
||||||
ARE_TEXTURE_RGBA8,
|
|
||||||
ARE_TEXTURE_R16F,
|
|
||||||
ARE_TEXTURE_RG16F,
|
|
||||||
ARE_TEXTURE_RGB16F,
|
|
||||||
ARE_TEXTURE_RGBA16F,
|
|
||||||
ARE_TEXTURE_R32F,
|
|
||||||
ARE_TEXTURE_RG32F,
|
|
||||||
ARE_TEXTURE_RGB32F,
|
|
||||||
ARE_TEXTURE_RGBA32F
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum TextureFilter
|
|
||||||
* @brief Texture filtering modes
|
|
||||||
*/
|
|
||||||
enum class TextureFilter {
|
|
||||||
ARE_TEXTURE_FILTER_NEAREST,
|
|
||||||
ARE_TEXTURE_FILTER_LINEAR,
|
|
||||||
ARE_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST,
|
|
||||||
ARE_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST,
|
|
||||||
ARE_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR,
|
|
||||||
ARE_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum TextureWrap
|
|
||||||
* @brief Texture wrapping modes
|
|
||||||
*/
|
|
||||||
enum class TextureWrap {
|
|
||||||
ARE_TEXTURE_WRAP_REPEAT,
|
|
||||||
ARE_TEXTURE_WRAP_CLAMP_TO_EDGE,
|
|
||||||
ARE_TEXTURE_WRAP_CLAMP_TO_BORDER,
|
|
||||||
ARE_TEXTURE_WRAP_MIRRORED_REPEAT
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Texture
|
|
||||||
* @brief OpenGL texture wrapper
|
|
||||||
*/
|
|
||||||
class Texture {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
Texture();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~Texture();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load texture from file
|
|
||||||
* @param filepath Image file path
|
|
||||||
* @param format Desired texture format
|
|
||||||
* @param generate_mipmaps Generate mipmaps
|
|
||||||
* @return true if load succeeded
|
|
||||||
*/
|
|
||||||
bool load_from_file(const std::string& filepath,
|
|
||||||
TextureFormat format = TextureFormat::ARE_TEXTURE_RGBA8,
|
|
||||||
bool generate_mipmaps = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create texture from raw data
|
|
||||||
* @param width Texture width
|
|
||||||
* @param height Texture height
|
|
||||||
* @param format Texture format
|
|
||||||
* @param data Pixel data
|
|
||||||
* @param generate_mipmaps Generate mipmaps
|
|
||||||
* @return true if creation succeeded
|
|
||||||
*/
|
|
||||||
bool create_from_data(int width, int height,
|
|
||||||
TextureFormat format,
|
|
||||||
const void* data,
|
|
||||||
bool generate_mipmaps = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Bind texture to texture unit
|
|
||||||
* @param unit Texture unit (0-31)
|
|
||||||
*/
|
|
||||||
void bind(int unit = 0) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unbind texture
|
|
||||||
*/
|
|
||||||
void unbind() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set texture filtering
|
|
||||||
* @param min_filter Minification filter
|
|
||||||
* @param mag_filter Magnification filter
|
|
||||||
*/
|
|
||||||
void set_filter(TextureFilter min_filter, TextureFilter mag_filter);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set texture wrapping
|
|
||||||
* @param wrap_s S-axis wrapping
|
|
||||||
* @param wrap_t T-axis wrapping
|
|
||||||
*/
|
|
||||||
void set_wrap(TextureWrap wrap_s, TextureWrap wrap_t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate mipmaps
|
|
||||||
*/
|
|
||||||
void generate_mipmaps();
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
uint32_t get_id() const { return texture_id_; }
|
|
||||||
int get_width() const { return width_; }
|
|
||||||
int get_height() const { return height_; }
|
|
||||||
TextureFormat get_format() const { return format_; }
|
|
||||||
bool is_valid() const { return texture_id_ != 0; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Delete texture
|
|
||||||
*/
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t texture_id_; ///< OpenGL texture ID
|
|
||||||
int width_; ///< Texture width
|
|
||||||
int height_; ///< Texture height
|
|
||||||
TextureFormat format_; ///< Texture format
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_TEXTURE_TEXTURE_H
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
/**
|
|
||||||
* @file texture_manager.h
|
|
||||||
* @brief Texture resource management and caching
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H
|
|
||||||
#define ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/texture/texture.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class TextureManager
|
|
||||||
* @brief Manages texture loading and caching
|
|
||||||
*
|
|
||||||
* Automatically handles texture deduplication and lifetime management.
|
|
||||||
*/
|
|
||||||
class TextureManager {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
TextureManager();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~TextureManager();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load texture from file (with caching)
|
|
||||||
* @param filepath Texture file path
|
|
||||||
* @param format Desired texture format
|
|
||||||
* @param generate_mipmaps Generate mipmaps
|
|
||||||
* @return Texture handle (are_invalid_handle if failed)
|
|
||||||
*/
|
|
||||||
TextureHandle load_texture(const std::string& filepath,
|
|
||||||
TextureFormat format = TextureFormat::ARE_TEXTURE_RGBA8,
|
|
||||||
bool generate_mipmaps = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create texture from raw data
|
|
||||||
* @param name Texture name (for caching)
|
|
||||||
* @param width Texture width
|
|
||||||
* @param height Texture height
|
|
||||||
* @param format Texture format
|
|
||||||
* @param data Pixel data
|
|
||||||
* @param generate_mipmaps Generate mipmaps
|
|
||||||
* @return Texture handle
|
|
||||||
*/
|
|
||||||
TextureHandle create_texture(const std::string& name,
|
|
||||||
int width, int height,
|
|
||||||
TextureFormat format,
|
|
||||||
const void* data,
|
|
||||||
bool generate_mipmaps = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get texture by handle
|
|
||||||
* @param handle Texture handle
|
|
||||||
* @return Texture pointer (nullptr if not found)
|
|
||||||
*/
|
|
||||||
Texture* get_texture(TextureHandle handle);
|
|
||||||
const Texture* get_texture(TextureHandle handle) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unload texture
|
|
||||||
* @param handle Texture handle
|
|
||||||
*/
|
|
||||||
void unload_texture(TextureHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all textures
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get total texture memory usage
|
|
||||||
* @return Memory usage in bytes
|
|
||||||
*/
|
|
||||||
size_t get_memory_usage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get number of loaded textures
|
|
||||||
* @return Texture count
|
|
||||||
*/
|
|
||||||
size_t get_texture_count() const { return textures_.size(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<std::string, TextureHandle> path_to_handle_;
|
|
||||||
std::unordered_map<TextureHandle, std::unique_ptr<Texture>> textures_;
|
|
||||||
TextureHandle next_handle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_TEXTURE_TEXTURE_MANAGER_H
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/**
|
|
||||||
* @file file_utils.h
|
|
||||||
* @brief File system utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
#define ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read entire file into string
|
|
||||||
* @param filepath File path
|
|
||||||
* @return File contents (empty if failed)
|
|
||||||
*/
|
|
||||||
std::string read_file_to_string(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read entire file into byte array
|
|
||||||
* @param filepath File path
|
|
||||||
* @return File contents (empty if failed)
|
|
||||||
*/
|
|
||||||
std::vector<uint8_t> read_file_to_bytes(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write string to file
|
|
||||||
* @param filepath File path
|
|
||||||
* @param content Content to write
|
|
||||||
* @return true if write succeeded
|
|
||||||
*/
|
|
||||||
bool write_string_to_file(const std::string& filepath, const std::string& content);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write bytes to file
|
|
||||||
* @param filepath File path
|
|
||||||
* @param data Data pointer
|
|
||||||
* @param size Data size in bytes
|
|
||||||
* @return true if write succeeded
|
|
||||||
*/
|
|
||||||
bool write_bytes_to_file(const std::string& filepath, const void* data, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if file exists
|
|
||||||
* @param filepath File path
|
|
||||||
* @return true if file exists
|
|
||||||
*/
|
|
||||||
bool file_exists(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if path is directory
|
|
||||||
* @param path Directory path
|
|
||||||
* @return true if directory exists
|
|
||||||
*/
|
|
||||||
bool is_directory(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create directory (including parent directories)
|
|
||||||
* @param path Directory path
|
|
||||||
* @return true if creation succeeded
|
|
||||||
*/
|
|
||||||
bool create_directory(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get file extension
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Extension (lowercase, without dot)
|
|
||||||
*/
|
|
||||||
std::string get_file_extension(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get filename from path
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Filename (without directory)
|
|
||||||
*/
|
|
||||||
std::string get_filename(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get directory from path
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Directory path
|
|
||||||
*/
|
|
||||||
std::string get_directory(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Join path components
|
|
||||||
* @param parts Path components
|
|
||||||
* @return Joined path
|
|
||||||
*/
|
|
||||||
std::string join_path(const std::vector<std::string>& parts);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Normalize path (resolve .. and .)
|
|
||||||
* @param path Path to normalize
|
|
||||||
* @return Normalized path
|
|
||||||
*/
|
|
||||||
std::string normalize_path(const std::string& path);
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
/**
|
|
||||||
* @file image_io.h
|
|
||||||
* @brief Image loading and saving utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_IMAGE_IO_H
|
|
||||||
#define ARE_INCLUDE_UTILS_IMAGE_IO_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum ImageFormat
|
|
||||||
* @brief Supported image formats
|
|
||||||
*/
|
|
||||||
enum class ImageFormat {
|
|
||||||
ARE_IMAGE_FORMAT_PPM,
|
|
||||||
ARE_IMAGE_FORMAT_BMP,
|
|
||||||
ARE_IMAGE_FORMAT_PNG,
|
|
||||||
ARE_IMAGE_FORMAT_JPG
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct ImageData
|
|
||||||
* @brief Container for image data
|
|
||||||
*/
|
|
||||||
struct ImageData {
|
|
||||||
int width_; ///< Image width
|
|
||||||
int height_; ///< Image height
|
|
||||||
int channels_; ///< Number of channels (3=RGB, 4=RGBA)
|
|
||||||
std::vector<uint8_t> data_; ///< Pixel data (row-major)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if image data is valid
|
|
||||||
* @return true if valid
|
|
||||||
*/
|
|
||||||
bool is_valid() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get pixel at (x, y)
|
|
||||||
* @param x X coordinate
|
|
||||||
* @param y Y coordinate
|
|
||||||
* @return Pointer to pixel data (RGB or RGBA)
|
|
||||||
*/
|
|
||||||
const uint8_t* get_pixel(int x, int y) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set pixel at (x, y)
|
|
||||||
* @param x X coordinate
|
|
||||||
* @param y Y coordinate
|
|
||||||
* @param r Red channel
|
|
||||||
* @param g Green channel
|
|
||||||
* @param b Blue channel
|
|
||||||
* @param a Alpha channel (optional)
|
|
||||||
*/
|
|
||||||
void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load image from file
|
|
||||||
* @param filename Image file path
|
|
||||||
* @param flip_vertically Whether to flip image vertically
|
|
||||||
* @return Image data (empty if failed)
|
|
||||||
*/
|
|
||||||
ImageData load_image(const std::string& filename, bool flip_vertically = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Save image to file
|
|
||||||
* @param filename Output file path
|
|
||||||
* @param data Image data
|
|
||||||
* @param format Output format (auto-detected from extension if not specified)
|
|
||||||
* @return true if save succeeded
|
|
||||||
*/
|
|
||||||
bool save_image(const std::string& filename, const ImageData& data,
|
|
||||||
ImageFormat format = ImageFormat::ARE_IMAGE_FORMAT_PNG);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Save raw pixel data to file
|
|
||||||
* @param filename Output file path
|
|
||||||
* @param pixels Pixel data pointer
|
|
||||||
* @param width Image width
|
|
||||||
* @param height Image height
|
|
||||||
* @param channels Number of channels
|
|
||||||
* @param format Output format
|
|
||||||
* @return true if save succeeded
|
|
||||||
*/
|
|
||||||
bool save_image(const std::string& filename, const uint8_t* pixels,
|
|
||||||
int width, int height, int channels,
|
|
||||||
ImageFormat format = ImageFormat::ARE_IMAGE_FORMAT_PNG);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Detect image format from file extension
|
|
||||||
* @param filename File path
|
|
||||||
* @return Detected format
|
|
||||||
*/
|
|
||||||
ImageFormat detect_format(const std::string& filename);
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_IMAGE_IO_H
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
/**
|
|
||||||
* @file math_utils.h
|
|
||||||
* @brief Mathematical utility functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_MATH_UTILS_H
|
|
||||||
#define ARE_INCLUDE_UTILS_MATH_UTILS_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clamp value to range [min, max]
|
|
||||||
* @param value Value to clamp
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Clamped value
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
inline T clamp(T value, T min, T max) {
|
|
||||||
return std::max(min, std::min(value, max));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Linear interpolation
|
|
||||||
* @param a Start value
|
|
||||||
* @param b End value
|
|
||||||
* @param t Interpolation factor [0, 1]
|
|
||||||
* @return Interpolated value
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
inline T lerp(T a, T b, Real t) {
|
|
||||||
return a + (b - a) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert degrees to radians
|
|
||||||
* @param degrees Angle in degrees
|
|
||||||
* @return Angle in radians
|
|
||||||
*/
|
|
||||||
inline Real degrees_to_radians(Real degrees) {
|
|
||||||
return degrees * are_pi / 180.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert radians to degrees
|
|
||||||
* @param radians Angle in radians
|
|
||||||
* @return Angle in degrees
|
|
||||||
*/
|
|
||||||
inline Real radians_to_degrees(Real radians) {
|
|
||||||
return radians * 180.0f / are_pi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if two floating point values are approximately equal
|
|
||||||
* @param a First value
|
|
||||||
* @param b Second value
|
|
||||||
* @param epsilon Tolerance
|
|
||||||
* @return true if approximately equal
|
|
||||||
*/
|
|
||||||
inline bool approx_equal(Real a, Real b, Real epsilon = are_epsilon) {
|
|
||||||
return std::abs(a - b) < epsilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute barycentric coordinates
|
|
||||||
* @param p Point
|
|
||||||
* @param a Triangle vertex A
|
|
||||||
* @param b Triangle vertex B
|
|
||||||
* @param c Triangle vertex C
|
|
||||||
* @param u Output barycentric coordinate u
|
|
||||||
* @param v Output barycentric coordinate v
|
|
||||||
* @param w Output barycentric coordinate w
|
|
||||||
*/
|
|
||||||
void compute_barycentric(const Vec3& p, const Vec3& a, const Vec3& b, const Vec3& c,
|
|
||||||
Real& u, Real& v, Real& w);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reflect vector around normal
|
|
||||||
* @param incident Incident vector
|
|
||||||
* @param normal Surface normal (normalized)
|
|
||||||
* @return Reflected vector
|
|
||||||
*/
|
|
||||||
Vec3 reflect(const Vec3& incident, const Vec3& normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Refract vector through surface
|
|
||||||
* @param incident Incident vector (normalized)
|
|
||||||
* @param normal Surface normal (normalized)
|
|
||||||
* @param eta Ratio of refractive indices
|
|
||||||
* @param refracted Output refracted vector
|
|
||||||
* @return true if refraction occurred (false for total internal reflection)
|
|
||||||
*/
|
|
||||||
bool refract(const Vec3& incident, const Vec3& normal, Real eta, Vec3& refracted);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute Fresnel reflectance (Schlick approximation)
|
|
||||||
* @param cos_theta Cosine of angle between view and normal
|
|
||||||
* @param f0 Reflectance at normal incidence
|
|
||||||
* @return Fresnel reflectance
|
|
||||||
*/
|
|
||||||
Real fresnel_schlick(Real cos_theta, Real f0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create orthonormal basis from normal
|
|
||||||
* @param normal Normal vector (normalized)
|
|
||||||
* @param tangent Output tangent vector
|
|
||||||
* @param bitangent Output bitangent vector
|
|
||||||
*/
|
|
||||||
void create_orthonormal_basis(const Vec3& normal, Vec3& tangent, Vec3& bitangent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Transform direction from tangent space to world space
|
|
||||||
* @param tangent_dir Direction in tangent space
|
|
||||||
* @param normal Surface normal
|
|
||||||
* @param tangent Surface tangent
|
|
||||||
* @param bitangent Surface bitangent
|
|
||||||
* @return Direction in world space
|
|
||||||
*/
|
|
||||||
Vec3 tangent_to_world(const Vec3& tangent_dir, const Vec3& normal,
|
|
||||||
const Vec3& tangent, const Vec3& bitangent);
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_MATH_UTILS_H
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
/**
|
|
||||||
* @file random.h
|
|
||||||
* @brief Random number generation utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
#define ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class RandomGenerator
|
|
||||||
* @brief Thread-safe random number generator
|
|
||||||
*
|
|
||||||
* Uses PCG (Permuted Congruential Generator) for high-quality random numbers.
|
|
||||||
*/
|
|
||||||
class RandomGenerator {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor with optional seed
|
|
||||||
* @param seed Random seed (0 = use random device)
|
|
||||||
*/
|
|
||||||
explicit RandomGenerator(uint64_t seed = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [0, 1)
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
Real random_float();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [min, max)
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
Real random_float(Real min, Real max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random integer in [min, max]
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random integer
|
|
||||||
*/
|
|
||||||
int random_int(int min, int max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random point in unit disk
|
|
||||||
* @return Random point (z = 0)
|
|
||||||
*/
|
|
||||||
Vec3 random_in_unit_disk();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random point in unit sphere
|
|
||||||
* @return Random point
|
|
||||||
*/
|
|
||||||
Vec3 random_in_unit_sphere();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random unit vector
|
|
||||||
* @return Random unit vector
|
|
||||||
*/
|
|
||||||
Vec3 random_unit_vector();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random vector in hemisphere
|
|
||||||
* @param normal Hemisphere normal
|
|
||||||
* @return Random vector in hemisphere
|
|
||||||
*/
|
|
||||||
Vec3 random_in_hemisphere(const Vec3 &normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random cosine-weighted direction
|
|
||||||
* @param normal Surface normal
|
|
||||||
* @return Random direction (cosine-weighted)
|
|
||||||
*/
|
|
||||||
Vec3 random_cosine_direction(const Vec3 &normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set seed for reproducible results
|
|
||||||
* @param seed Random seed
|
|
||||||
*/
|
|
||||||
void set_seed(uint64_t seed);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mt19937_64 rng_; ///< Random number generator
|
|
||||||
std::uniform_real_distribution<Real> dist_; ///< Uniform distribution [0, 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get thread-local random generator
|
|
||||||
* @return Reference to thread-local generator
|
|
||||||
*/
|
|
||||||
RandomGenerator &get_thread_random();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [0, 1) using thread-local generator
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
inline Real random_float() {
|
|
||||||
return get_thread_random().random_float();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [min, max) using thread-local generator
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
inline Real random_float(Real min, Real max) {
|
|
||||||
return get_thread_random().random_float(min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
|
|
@ -1,854 +0,0 @@
|
||||||
我正在开发 Aurora Rendering Engine (ARE),一个 C++17 跨平台混合渲染引擎。
|
|
||||||
|
|
||||||
**核心特性**:
|
|
||||||
- 混合渲染:光栅化(G-Buffer)+ 光线追踪(GI/AO/软阴影)
|
|
||||||
- 双后端:CPU 和 Compute Shader 光线追踪,可运行时切换
|
|
||||||
- BVH 加速结构
|
|
||||||
- PBR 材质系统
|
|
||||||
- 工业级可扩展性
|
|
||||||
|
|
||||||
**技术栈**:
|
|
||||||
- C++17, OpenGL 4.3+, GLFW, GLAD, GLM, spdlog, stb_image
|
|
||||||
- 平台:Windows + Linux
|
|
||||||
- 构建:CMake 3.15+
|
|
||||||
|
|
||||||
**编码规范(NanoEra)**:
|
|
||||||
- 命名:函数/变量 `snake_case`,类 `PascalCase`,类成员后缀 `_`(但用户直接使用的结构体成员不加下划线)
|
|
||||||
- 全局宏前缀 `are_`,头文件保护 `ARE_INCLUDE_[路径]_H`
|
|
||||||
- 注释:全英文,重要接口用 Doxygen
|
|
||||||
- 禁用虚函数(性能优先,但不影响性能时可用)
|
|
||||||
|
|
||||||
**重要设计决策**:
|
|
||||||
1. 配置系统:纯代码配置,不支持文件加载
|
|
||||||
2. 错误处理:简化设计,直接输出,无堆栈累积
|
|
||||||
3. Shader 管理:外部文件,用户在 config 中指定路径
|
|
||||||
4. 场景数据:渲染器拷贝数据(用户友好)
|
|
||||||
5. BVH 构建:延迟构建(在 begin_frame 时检测并构建)
|
|
||||||
6. 材质系统:Mesh 持有 Material ID,支持复用
|
|
||||||
|
|
||||||
**文件目录树**:
|
|
||||||
```
|
|
||||||
aurora-rendering-engine/
|
|
||||||
├── include/ # 头文件目录
|
|
||||||
│ ├── are/ # 主命名空间目录
|
|
||||||
│ │ ├── are.h # 总头文件(包含所有公共接口)
|
|
||||||
│ │ ├── core/ # 核心模块
|
|
||||||
│ │ │ ├── config.h # 配置系统
|
|
||||||
│ │ │ ├── logger.h # 日志系统
|
|
||||||
│ │ │ ├── types.h # 基础类型定义
|
|
||||||
│ │ │ └── profiler.h # 性能分析器
|
|
||||||
│ │ ├── renderer/ # 渲染器模块
|
|
||||||
│ │ │ ├── renderer.h # 主渲染器接口
|
|
||||||
│ │ │ ├── render_context.h # 渲染上下文
|
|
||||||
│ │ │ └── render_stats.h # 渲染统计信息
|
|
||||||
│ │ ├── scene/ # 场景管理模块
|
|
||||||
│ │ │ ├── camera.h # 相机
|
|
||||||
│ │ │ ├── mesh.h # 网格
|
|
||||||
│ │ │ ├── material.h # 材质
|
|
||||||
│ │ │ ├── light.h # 光源(基类)
|
|
||||||
│ │ │ ├── directional_light.h # 平行光
|
|
||||||
│ │ │ ├── point_light.h # 点光源
|
|
||||||
│ │ │ ├── spot_light.h # 聚光灯
|
|
||||||
│ │ │ └── scene_manager.h # 场景管理器
|
|
||||||
│ │ ├── geometry/ # 几何处理模块
|
|
||||||
│ │ │ ├── vertex.h # 顶点结构
|
|
||||||
│ │ │ ├── triangle.h # 三角形
|
|
||||||
│ │ │ ├── aabb.h # 轴对齐包围盒
|
|
||||||
│ │ │ └── transform.h # 变换矩阵
|
|
||||||
│ │ ├── acceleration/ # 加速结构模块
|
|
||||||
│ │ │ ├── bvh.h # BVH 接口
|
|
||||||
│ │ │ ├── bvh_node.h # BVH 节点
|
|
||||||
│ │ │ └── bvh_builder.h # BVH 构建器
|
|
||||||
│ │ ├── rasterizer/ # 光栅化模块
|
|
||||||
│ │ │ ├── rasterizer.h # 光栅化器接口
|
|
||||||
│ │ │ ├── gbuffer.h # G-Buffer 管理
|
|
||||||
│ │ │ └── shader_program.h # Shader 程序封装
|
|
||||||
│ │ ├── raytracer/ # 光线追踪模块
|
|
||||||
│ │ │ ├── raytracer.h # 光线追踪器接口
|
|
||||||
│ │ │ ├── ray.h # 光线结构
|
|
||||||
│ │ │ ├── hit_record.h # 碰撞记录
|
|
||||||
│ │ │ ├── cpu_raytracer.h # CPU 光线追踪实现
|
|
||||||
│ │ │ └── compute_raytracer.h # Compute Shader 实现
|
|
||||||
│ │ ├── texture/ # 纹理模块
|
|
||||||
│ │ │ ├── texture.h # 纹理接口
|
|
||||||
│ │ │ ├── texture_manager.h # 纹理管理器
|
|
||||||
│ │ │ └── sampler.h # 采样器
|
|
||||||
│ │ ├── utils/ # 工具模块
|
|
||||||
│ │ │ ├── image_io.h # 图像读写
|
|
||||||
│ │ │ ├── random.h # 随机数生成
|
|
||||||
│ │ │ ├── math_utils.h # 数学工具函数
|
|
||||||
│ │ │ └── file_utils.h # 文件工具函数
|
|
||||||
│ │ └── platform/ # 平台相关模块
|
|
||||||
│ │ ├── window.h # 窗口管理
|
|
||||||
│ │ └── gl_context.h # OpenGL 上下文
|
|
||||||
│ └── extended_folders.list # 模块分类文件夹列表
|
|
||||||
│
|
|
||||||
├── src/ # 源代码目录
|
|
||||||
│ ├── core/
|
|
||||||
│ │ ├── config.cpp
|
|
||||||
│ │ ├── logger.cpp
|
|
||||||
│ │ └── profiler.cpp
|
|
||||||
│ ├── renderer/
|
|
||||||
│ │ ├── renderer.cpp
|
|
||||||
│ │ ├── render_context.cpp
|
|
||||||
│ │ └── render_stats.cpp
|
|
||||||
│ ├── scene/
|
|
||||||
│ │ ├── camera.cpp
|
|
||||||
│ │ ├── mesh.cpp
|
|
||||||
│ │ ├── material.cpp
|
|
||||||
│ │ ├── light.cpp
|
|
||||||
│ │ ├── directional_light.cpp
|
|
||||||
│ │ ├── point_light.cpp
|
|
||||||
│ │ ├── spot_light.cpp
|
|
||||||
│ │ └── scene_manager.cpp
|
|
||||||
│ ├── geometry/
|
|
||||||
│ │ ├── vertex.cpp
|
|
||||||
│ │ ├── triangle.cpp
|
|
||||||
│ │ ├── aabb.cpp
|
|
||||||
│ │ └── transform.cpp
|
|
||||||
│ ├── acceleration/
|
|
||||||
│ │ ├── bvh.cpp
|
|
||||||
│ │ ├── bvh_node.cpp
|
|
||||||
│ │ └── bvh_builder.cpp
|
|
||||||
│ ├── rasterizer/
|
|
||||||
│ │ ├── rasterizer.cpp
|
|
||||||
│ │ ├── gbuffer.cpp
|
|
||||||
│ │ └── shader_program.cpp
|
|
||||||
│ ├── raytracer/
|
|
||||||
│ │ ├── raytracer.cpp
|
|
||||||
│ │ ├── ray.cpp
|
|
||||||
│ │ ├── hit_record.cpp
|
|
||||||
│ │ ├── cpu_raytracer.cpp
|
|
||||||
│ │ └── compute_raytracer.cpp
|
|
||||||
│ ├── texture/
|
|
||||||
│ │ ├── texture.cpp
|
|
||||||
│ │ ├── texture_manager.cpp
|
|
||||||
│ │ └── sampler.cpp
|
|
||||||
│ ├── utils/
|
|
||||||
│ │ ├── image_io.cpp
|
|
||||||
│ │ ├── random.cpp
|
|
||||||
│ │ ├── math_utils.cpp
|
|
||||||
│ │ └── file_utils.cpp
|
|
||||||
│ └── platform/
|
|
||||||
│ ├── window.cpp
|
|
||||||
│ └── gl_context.cpp
|
|
||||||
│
|
|
||||||
├── shaders/ # Shader 文件目录
|
|
||||||
│ ├── gbuffer/
|
|
||||||
│ │ ├── gbuffer.vert # G-Buffer 顶点着色器
|
|
||||||
│ │ └── gbuffer.frag # G-Buffer 片段着色器
|
|
||||||
│ ├── raytracing/
|
|
||||||
│ │ └── raytracing.comp # 光线追踪计算着色器
|
|
||||||
│ ├── post_process/
|
|
||||||
│ │ ├── tonemap.vert # Tone Mapping 顶点着色器
|
|
||||||
│ │ ├── tonemap.frag # Tone Mapping 片段着色器
|
|
||||||
│ │ └── fullscreen_quad.vert # 全屏四边形顶点着色器
|
|
||||||
│ └── debug/
|
|
||||||
│ ├── visualize_gbuffer.frag # G-Buffer 可视化
|
|
||||||
│ └── visualize_bvh.frag # BVH 可视化
|
|
||||||
│
|
|
||||||
├── lib/ # 第三方库目录(用户自行安装)
|
|
||||||
│ ├── glfw/ # GLFW(用户提供)
|
|
||||||
│ ├── glad/ # GLAD(用户提供)
|
|
||||||
│ ├── glm/ # GLM(用户提供)
|
|
||||||
│ ├── spdlog/ # spdlog(用户提供)
|
|
||||||
│ └── stb/ # stb 库(项目包含)
|
|
||||||
│ ├── stb_image.h
|
|
||||||
│ └── stb_image_write.h
|
|
||||||
│
|
|
||||||
├── examples/ # 示例程序目录
|
|
||||||
│ ├── 01_hello_triangle/
|
|
||||||
│ │ ├── main.cpp # 基础三角形示例
|
|
||||||
│ │ └── CMakeLists.txt
|
|
||||||
│ ├── 02_cornell_box/
|
|
||||||
│ │ ├── main.cpp # Cornell Box 示例
|
|
||||||
│ │ └── CMakeLists.txt
|
|
||||||
│ ├── 03_material_showcase/
|
|
||||||
│ │ ├── main.cpp # 材质展示示例
|
|
||||||
│ │ └── CMakeLists.txt
|
|
||||||
│ └── 04_performance_test/
|
|
||||||
│ ├── main.cpp # 性能测试示例
|
|
||||||
│ └── CMakeLists.txt
|
|
||||||
│
|
|
||||||
├── experiments/ # 实验和测试记录目录
|
|
||||||
│ └── .gitkeep
|
|
||||||
│
|
|
||||||
├── docs/ # 文档目录
|
|
||||||
│ ├── API.md # API 文档
|
|
||||||
│ ├── ARCHITECTURE.md # 架构设计文档
|
|
||||||
│ ├── BUILD.md # 构建指南
|
|
||||||
│ └── CODING_STYLE.md # 编码规范
|
|
||||||
│
|
|
||||||
├── scripts/ # 脚本目录
|
|
||||||
│ ├── export_modules_requirements.sh # 导出模块依赖
|
|
||||||
│ ├── check_code.sh # 代码规范检查
|
|
||||||
│ └── setup_dependencies.sh # 依赖配置脚本(您后续编写)
|
|
||||||
│
|
|
||||||
├── res/ # 资源文件目录
|
|
||||||
│ └── icon.png # 项目图标
|
|
||||||
│
|
|
||||||
├── CMakeLists.txt # 主 CMake 配置文件
|
|
||||||
├── .gitignore # Git 忽略文件
|
|
||||||
├── .gitmodules # Git 子模块配置(如果使用)
|
|
||||||
├── LICENSE # 许可证
|
|
||||||
└── README.md # 项目说明
|
|
||||||
```
|
|
||||||
如下是模块间依赖关系示意图:
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Renderer │
|
|
||||||
│ (主渲染器接口) │
|
|
||||||
└────────────┬────────────────────────────────┬───────────────┘
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
┌────────────────┐ ┌─────────────────┐
|
|
||||||
│ Rasterizer │ │ RayTracer │
|
|
||||||
│ (光栅化器) │ │ (光线追踪器) │
|
|
||||||
└────────┬───────┘ └────────┬────────┘
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
┌────────────────┐ ┌─────────────────┐
|
|
||||||
│ GBuffer │ │ CPU/Compute │
|
|
||||||
│ (G-Buffer) │ │ (双实现) │
|
|
||||||
└────────────────┘ └────────┬────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────┐
|
|
||||||
│ BVH │
|
|
||||||
│ (加速结构) │
|
|
||||||
└────────┬────────┘
|
|
||||||
│
|
|
||||||
┌───────────────────────────────┴───────────────┐
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
┌────────────────┐ ┌─────────────────┐
|
|
||||||
│ SceneManager │ │ Geometry │
|
|
||||||
│ (场景管理) │ │ (几何数据) │
|
|
||||||
└────────┬───────┘ └─────────────────┘
|
|
||||||
│
|
|
||||||
├─────► Camera (相机)
|
|
||||||
├─────► Mesh (网格)
|
|
||||||
├─────► Material (材质)
|
|
||||||
└─────► Light (光源)
|
|
||||||
│
|
|
||||||
├─────► DirectionalLight
|
|
||||||
├─────► PointLight
|
|
||||||
└─────► SpotLight
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 支撑模块 (底层) │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ Core: Config, Logger, Profiler, Types │
|
|
||||||
│ Texture: TextureManager, Sampler │
|
|
||||||
│ Utils: ImageIO, Random, MathUtils, FileUtils │
|
|
||||||
│ Platform: Window, GLContext │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
关于项目实现架构,我有以下步骤:
|
|
||||||
### Phase 1: 基础设施搭建(Week 1-2)
|
|
||||||
**目标**:建立项目骨架,确保编译通过
|
|
||||||
|
|
||||||
#### Step 1.1: 核心模块
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/core/types.h # 基础类型定义
|
|
||||||
- include/are/core/config.h # 配置系统
|
|
||||||
- include/are/core/logger.h # 日志系统
|
|
||||||
- src/core/config.cpp
|
|
||||||
- src/core/logger.cpp
|
|
||||||
|
|
||||||
依赖: spdlog
|
|
||||||
|
|
||||||
验证: 能够创建 Config 对象并输出日志
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 1.2: 平台层
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/platform/window.h # 窗口管理
|
|
||||||
- include/are/platform/gl_context.h # OpenGL 上下文
|
|
||||||
- src/platform/window.cpp
|
|
||||||
- src/platform/gl_context.cpp
|
|
||||||
|
|
||||||
依赖: GLFW, GLAD
|
|
||||||
|
|
||||||
验证: 能够创建窗口并初始化 OpenGL 上下文
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 1.3: 工具模块
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/utils/math_utils.h # 数学工具
|
|
||||||
- include/are/utils/file_utils.h # 文件工具
|
|
||||||
- include/are/utils/random.h # 随机数生成
|
|
||||||
- src/utils/*.cpp
|
|
||||||
|
|
||||||
依赖: GLM
|
|
||||||
|
|
||||||
验证: 基础工具函数可用
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 2: 几何与场景系统(Week 2-3)
|
|
||||||
|
|
||||||
**目标**:能够构建和管理场景数据
|
|
||||||
|
|
||||||
#### Step 2.1: 几何数据结构
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/geometry/vertex.h # 顶点结构
|
|
||||||
- include/are/geometry/triangle.h # 三角形
|
|
||||||
- include/are/geometry/aabb.h # 包围盒
|
|
||||||
- include/are/geometry/transform.h # 变换
|
|
||||||
- src/geometry/*.cpp
|
|
||||||
|
|
||||||
依赖: GLM
|
|
||||||
|
|
||||||
验证: 能够创建和操作几何数据
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 2.2: 场景对象
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/scene/camera.h # 相机
|
|
||||||
- include/are/scene/mesh.h # 网格
|
|
||||||
- include/are/scene/material.h # 材质
|
|
||||||
- include/are/scene/light.h # 光源基类
|
|
||||||
- include/are/scene/directional_light.h
|
|
||||||
- include/are/scene/point_light.h
|
|
||||||
- include/are/scene/spot_light.h
|
|
||||||
- src/scene/*.cpp
|
|
||||||
|
|
||||||
依赖: geometry
|
|
||||||
|
|
||||||
验证: 能够创建场景对象
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 2.3: 场景管理器
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/scene/scene_manager.h
|
|
||||||
- src/scene/scene_manager.cpp
|
|
||||||
|
|
||||||
依赖: scene/*
|
|
||||||
|
|
||||||
验证: 能够添加/删除/查询场景对象
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 3: 光栅化管线(Week 3-4)
|
|
||||||
|
|
||||||
**目标**:实现 G-Buffer 生成
|
|
||||||
|
|
||||||
#### Step 3.1: Shader 管理
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/rasterizer/shader_program.h
|
|
||||||
- src/rasterizer/shader_program.cpp
|
|
||||||
- shaders/gbuffer/gbuffer.vert
|
|
||||||
- shaders/gbuffer/gbuffer.frag
|
|
||||||
|
|
||||||
依赖: platform/gl_context
|
|
||||||
|
|
||||||
验证: 能够加载和编译 Shader
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3.2: G-Buffer
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/rasterizer/gbuffer.h
|
|
||||||
- src/rasterizer/gbuffer.cpp
|
|
||||||
|
|
||||||
依赖: shader_program
|
|
||||||
|
|
||||||
验证: 能够创建 G-Buffer 纹理
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3.3: 光栅化器
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/rasterizer/rasterizer.h
|
|
||||||
- src/rasterizer/rasterizer.cpp
|
|
||||||
|
|
||||||
依赖: gbuffer, scene_manager
|
|
||||||
|
|
||||||
验证: 能够渲染场景到 G-Buffer
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 4: 加速结构(Week 4-5)
|
|
||||||
|
|
||||||
**目标**:实现 BVH 构建
|
|
||||||
|
|
||||||
#### Step 4.1: BVH 节点
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/acceleration/bvh_node.h
|
|
||||||
- src/acceleration/bvh_node.cpp
|
|
||||||
|
|
||||||
依赖: geometry/aabb
|
|
||||||
|
|
||||||
验证: 能够创建 BVH 节点
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 4.2: BVH 构建器
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/acceleration/bvh_builder.h
|
|
||||||
- src/acceleration/bvh_builder.cpp
|
|
||||||
|
|
||||||
依赖: bvh_node, scene/mesh
|
|
||||||
|
|
||||||
验证: 能够从三角形列表构建 BVH(使用 SAH 或中点分割)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 4.3: BVH 接口
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/acceleration/bvh.h
|
|
||||||
- src/acceleration/bvh.cpp
|
|
||||||
|
|
||||||
依赖: bvh_builder
|
|
||||||
|
|
||||||
验证: 提供统一的 BVH 查询接口
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 5: CPU 光线追踪(Week 5-6)
|
|
||||||
|
|
||||||
**目标**:实现基础的 CPU 光线追踪
|
|
||||||
|
|
||||||
#### Step 5.1: 光线与碰撞
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/raytracer/ray.h
|
|
||||||
- include/are/raytracer/hit_record.h
|
|
||||||
- src/raytracer/ray.cpp
|
|
||||||
- src/raytracer/hit_record.cpp
|
|
||||||
|
|
||||||
依赖: geometry
|
|
||||||
|
|
||||||
验证: 能够进行光线-三角形相交测试
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 5.2: CPU 光线追踪器
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/raytracer/cpu_raytracer.h
|
|
||||||
- src/raytracer/cpu_raytracer.cpp
|
|
||||||
|
|
||||||
依赖: ray, hit_record, bvh, scene_manager
|
|
||||||
|
|
||||||
验证: 能够追踪光线并计算基础着色(漫反射)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 5.3: 多线程优化
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
修改: src/raytracer/cpu_raytracer.cpp
|
|
||||||
|
|
||||||
依赖: OpenMP 或 std::thread
|
|
||||||
|
|
||||||
验证: 多线程加速光线追踪
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6: Compute Shader 光线追踪(Week 6-7)
|
|
||||||
|
|
||||||
**目标**:实现 GPU 光线追踪
|
|
||||||
|
|
||||||
#### Step 6.1: Compute Shader 编写
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- shaders/raytracing/raytracing.comp
|
|
||||||
|
|
||||||
依赖: OpenGL 4.3+
|
|
||||||
|
|
||||||
验证: Shader 能够编译通过
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 6.2: GPU 数据传输
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/raytracer/compute_raytracer.h
|
|
||||||
- src/raytracer/compute_raytracer.cpp
|
|
||||||
|
|
||||||
依赖: rasterizer/shader_program, bvh
|
|
||||||
|
|
||||||
验证: 能够将 BVH 和场景数据上传到 GPU(SSBO)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 6.3: GPU 光线追踪实现
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
修改: shaders/raytracing/raytracing.comp
|
|
||||||
src/raytracer/compute_raytracer.cpp
|
|
||||||
|
|
||||||
验证: GPU 光线追踪能够产生正确结果
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 7: 主渲染器集成(Week 7-8)
|
|
||||||
|
|
||||||
**目标**:整合所有模块
|
|
||||||
|
|
||||||
#### Step 7.1: 渲染上下文
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/renderer/render_context.h
|
|
||||||
- include/are/renderer/render_stats.h
|
|
||||||
- src/renderer/render_context.cpp
|
|
||||||
- src/renderer/render_stats.cpp
|
|
||||||
|
|
||||||
依赖: 所有模块
|
|
||||||
|
|
||||||
验证: 能够管理渲染状态
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 7.2: 主渲染器
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- include/are/renderer/renderer.h
|
|
||||||
- src/renderer/renderer.cpp
|
|
||||||
|
|
||||||
依赖: rasterizer, raytracer, scene_manager
|
|
||||||
|
|
||||||
验证: 能够完成完整的混合渲染流程
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 7.3: 后处理(Tone Mapping)
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- shaders/post_process/tonemap.vert
|
|
||||||
- shaders/post_process/tonemap.frag
|
|
||||||
|
|
||||||
修改: src/renderer/renderer.cpp
|
|
||||||
|
|
||||||
验证: HDR 到 LDR 的转换
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 8: 纹理系统(Week 8-9)
|
|
||||||
|
|
||||||
**目标**:支持纹理贴图
|
|
||||||
|
|
||||||
#### Step 8.1: 图像 I/O
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/utils/image_io.h
|
|
||||||
- src/utils/image_io.cpp
|
|
||||||
|
|
||||||
依赖: stb_image, stb_image_write
|
|
||||||
|
|
||||||
验证: 能够加载和保存图像
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 8.2: 纹理管理
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/texture/texture.h
|
|
||||||
- include/are/texture/texture_manager.h
|
|
||||||
- include/are/texture/sampler.h
|
|
||||||
- src/texture/*.cpp
|
|
||||||
|
|
||||||
依赖: image_io
|
|
||||||
|
|
||||||
验证: 能够加载纹理并在材质中使用
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 8.3: 集成到渲染管线
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
修改: src/rasterizer/rasterizer.cpp
|
|
||||||
src/raytracer/cpu_raytracer.cpp
|
|
||||||
shaders/raytracing/raytracing.comp
|
|
||||||
|
|
||||||
验证: 纹理能够正确显示
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 9: 高级光线追踪特性(Week 9-11)
|
|
||||||
|
|
||||||
**目标**:实现高质量渲染特性
|
|
||||||
|
|
||||||
#### Step 9.1: 环境光遮蔽(AO)
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
修改: src/raytracer/cpu_raytracer.cpp
|
|
||||||
shaders/raytracing/raytracing.comp
|
|
||||||
|
|
||||||
验证: AO 效果正确
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 9.2: 软阴影
|
|
||||||
```
|
|
||||||
优先级: ★★★☆☆
|
|
||||||
修改: 光线追踪器实现
|
|
||||||
|
|
||||||
验证: 软阴影效果
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 9.3: 镜面反射
|
|
||||||
```
|
|
||||||
优先级: ★★★☆☆
|
|
||||||
修改: 光线追踪器实现
|
|
||||||
|
|
||||||
验证: 镜面反射效果
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 9.4: 折射/透明
|
|
||||||
```
|
|
||||||
优先级: ★★☆☆☆
|
|
||||||
修改: 光线追踪器实现
|
|
||||||
|
|
||||||
验证: 透明材质效果
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 10: 调试与性能分析(Week 11-12)
|
|
||||||
|
|
||||||
**目标**:提供调试工具和性能优化
|
|
||||||
|
|
||||||
#### Step 10.1: 性能分析器
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- include/are/core/profiler.h
|
|
||||||
- src/core/profiler.cpp
|
|
||||||
|
|
||||||
依赖: logger
|
|
||||||
|
|
||||||
验证: 能够测量各阶段耗时
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 10.2: 调试可视化
|
|
||||||
```
|
|
||||||
优先级: ★★★☆☆
|
|
||||||
文件:
|
|
||||||
- shaders/debug/visualize_gbuffer.frag
|
|
||||||
- shaders/debug/visualize_bvh.frag
|
|
||||||
|
|
||||||
修改: src/renderer/renderer.cpp
|
|
||||||
|
|
||||||
验证: 能够可视化 G-Buffer 各通道和 BVH 包围盒
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 10.3: 帧捕获功能
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
修改: src/renderer/renderer.cpp
|
|
||||||
src/utils/image_io.cpp
|
|
||||||
|
|
||||||
验证: 能够保存渲染结果到文件
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 11: 示例程序(Week 12-13)
|
|
||||||
|
|
||||||
**目标**:提供完整的使用示例
|
|
||||||
|
|
||||||
#### Step 11.1: Hello Triangle
|
|
||||||
```
|
|
||||||
优先级: ★★★★★
|
|
||||||
文件:
|
|
||||||
- examples/01_hello_triangle/main.cpp
|
|
||||||
- examples/01_hello_triangle/CMakeLists.txt
|
|
||||||
|
|
||||||
验证: 最简单的三角形渲染
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 11.2: Cornell Box
|
|
||||||
```
|
|
||||||
优先级: ★★★★☆
|
|
||||||
文件:
|
|
||||||
- examples/02_cornell_box/main.cpp
|
|
||||||
- examples/02_cornell_box/CMakeLists.txt
|
|
||||||
|
|
||||||
验证: 经典的 Cornell Box 场景,展示全局光照
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 11.3: Material Showcase
|
|
||||||
```
|
|
||||||
优先级: ★★★☆☆
|
|
||||||
文件:
|
|
||||||
- examples/03_material_showcase/main.cpp
|
|
||||||
- examples/03_material_showcase/CMakeLists.txt
|
|
||||||
|
|
||||||
验证: 展示不同材质参数的效果
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 11.4: Performance Test
|
|
||||||
```
|
|
||||||
优先级: ★★★☆☆
|
|
||||||
文件:
|
|
||||||
- examples/04_performance_test/main.cpp
|
|
||||||
- examples/04_performance_test/CMakeLists.txt
|
|
||||||
|
|
||||||
验证: CPU vs GPU 性能对比
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
接下来是开发策略说明
|
|
||||||
|
|
||||||
### 开发原则
|
|
||||||
|
|
||||||
1. **自底向上构建**:
|
|
||||||
- 先实现底层模块(core, utils, platform)
|
|
||||||
- 再实现数据层(geometry, scene)
|
|
||||||
- 最后实现算法层和接口层
|
|
||||||
|
|
||||||
2. **增量验证**:
|
|
||||||
- 每个 Phase 完成后都有明确的验证目标
|
|
||||||
- 通过简单的测试程序验证功能正确性
|
|
||||||
- 避免大量代码堆积后才发现问题
|
|
||||||
|
|
||||||
3. **并行开发可能性**:
|
|
||||||
- Phase 3(光栅化)和 Phase 4(BVH)可以并行开发
|
|
||||||
- Phase 5(CPU 光追)和 Phase 6(GPU 光追)可以并行开发
|
|
||||||
- 但建议先完成 CPU 版本,再实现 GPU 版本(便于调试)
|
|
||||||
|
|
||||||
4. **性能优先**:
|
|
||||||
- 初期实现注重正确性
|
|
||||||
- 后期通过 Profiler 识别瓶颈
|
|
||||||
- 针对性优化热点代码
|
|
||||||
|
|
||||||
### 关键里程碑
|
|
||||||
|
|
||||||
**Milestone 1(Week 4)**:能够渲染静态场景到 G-Buffer
|
|
||||||
```cpp
|
|
||||||
// 验证代码
|
|
||||||
are::Renderer renderer(config);
|
|
||||||
renderer.add_mesh(triangle_mesh);
|
|
||||||
renderer.begin_frame();
|
|
||||||
renderer.render_gbuffer(); // 只渲染 G-Buffer
|
|
||||||
renderer.end_frame();
|
|
||||||
renderer.save_frame("gbuffer.png");
|
|
||||||
```
|
|
||||||
|
|
||||||
**Milestone 2(Week 6)**:CPU 光线追踪能够产生正确结果
|
|
||||||
```cpp
|
|
||||||
// 验证代码
|
|
||||||
config.ray_tracing.backend = are::ARE_RT_BACKEND_CPU;
|
|
||||||
config.ray_tracing.spp = 16;
|
|
||||||
renderer.render(); // 完整的混合渲染
|
|
||||||
renderer.save_frame("cpu_result.png");
|
|
||||||
```
|
|
||||||
|
|
||||||
**Milestone 3(Week 8)**:GPU 光线追踪能够产生正确结果
|
|
||||||
```cpp
|
|
||||||
// 验证代码
|
|
||||||
config.ray_tracing.backend = are::ARE_RT_BACKEND_COMPUTE_SHADER;
|
|
||||||
renderer.render();
|
|
||||||
renderer.save_frame("gpu_result.png");
|
|
||||||
// 对比 CPU 和 GPU 结果,误差应在可接受范围内
|
|
||||||
```
|
|
||||||
|
|
||||||
**Milestone 4(Week 10)**:支持纹理和多种光源
|
|
||||||
```cpp
|
|
||||||
// 验证代码
|
|
||||||
material.set_albedo_map("brick.png");
|
|
||||||
renderer.add_light(directional_light);
|
|
||||||
renderer.add_light(point_light);
|
|
||||||
renderer.render();
|
|
||||||
```
|
|
||||||
|
|
||||||
**Milestone 5(Week 14)**:项目完成,所有示例可运行
|
|
||||||
|
|
||||||
### 风险控制
|
|
||||||
|
|
||||||
**潜在风险**:
|
|
||||||
1. **BVH 构建性能**:如果场景复杂,构建时间可能过长
|
|
||||||
- 缓解:使用多线程构建,考虑增量更新
|
|
||||||
|
|
||||||
2. **GPU 内存限制**:大场景可能超出 GPU 内存
|
|
||||||
- 缓解:实现场景分块,按需加载
|
|
||||||
|
|
||||||
3. **Compute Shader 调试困难**:GPU 代码难以调试
|
|
||||||
- 缓解:先在 CPU 上验证算法正确性,再移植到 GPU
|
|
||||||
|
|
||||||
4. **跨平台兼容性**:Windows 和 Linux 的差异
|
|
||||||
- 缓解:使用 CMake 和标准库,避免平台特定代码
|
|
||||||
|
|
||||||
接下来是一些小总结
|
|
||||||
|
|
||||||
### 项目规模估算
|
|
||||||
- **总文件数**:约 80-100 个文件(头文件 + 源文件 + Shader)
|
|
||||||
- **代码量**:约 15,000-20,000 行(不含注释和空行)
|
|
||||||
- **开发周期**:14 周(约 3.5 个月)
|
|
||||||
- **核心模块**:10 个(core, platform, utils, geometry, scene, acceleration, rasterizer, raytracer, texture, renderer)
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
- **语言**:C++17
|
|
||||||
- **图形 API**:OpenGL 4.3+
|
|
||||||
- **依赖库**:GLFW, GLAD, GLM, spdlog, stb
|
|
||||||
- **构建系统**:CMake 3.15+
|
|
||||||
- **并行**:OpenMP / std::thread
|
|
||||||
|
|
||||||
### 设计亮点
|
|
||||||
1. **模块化设计**:清晰的层次结构,低耦合高内聚
|
|
||||||
2. **双后端支持**:CPU 和 GPU 光线追踪可运行时切换
|
|
||||||
3. **工业级可扩展性**:预留接口,便于后期添加新特性
|
|
||||||
4. **性能优先**:BVH 加速、多线程、GPU 计算
|
|
||||||
5. **易于调试**:完善的日志系统、性能分析、可视化工具
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
此外,还有一些额外要求:
|
|
||||||
### ⚠️ 关键修改记录
|
|
||||||
1. **config.h 成员变量已去除下划线**
|
|
||||||
- 原因:用户直接使用的结构体成员不加下划线
|
|
||||||
- 影响:所有访问 config 的代码都使用无下划线版本
|
|
||||||
- 示例:`config.window.width` 而不是 `config.window.width_`
|
|
||||||
|
|
||||||
### 📌 API 一致性检查清单
|
|
||||||
在实现时应该:
|
|
||||||
- [ ] 检查头文件中声明的所有函数是否都实现
|
|
||||||
- [ ] 不创造头文件中未声明的公共函数
|
|
||||||
- [ ] 成员变量命名符合规范(类成员变量后缀 `_`,用户结构体,即public成员变量不加)
|
|
||||||
- [ ] 所有公共 API 都有 Doxygen 注释
|
|
||||||
- [ ] 错误处理使用 `ARE_LOG_ERROR`
|
|
||||||
- [ ] 性能关键函数使用 `ARE_PROFILE_FUNCTION()`
|
|
||||||
|
|
||||||
### 🎯 Phase 2 成功标准
|
|
||||||
- [ ] 所有源文件编译通过(0 错误 0 警告)
|
|
||||||
- [ ] 验证程序能够创建场景对象
|
|
||||||
- [ ] 相机能够生成光线
|
|
||||||
- [ ] 网格能够计算 AABB
|
|
||||||
- [ ] 光源能够打包数据
|
|
||||||
- [ ] 场景管理器能够管理对象
|
|
||||||
|
|
||||||
对于目前的进度,我们已经实现到了Phase 4,正在实现Phase 5,请你帮我们进行Phase 5以及后续代码的实现。
|
|
||||||
但是因为我还没有给你我们已有的代码,因此你需要哪些头文件的代码?请在下一轮向我询问,然后我们会开始Phase 5的开发。
|
|
||||||
605
phase3.md
605
phase3.md
|
|
@ -1,605 +0,0 @@
|
||||||
### 文件:include/are/platform/window.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file window.h
|
|
||||||
* @brief Window management using GLFW
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
#define ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Forward declare GLFW types to avoid including GLFW in header
|
|
||||||
struct GLFWwindow;
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Window
|
|
||||||
* @brief GLFW window wrapper
|
|
||||||
*
|
|
||||||
* Manages window creation, input handling, and OpenGL context.
|
|
||||||
*/
|
|
||||||
class Window {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Window configuration
|
|
||||||
*/
|
|
||||||
explicit Window(const WindowConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~Window();
|
|
||||||
|
|
||||||
// Window control
|
|
||||||
bool should_close() const;
|
|
||||||
void set_should_close(bool should_close);
|
|
||||||
void swap_buffers();
|
|
||||||
void poll_events();
|
|
||||||
|
|
||||||
// Window properties
|
|
||||||
int get_width() const;
|
|
||||||
int get_height() const;
|
|
||||||
Real get_aspect_ratio() const;
|
|
||||||
const std::string& get_title() const;
|
|
||||||
|
|
||||||
void set_title(const std::string& title);
|
|
||||||
void set_size(int width, int height);
|
|
||||||
|
|
||||||
// Framebuffer size (may differ from window size on high-DPI displays)
|
|
||||||
void get_framebuffer_size(int& width, int& height) const;
|
|
||||||
|
|
||||||
// VSync control
|
|
||||||
void set_vsync(bool enabled);
|
|
||||||
bool get_vsync() const;
|
|
||||||
|
|
||||||
// Input queries (basic support)
|
|
||||||
bool is_key_pressed(int key) const;
|
|
||||||
bool is_mouse_button_pressed(int button) const;
|
|
||||||
void get_cursor_pos(double& x, double& y) const;
|
|
||||||
|
|
||||||
// Internal
|
|
||||||
GLFWwindow* get_native_window() const { return window_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize_glfw();
|
|
||||||
void create_window();
|
|
||||||
void setup_callbacks();
|
|
||||||
|
|
||||||
static void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
|
||||||
static void error_callback(int error, const char* description);
|
|
||||||
|
|
||||||
GLFWwindow* window_; ///< GLFW window handle
|
|
||||||
WindowConfig config_; ///< Window configuration
|
|
||||||
bool vsync_enabled_; ///< VSync state
|
|
||||||
|
|
||||||
static int instance_count_; ///< Number of Window instances
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_PLATFORM_WINDOW_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/platform/gl_context.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file gl_context.h
|
|
||||||
* @brief OpenGL context management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
#define ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class GLContext
|
|
||||||
* @brief OpenGL context initialization and management
|
|
||||||
*
|
|
||||||
* Handles GLAD initialization and provides OpenGL utility functions.
|
|
||||||
*/
|
|
||||||
class GLContext {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Initialize OpenGL context (load function pointers)
|
|
||||||
* @return true if initialization succeeded
|
|
||||||
*/
|
|
||||||
static bool initialize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if context is initialized
|
|
||||||
* @return true if initialized
|
|
||||||
*/
|
|
||||||
static bool is_initialized();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL version string
|
|
||||||
* @return Version string
|
|
||||||
*/
|
|
||||||
static std::string get_version();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL renderer string
|
|
||||||
* @return Renderer string
|
|
||||||
*/
|
|
||||||
static std::string get_renderer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL vendor string
|
|
||||||
* @return Vendor string
|
|
||||||
*/
|
|
||||||
static std::string get_vendor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if OpenGL extension is supported
|
|
||||||
* @param extension Extension name
|
|
||||||
* @return true if supported
|
|
||||||
*/
|
|
||||||
static bool is_extension_supported(const std::string& extension);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Print OpenGL information to console
|
|
||||||
*/
|
|
||||||
static void print_info();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check for OpenGL errors
|
|
||||||
* @param file Source file
|
|
||||||
* @param line Line number
|
|
||||||
* @return true if error occurred
|
|
||||||
*/
|
|
||||||
static bool check_error(const char* file, int line);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all OpenGL errors
|
|
||||||
*/
|
|
||||||
static void clear_errors();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool initialized_; ///< Initialization flag
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
// OpenGL error checking macro
|
|
||||||
#ifdef ARE_ENABLE_DEBUG_VIS
|
|
||||||
#define ARE_GL_CHECK() are::GLContext::check_error(__FILE__, __LINE__)
|
|
||||||
#else
|
|
||||||
#define ARE_GL_CHECK() ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_PLATFORM_GL_CONTEXT_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/utils/file_utils.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file file_utils.h
|
|
||||||
* @brief File system utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
#define ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read entire file into string
|
|
||||||
* @param filepath File path
|
|
||||||
* @return File contents (empty if failed)
|
|
||||||
*/
|
|
||||||
std::string read_file_to_string(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read entire file into byte array
|
|
||||||
* @param filepath File path
|
|
||||||
* @return File contents (empty if failed)
|
|
||||||
*/
|
|
||||||
std::vector<uint8_t> read_file_to_bytes(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write string to file
|
|
||||||
* @param filepath File path
|
|
||||||
* @param content Content to write
|
|
||||||
* @return true if write succeeded
|
|
||||||
*/
|
|
||||||
bool write_string_to_file(const std::string& filepath, const std::string& content);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write bytes to file
|
|
||||||
* @param filepath File path
|
|
||||||
* @param data Data pointer
|
|
||||||
* @param size Data size in bytes
|
|
||||||
* @return true if write succeeded
|
|
||||||
*/
|
|
||||||
bool write_bytes_to_file(const std::string& filepath, const void* data, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if file exists
|
|
||||||
* @param filepath File path
|
|
||||||
* @return true if file exists
|
|
||||||
*/
|
|
||||||
bool file_exists(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if path is directory
|
|
||||||
* @param path Directory path
|
|
||||||
* @return true if directory exists
|
|
||||||
*/
|
|
||||||
bool is_directory(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create directory (including parent directories)
|
|
||||||
* @param path Directory path
|
|
||||||
* @return true if creation succeeded
|
|
||||||
*/
|
|
||||||
bool create_directory(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get file extension
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Extension (lowercase, without dot)
|
|
||||||
*/
|
|
||||||
std::string get_file_extension(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get filename from path
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Filename (without directory)
|
|
||||||
*/
|
|
||||||
std::string get_filename(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get directory from path
|
|
||||||
* @param filepath File path
|
|
||||||
* @return Directory path
|
|
||||||
*/
|
|
||||||
std::string get_directory(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Join path components
|
|
||||||
* @param parts Path components
|
|
||||||
* @return Joined path
|
|
||||||
*/
|
|
||||||
std::string join_path(const std::vector<std::string>& parts);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Normalize path (resolve .. and .)
|
|
||||||
* @param path Path to normalize
|
|
||||||
* @return Normalized path
|
|
||||||
*/
|
|
||||||
std::string normalize_path(const std::string& path);
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_FILE_UTILS_H
|
|
||||||
```
|
|
||||||
|
|
||||||
这是你所要求的三个头文件,如果还需要更多头文件的代码,请随时向我提出。同时,平台层着三个函数的代码我也已经实现,你只需要专心考虑渲染管线即可。此外,渲染管线的头文件我也实现了,你只需要负责实现渲染管线的shader以及代码实现即可。
|
|
||||||
### 文件:include/are/rasterizer/shader_program.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file shader_program.h
|
|
||||||
* @brief OpenGL shader program wrapper
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum ShaderType
|
|
||||||
* @brief Shader stage types
|
|
||||||
*/
|
|
||||||
enum class ShaderType {
|
|
||||||
ARE_SHADER_VERTEX,
|
|
||||||
ARE_SHADER_FRAGMENT,
|
|
||||||
ARE_SHADER_COMPUTE
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ShaderProgram
|
|
||||||
* @brief OpenGL shader program management
|
|
||||||
*/
|
|
||||||
class ShaderProgram {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
ShaderProgram();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~ShaderProgram();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load and compile shader from file
|
|
||||||
* @param type Shader type
|
|
||||||
* @param filepath Shader file path
|
|
||||||
* @return true if compilation succeeded
|
|
||||||
*/
|
|
||||||
bool load_shader(ShaderType type, const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compile shader from source string
|
|
||||||
* @param type Shader type
|
|
||||||
* @param source Shader source code
|
|
||||||
* @return true if compilation succeeded
|
|
||||||
*/
|
|
||||||
bool compile_shader(ShaderType type, const std::string& source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Link shader program
|
|
||||||
* @return true if linking succeeded
|
|
||||||
*/
|
|
||||||
bool link();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Use this shader program
|
|
||||||
*/
|
|
||||||
void use() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if program is valid
|
|
||||||
* @return true if valid
|
|
||||||
*/
|
|
||||||
bool is_valid() const { return program_ != 0 && linked_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get OpenGL program ID
|
|
||||||
* @return Program ID
|
|
||||||
*/
|
|
||||||
uint32_t get_program() const { return program_; }
|
|
||||||
|
|
||||||
// Uniform setters
|
|
||||||
void set_uniform(const std::string& name, int value);
|
|
||||||
void set_uniform(const std::string& name, float value);
|
|
||||||
void set_uniform(const std::string& name, const Vec2& value);
|
|
||||||
void set_uniform(const std::string& name, const Vec3& value);
|
|
||||||
void set_uniform(const std::string& name, const Vec4& value);
|
|
||||||
void set_uniform(const std::string& name, const Mat3& value);
|
|
||||||
void set_uniform(const std::string& name, const Mat4& value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get uniform location (cached)
|
|
||||||
* @param name Uniform name
|
|
||||||
* @return Uniform location (-1 if not found)
|
|
||||||
*/
|
|
||||||
int get_uniform_location(const std::string& name);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool check_compile_errors(uint32_t shader, ShaderType type);
|
|
||||||
bool check_link_errors();
|
|
||||||
|
|
||||||
uint32_t program_; ///< OpenGL program ID
|
|
||||||
uint32_t vertex_shader_; ///< Vertex shader ID
|
|
||||||
uint32_t fragment_shader_; ///< Fragment shader ID
|
|
||||||
uint32_t compute_shader_; ///< Compute shader ID
|
|
||||||
|
|
||||||
bool linked_; ///< Link status
|
|
||||||
std::unordered_map<std::string, int> uniform_cache_; ///< Uniform location cache
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_SHADER_PROGRAM_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/rasterizer/gbuffer.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file gbuffer.h
|
|
||||||
* @brief G-Buffer management for deferred rendering
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class GBuffer
|
|
||||||
* @brief G-Buffer for deferred rendering
|
|
||||||
*
|
|
||||||
* Contains multiple render targets for position, normal, albedo, etc.
|
|
||||||
*/
|
|
||||||
class GBuffer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param width Buffer width
|
|
||||||
* @param height Buffer height
|
|
||||||
*/
|
|
||||||
GBuffer(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~GBuffer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Resize G-Buffer
|
|
||||||
* @param width New width
|
|
||||||
* @param height New height
|
|
||||||
*/
|
|
||||||
void resize(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Bind G-Buffer for rendering
|
|
||||||
*/
|
|
||||||
void bind();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unbind G-Buffer
|
|
||||||
*/
|
|
||||||
void unbind();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all buffers
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Bind texture for reading
|
|
||||||
* @param index Texture index (0=position, 1=normal, 2=albedo, etc.)
|
|
||||||
* @param texture_unit Texture unit to bind to
|
|
||||||
*/
|
|
||||||
void bind_texture(int index, int texture_unit);
|
|
||||||
|
|
||||||
// Texture getters
|
|
||||||
uint32_t get_position_texture() const { return position_texture_; }
|
|
||||||
uint32_t get_normal_texture() const { return normal_texture_; }
|
|
||||||
uint32_t get_albedo_texture() const { return albedo_texture_; }
|
|
||||||
uint32_t get_material_texture() const { return material_texture_; }
|
|
||||||
uint32_t get_depth_texture() const { return depth_texture_; }
|
|
||||||
|
|
||||||
// Dimensions
|
|
||||||
int get_width() const { return width_; }
|
|
||||||
int get_height() const { return height_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read pixel data from G-Buffer
|
|
||||||
* @param index Buffer index
|
|
||||||
* @param data Output data pointer
|
|
||||||
*/
|
|
||||||
void read_pixels(int index, void* data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void create_textures();
|
|
||||||
void delete_textures();
|
|
||||||
void create_framebuffer();
|
|
||||||
|
|
||||||
uint32_t fbo_; ///< Framebuffer object
|
|
||||||
uint32_t rbo_depth_; ///< Depth renderbuffer
|
|
||||||
|
|
||||||
// G-Buffer textures
|
|
||||||
uint32_t position_texture_; ///< World position (RGB16F)
|
|
||||||
uint32_t normal_texture_; ///< World normal (RGB16F)
|
|
||||||
uint32_t albedo_texture_; ///< Albedo + Metallic (RGBA8)
|
|
||||||
uint32_t material_texture_; ///< Roughness + AO (RG8)
|
|
||||||
uint32_t depth_texture_; ///< Depth (R32F)
|
|
||||||
|
|
||||||
int width_; ///< Buffer width
|
|
||||||
int height_; ///< Buffer height
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_GBUFFER_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/rasterizer/rasterizer.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file rasterizer.h
|
|
||||||
* @brief Rasterization pipeline for G-Buffer generation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
#define ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class GBuffer;
|
|
||||||
class ShaderProgram;
|
|
||||||
class SceneManager;
|
|
||||||
class Camera;
|
|
||||||
class Mesh;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Rasterizer
|
|
||||||
* @brief OpenGL rasterization pipeline
|
|
||||||
*
|
|
||||||
* Renders scene geometry to G-Buffer using traditional rasterization.
|
|
||||||
*/
|
|
||||||
class Rasterizer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param width Framebuffer width
|
|
||||||
* @param height Framebuffer height
|
|
||||||
*/
|
|
||||||
Rasterizer(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~Rasterizer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Resize framebuffer
|
|
||||||
* @param width New width
|
|
||||||
* @param height New height
|
|
||||||
*/
|
|
||||||
void resize(int width, int height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene to G-Buffer
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
*/
|
|
||||||
void render_gbuffer(const SceneManager& scene, const Camera& camera);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get G-Buffer
|
|
||||||
* @return G-Buffer reference
|
|
||||||
*/
|
|
||||||
GBuffer& get_gbuffer();
|
|
||||||
const GBuffer& get_gbuffer() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Upload mesh data to GPU
|
|
||||||
* @param mesh Mesh to upload
|
|
||||||
*/
|
|
||||||
void upload_mesh(Mesh& mesh);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Delete mesh GPU resources
|
|
||||||
* @param mesh Mesh to delete
|
|
||||||
*/
|
|
||||||
void delete_mesh(Mesh& mesh);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize_shaders(const std::string& shader_dir);
|
|
||||||
void setup_mesh_buffers(Mesh& mesh);
|
|
||||||
|
|
||||||
std::unique_ptr<GBuffer> gbuffer_; ///< G-Buffer
|
|
||||||
std::unique_ptr<ShaderProgram> gbuffer_shader_; ///< G-Buffer shader
|
|
||||||
|
|
||||||
int width_; ///< Framebuffer width
|
|
||||||
int height_; ///< Framebuffer height
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RASTERIZER_RASTERIZER_H
|
|
||||||
```
|
|
||||||
|
|
||||||
如果没有还需要我给出的内容的话,你就可以开始实现了。
|
|
||||||
270
phase4.md
270
phase4.md
|
|
@ -1,270 +0,0 @@
|
||||||
很好!让我们来实现Phase 4吧!
|
|
||||||
如下是我已经实现的Phase 4头文件:
|
|
||||||
### 文件:include/are/acceleration/bvh_node.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file bvh_node.h
|
|
||||||
* @brief BVH node structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct BVHNode
|
|
||||||
* @brief Node in Bounding Volume Hierarchy
|
|
||||||
*
|
|
||||||
* Uses a compact representation for efficient GPU transfer.
|
|
||||||
*/
|
|
||||||
struct BVHNode {
|
|
||||||
AABB bounds_; ///< Node bounding box
|
|
||||||
|
|
||||||
union {
|
|
||||||
uint32_t left_child_; ///< Left child index (internal node)
|
|
||||||
uint32_t first_primitive_; ///< First primitive index (leaf node)
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
uint32_t right_child_; ///< Right child index (internal node)
|
|
||||||
uint32_t primitive_count_; ///< Number of primitives (leaf node)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if node is a leaf
|
|
||||||
* @return true if leaf node
|
|
||||||
*/
|
|
||||||
bool is_leaf() const {
|
|
||||||
return primitive_count_ > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get node surface area (for SAH)
|
|
||||||
* @return Surface area
|
|
||||||
*/
|
|
||||||
Real surface_area() const {
|
|
||||||
return bounds_.surface_area();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_NODE_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/acceleration/bvh_builder.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file bvh_builder.h
|
|
||||||
* @brief BVH construction algorithms
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/acceleration/bvh_node.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum BVHSplitMethod
|
|
||||||
* @brief BVH splitting strategies
|
|
||||||
*/
|
|
||||||
enum class BVHSplitMethod {
|
|
||||||
ARE_BVH_SPLIT_MIDDLE, ///< Split at midpoint
|
|
||||||
ARE_BVH_SPLIT_SAH ///< Surface Area Heuristic
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct BVHBuildConfig
|
|
||||||
* @brief Configuration for BVH construction
|
|
||||||
*/
|
|
||||||
struct BVHBuildConfig {
|
|
||||||
BVHSplitMethod split_method_ = BVHSplitMethod::ARE_BVH_SPLIT_SAH;
|
|
||||||
int max_leaf_size_ = 4; ///< Maximum triangles per leaf
|
|
||||||
int max_depth_ = 64; ///< Maximum tree depth
|
|
||||||
bool use_multithreading_ = true; ///< Use parallel construction
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class BVHBuilder
|
|
||||||
* @brief Constructs BVH from triangle list
|
|
||||||
*/
|
|
||||||
class BVHBuilder {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Build configuration
|
|
||||||
*/
|
|
||||||
explicit BVHBuilder(const BVHBuildConfig& config = BVHBuildConfig());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Build BVH from triangles
|
|
||||||
* @param triangles Triangle list
|
|
||||||
* @param nodes Output node list
|
|
||||||
* @param primitive_indices Output primitive index list
|
|
||||||
* @return Root node index
|
|
||||||
*/
|
|
||||||
uint32_t build(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get build statistics
|
|
||||||
* @param node_count Output node count
|
|
||||||
* @param leaf_count Output leaf count
|
|
||||||
* @param max_depth Output maximum depth reached
|
|
||||||
*/
|
|
||||||
void get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct BuildEntry {
|
|
||||||
uint32_t parent_;
|
|
||||||
uint32_t start_;
|
|
||||||
uint32_t end_;
|
|
||||||
int depth_;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t build_recursive(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices,
|
|
||||||
uint32_t start, uint32_t end, int depth);
|
|
||||||
|
|
||||||
int find_best_split_axis(const std::vector<Triangle>& triangles,
|
|
||||||
const std::vector<uint32_t>& indices,
|
|
||||||
uint32_t start, uint32_t end);
|
|
||||||
|
|
||||||
Real compute_sah_cost(const AABB& bounds, uint32_t count);
|
|
||||||
|
|
||||||
BVHBuildConfig config_;
|
|
||||||
size_t node_count_;
|
|
||||||
size_t leaf_count_;
|
|
||||||
int max_depth_reached_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_BUILDER_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/acceleration/bvh.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file bvh.h
|
|
||||||
* @brief BVH interface and traversal
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
#define ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/acceleration/bvh_node.h>
|
|
||||||
#include <are/acceleration/bvh_builder.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class BVH
|
|
||||||
* @brief Bounding Volume Hierarchy for ray tracing acceleration
|
|
||||||
*/
|
|
||||||
class BVH {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*/
|
|
||||||
BVH();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~BVH();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Build BVH from triangle list
|
|
||||||
* @param triangles Triangle list
|
|
||||||
* @param config Build configuration
|
|
||||||
* @return true if build succeeded
|
|
||||||
*/
|
|
||||||
bool build(const std::vector<Triangle>& triangles,
|
|
||||||
const BVHBuildConfig& config = BVHBuildConfig());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Traverse BVH and find closest intersection
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param hit Output hit record
|
|
||||||
* @return true if intersection found
|
|
||||||
*/
|
|
||||||
bool intersect(const Ray& ray, HitRecord& hit) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Fast occlusion test (any hit)
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
* @return true if any intersection found
|
|
||||||
*/
|
|
||||||
bool intersect_any(const Ray& ray, Real t_max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if BVH is built
|
|
||||||
* @return true if built
|
|
||||||
*/
|
|
||||||
bool is_built() const { return !nodes_.empty(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get BVH nodes (for GPU upload)
|
|
||||||
* @return Node array
|
|
||||||
*/
|
|
||||||
const std::vector<BVHNode>& get_nodes() const { return nodes_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get primitive indices
|
|
||||||
* @return Index array
|
|
||||||
*/
|
|
||||||
const std::vector<uint32_t>& get_primitive_indices() const {
|
|
||||||
return primitive_indices_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get triangles
|
|
||||||
* @return Triangle array
|
|
||||||
*/
|
|
||||||
const std::vector<Triangle>& get_triangles() const { return triangles_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get memory usage in bytes
|
|
||||||
* @return Memory usage
|
|
||||||
*/
|
|
||||||
size_t get_memory_usage() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear BVH data
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool intersect_recursive(uint32_t node_index, const Ray& ray, HitRecord& hit) const;
|
|
||||||
bool intersect_any_recursive(uint32_t node_index, const Ray& ray, Real t_max) const;
|
|
||||||
|
|
||||||
std::vector<BVHNode> nodes_; ///< BVH nodes
|
|
||||||
std::vector<uint32_t> primitive_indices_; ///< Primitive index array
|
|
||||||
std::vector<Triangle> triangles_; ///< Triangle data
|
|
||||||
uint32_t root_index_; ///< Root node index
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_ACCELERATION_BVH_H
|
|
||||||
```
|
|
||||||
|
|
||||||
如果有依赖或者需要给你的头文件或实现文件我还没有给你,欢迎提出!
|
|
||||||
310
phase5.md
310
phase5.md
|
|
@ -1,310 +0,0 @@
|
||||||
很好!现在让我们开始Phase 5的实现吧!如下是Phase 5我已经写好的头文件与依赖文件(注:ray.h/cpp&hit_record.h/cpp在前面的代码中你已经实现了,现在只需要再检查一遍之前的实现是否正确即可):
|
|
||||||
### 文件:include/are/raytracer/cpu_raytracer.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file cpu_raytracer.h
|
|
||||||
* @brief CPU-based ray tracing implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
|
|
||||||
#include <are/raytracer/raytracer.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class CPURayTracer
|
|
||||||
* @brief CPU-based ray tracing implementation
|
|
||||||
*
|
|
||||||
* Uses multithreading for parallel ray tracing on CPU.
|
|
||||||
*/
|
|
||||||
class CPURayTracer : public RayTracer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Ray tracing configuration
|
|
||||||
*/
|
|
||||||
explicit CPURayTracer(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~CPURayTracer() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene using CPU ray tracing
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
* @param gbuffer G-Buffer (optional)
|
|
||||||
* @param output Output texture ID
|
|
||||||
*/
|
|
||||||
void render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update BVH
|
|
||||||
* @param bvh BVH reference
|
|
||||||
*/
|
|
||||||
void update_bvh(const BVH& bvh) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Trace a single ray
|
|
||||||
* @param ray Ray to trace
|
|
||||||
* @param depth Current recursion depth
|
|
||||||
* @return Ray color
|
|
||||||
*/
|
|
||||||
Vec3 trace_ray(const Ray& ray, int depth);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shade hit point
|
|
||||||
* @param hit Hit record
|
|
||||||
* @param ray Incident ray
|
|
||||||
* @param depth Current recursion depth
|
|
||||||
* @return Shaded color
|
|
||||||
*/
|
|
||||||
Vec3 shade(const HitRecord& hit, const Ray& ray, int depth);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute direct lighting
|
|
||||||
* @param hit Hit record
|
|
||||||
* @return Direct lighting contribution
|
|
||||||
*/
|
|
||||||
Vec3 compute_direct_lighting(const HitRecord& hit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compute ambient occlusion
|
|
||||||
* @param hit Hit record
|
|
||||||
* @return AO factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real compute_ambient_occlusion(const HitRecord& hit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check shadow ray
|
|
||||||
* @param origin Shadow ray origin
|
|
||||||
* @param direction Shadow ray direction
|
|
||||||
* @param max_distance Maximum distance
|
|
||||||
* @return true if in shadow
|
|
||||||
*/
|
|
||||||
bool is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance);
|
|
||||||
|
|
||||||
const BVH* bvh_; ///< BVH reference
|
|
||||||
const SceneManager* scene_; ///< Scene reference
|
|
||||||
std::vector<Vec3> framebuffer_; ///< CPU framebuffer (HDR)
|
|
||||||
int width_; ///< Framebuffer width
|
|
||||||
int height_; ///< Framebuffer height
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_CPU_RAYTRACER_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/raytracer/raytracer.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file raytracer.h
|
|
||||||
* @brief Ray tracing interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <are/core/config.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class SceneManager;
|
|
||||||
class Camera;
|
|
||||||
class GBuffer;
|
|
||||||
class BVH;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class RayTracer
|
|
||||||
* @brief Abstract ray tracing interface
|
|
||||||
*
|
|
||||||
* Base class for CPU and GPU ray tracing implementations.
|
|
||||||
*/
|
|
||||||
class RayTracer {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
* @param config Ray tracing configuration
|
|
||||||
*/
|
|
||||||
explicit RayTracer(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Virtual destructor
|
|
||||||
*/
|
|
||||||
virtual ~RayTracer() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render scene using ray tracing
|
|
||||||
* @param scene Scene manager
|
|
||||||
* @param camera Camera
|
|
||||||
* @param gbuffer G-Buffer (optional, for hybrid rendering)
|
|
||||||
* @param output Output texture ID
|
|
||||||
*/
|
|
||||||
virtual void render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update BVH
|
|
||||||
* @param bvh BVH reference
|
|
||||||
*/
|
|
||||||
virtual void update_bvh(const BVH& bvh) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set configuration
|
|
||||||
* @param config New configuration
|
|
||||||
*/
|
|
||||||
virtual void set_config(const RayTracingConfig& config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get configuration
|
|
||||||
* @return Current configuration
|
|
||||||
*/
|
|
||||||
const RayTracingConfig& get_config() const { return config_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
RayTracingConfig config_; ///< Ray tracing configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_RAYTRACER_H
|
|
||||||
```
|
|
||||||
此外,你可能需要随机数模块。
|
|
||||||
### 文件:include/utils/random.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file random.h
|
|
||||||
* @brief Random number generation utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
#define ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class RandomGenerator
|
|
||||||
* @brief Thread-safe random number generator
|
|
||||||
*
|
|
||||||
* Uses PCG (Permuted Congruential Generator) for high-quality random numbers.
|
|
||||||
*/
|
|
||||||
class RandomGenerator {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor with optional seed
|
|
||||||
* @param seed Random seed (0 = use random device)
|
|
||||||
*/
|
|
||||||
explicit RandomGenerator(uint64_t seed = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [0, 1)
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
Real random_float();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [min, max)
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
Real random_float(Real min, Real max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random integer in [min, max]
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random integer
|
|
||||||
*/
|
|
||||||
int random_int(int min, int max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random point in unit disk
|
|
||||||
* @return Random point (z = 0)
|
|
||||||
*/
|
|
||||||
Vec3 random_in_unit_disk();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random point in unit sphere
|
|
||||||
* @return Random point
|
|
||||||
*/
|
|
||||||
Vec3 random_in_unit_sphere();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random unit vector
|
|
||||||
* @return Random unit vector
|
|
||||||
*/
|
|
||||||
Vec3 random_unit_vector();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random vector in hemisphere
|
|
||||||
* @param normal Hemisphere normal
|
|
||||||
* @return Random vector in hemisphere
|
|
||||||
*/
|
|
||||||
Vec3 random_in_hemisphere(const Vec3 &normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random cosine-weighted direction
|
|
||||||
* @param normal Surface normal
|
|
||||||
* @return Random direction (cosine-weighted)
|
|
||||||
*/
|
|
||||||
Vec3 random_cosine_direction(const Vec3 &normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set seed for reproducible results
|
|
||||||
* @param seed Random seed
|
|
||||||
*/
|
|
||||||
void set_seed(uint64_t seed);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mt19937_64 rng_; ///< Random number generator
|
|
||||||
std::uniform_real_distribution<Real> dist_; ///< Uniform distribution [0, 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get thread-local random generator
|
|
||||||
* @return Reference to thread-local generator
|
|
||||||
*/
|
|
||||||
RandomGenerator &get_thread_random();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [0, 1) using thread-local generator
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
inline Real random_float() {
|
|
||||||
return get_thread_random().random_float();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate random float in [min, max) using thread-local generator
|
|
||||||
* @param min Minimum value
|
|
||||||
* @param max Maximum value
|
|
||||||
* @return Random float
|
|
||||||
*/
|
|
||||||
inline Real random_float(Real min, Real max) {
|
|
||||||
return get_thread_random().random_float(min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_UTILS_RANDOM_H
|
|
||||||
```
|
|
||||||
|
|
||||||
同样地,如果有依赖或者需要给你的头文件/实现文件还没有给你,欢迎提出;如果已有的数学或者随机数模块需要补充,请分别给出函数声明与实现。
|
|
||||||
322
request2.md
322
request2.md
|
|
@ -1,322 +0,0 @@
|
||||||
你对代码的观察非常敏锐!我对你的实现计划表示认同,下面是你要求到的和可能用到的头文件:
|
|
||||||
### 文件:include/are/raytracer/ray.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file ray.h
|
|
||||||
* @brief Ray structure for ray tracing
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct Ray
|
|
||||||
* @brief Ray representation for ray tracing
|
|
||||||
*/
|
|
||||||
struct Ray {
|
|
||||||
Vec3 origin_; ///< Ray origin
|
|
||||||
Vec3 direction_; ///< Ray direction (normalized)
|
|
||||||
Real t_min_; ///< Minimum t value
|
|
||||||
Real t_max_; ///< Maximum t value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
Ray();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct ray with origin and direction
|
|
||||||
* @param origin Ray origin
|
|
||||||
* @param direction Ray direction (will be normalized)
|
|
||||||
* @param t_min Minimum t value
|
|
||||||
* @param t_max Maximum t value
|
|
||||||
*/
|
|
||||||
Ray(const Vec3& origin, const Vec3& direction,
|
|
||||||
Real t_min = are_epsilon, Real t_max = 1e30f);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Evaluate ray at parameter t
|
|
||||||
* @param t Parameter value
|
|
||||||
* @return Point on ray
|
|
||||||
*/
|
|
||||||
Vec3 at(Real t) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if t is within valid range
|
|
||||||
* @param t Parameter value
|
|
||||||
* @return true if t is valid
|
|
||||||
*/
|
|
||||||
bool is_valid_t(Real t) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_RAY_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/raytracer/hit_record.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file hit_record.h
|
|
||||||
* @brief Ray-surface intersection record
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
#define ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct HitRecord
|
|
||||||
* @brief Information about ray-surface intersection
|
|
||||||
*/
|
|
||||||
struct HitRecord {
|
|
||||||
Vec3 position_; ///< Hit position in world space
|
|
||||||
Vec3 normal_; ///< Surface normal at hit point
|
|
||||||
Vec2 texcoord_; ///< Texture coordinates at hit point
|
|
||||||
Vec3 tangent_; ///< Tangent vector at hit point
|
|
||||||
Real t_; ///< Ray parameter at hit point
|
|
||||||
MaterialHandle material_; ///< Material at hit point
|
|
||||||
uint32_t triangle_index_; ///< Triangle index that was hit
|
|
||||||
bool front_face_; ///< Whether ray hit front face
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
HitRecord();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set face normal based on ray direction
|
|
||||||
* @param ray_direction Ray direction
|
|
||||||
* @param outward_normal Outward-facing normal
|
|
||||||
*/
|
|
||||||
void set_face_normal(const Vec3& ray_direction, const Vec3& outward_normal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if hit record is valid
|
|
||||||
* @return true if hit occurred
|
|
||||||
*/
|
|
||||||
bool is_valid() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_RAYTRACER_HIT_RECORD_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/scene/directional_light.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file directional_light.h
|
|
||||||
* @brief Directional light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class DirectionalLight
|
|
||||||
* @brief Directional light source (sun-like)
|
|
||||||
*
|
|
||||||
* Represents an infinitely distant light source with parallel rays.
|
|
||||||
*/
|
|
||||||
class DirectionalLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
DirectionalLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with direction and color
|
|
||||||
* @param direction Light direction (will be normalized)
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
*/
|
|
||||||
DirectionalLight(const Vec3& direction, const Vec3& color = Vec3(1.0f),
|
|
||||||
Real intensity = 1.0f);
|
|
||||||
|
|
||||||
// Direction
|
|
||||||
void set_direction(const Vec3& direction);
|
|
||||||
const Vec3& get_direction() const { return direction_; }
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 direction_; ///< Light direction (normalized)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_DIRECTIONAL_LIGHT_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/scene/point_light.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file point_light.h
|
|
||||||
* @brief Point light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class PointLight
|
|
||||||
* @brief Point light source
|
|
||||||
*
|
|
||||||
* Emits light equally in all directions from a single point.
|
|
||||||
*/
|
|
||||||
class PointLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
PointLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with position and color
|
|
||||||
* @param position Light position
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
* @param range Light range (attenuation distance)
|
|
||||||
*/
|
|
||||||
PointLight(const Vec3& position, const Vec3& color = Vec3(1.0f),
|
|
||||||
Real intensity = 1.0f, Real range = 10.0f);
|
|
||||||
|
|
||||||
// Position
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
|
|
||||||
// Range (attenuation)
|
|
||||||
void set_range(Real range);
|
|
||||||
Real get_range() const { return range_; }
|
|
||||||
|
|
||||||
// Attenuation parameters
|
|
||||||
void set_attenuation(Real constant, Real linear, Real quadratic);
|
|
||||||
Real get_constant_attenuation() const { return attenuation_constant_; }
|
|
||||||
Real get_linear_attenuation() const { return attenuation_linear_; }
|
|
||||||
Real get_quadratic_attenuation() const { return attenuation_quadratic_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate attenuation at given distance
|
|
||||||
* @param distance Distance from light
|
|
||||||
* @return Attenuation factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real calculate_attenuation(Real distance) const;
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 position_; ///< Light position
|
|
||||||
Real range_; ///< Light range
|
|
||||||
Real attenuation_constant_; ///< Constant attenuation factor
|
|
||||||
Real attenuation_linear_; ///< Linear attenuation factor
|
|
||||||
Real attenuation_quadratic_; ///< Quadratic attenuation factor
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_POINT_LIGHT_H
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件:include/are/scene/spot_light.h
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @file spot_light.h
|
|
||||||
* @brief Spot light implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
#define ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class SpotLight
|
|
||||||
* @brief Spot light source
|
|
||||||
*
|
|
||||||
* Emits light in a cone from a single point.
|
|
||||||
*/
|
|
||||||
class SpotLight : public Light {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Default constructor
|
|
||||||
*/
|
|
||||||
SpotLight();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct with position, direction, and angles
|
|
||||||
* @param position Light position
|
|
||||||
* @param direction Light direction
|
|
||||||
* @param inner_angle Inner cone angle in degrees
|
|
||||||
* @param outer_angle Outer cone angle in degrees
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
*/
|
|
||||||
SpotLight(const Vec3& position, const Vec3& direction,Real inner_angle, Real outer_angle,
|
|
||||||
const Vec3& color = Vec3(1.0f), Real intensity = 1.0f);
|
|
||||||
|
|
||||||
// Position and direction
|
|
||||||
void set_position(const Vec3& position);
|
|
||||||
void set_direction(const Vec3& direction);
|
|
||||||
const Vec3& get_position() const { return position_; }
|
|
||||||
const Vec3& get_direction() const { return direction_; }
|
|
||||||
|
|
||||||
// Cone angles (in degrees)
|
|
||||||
void set_inner_angle(Real angle);
|
|
||||||
void set_outer_angle(Real angle);
|
|
||||||
Real get_inner_angle() const { return inner_angle_; }
|
|
||||||
Real get_outer_angle() const { return outer_angle_; }
|
|
||||||
|
|
||||||
// Range
|
|
||||||
void set_range(Real range);
|
|
||||||
Real get_range() const { return range_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate spotlight intensity at given direction
|
|
||||||
* @param to_point Direction from light to point (normalized)
|
|
||||||
* @return Spotlight factor [0, 1]
|
|
||||||
*/
|
|
||||||
Real calculate_spot_factor(const Vec3& to_point) const;
|
|
||||||
|
|
||||||
// Light interface
|
|
||||||
LightData pack() const override;
|
|
||||||
bool affects_point(const Vec3& point) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec3 position_; ///< Light position
|
|
||||||
Vec3 direction_; ///< Light direction (normalized)
|
|
||||||
Real inner_angle_; ///< Inner cone angle (degrees)
|
|
||||||
Real outer_angle_; ///< Outer cone angle (degrees)
|
|
||||||
Real range_; ///< Light range
|
|
||||||
Real cos_inner_; ///< Cosine of inner angle (cache
|
|
||||||
Real cos_outer_; ///< Cosine of outer angle (cached)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
||||||
#endif // ARE_INCLUDE_SCENE_SPOT_LIGHT_H
|
|
||||||
```
|
|
||||||
|
|
||||||
还有缺失的头文件需要补充吗?如果没有的话,我们就可以开始实现Phase 2了。
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#version 430 core
|
|
||||||
|
|
||||||
in vec3 v_world_position;
|
|
||||||
in vec3 v_world_normal;
|
|
||||||
in vec2 v_texcoord;
|
|
||||||
flat in uint v_triangle_id_base;
|
|
||||||
|
|
||||||
uniform vec3 u_albedo;
|
|
||||||
uniform float u_metallic;
|
|
||||||
uniform float u_roughness;
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 g_position;
|
|
||||||
layout(location = 1) out vec3 g_normal;
|
|
||||||
layout(location = 2) out vec4 g_albedo_metallic;
|
|
||||||
layout(location = 3) out vec2 g_roughness_ao;
|
|
||||||
layout(location = 4) out uint g_primitive_id;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
g_position = v_world_position;
|
|
||||||
g_normal = normalize(v_world_normal);
|
|
||||||
g_albedo_metallic = vec4(u_albedo, u_metallic);
|
|
||||||
g_roughness_ao = vec2(u_roughness, 1.0);
|
|
||||||
|
|
||||||
// Global primitive ID = mesh base + primitive id within draw call
|
|
||||||
g_primitive_id = v_triangle_id_base + uint(gl_PrimitiveID);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#version 430 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 a_position;
|
|
||||||
layout(location = 1) in vec3 a_normal;
|
|
||||||
layout(location = 2) in vec2 a_texcoord;
|
|
||||||
layout(location = 3) in vec3 a_tangent;
|
|
||||||
|
|
||||||
uniform mat4 u_model;
|
|
||||||
uniform mat4 u_view;
|
|
||||||
uniform mat4 u_projection;
|
|
||||||
uniform mat3 u_normal_matrix;
|
|
||||||
|
|
||||||
// NOTE: We store it as int uniform in C++ for simplicity; GLSL expects uint.
|
|
||||||
uniform uint u_triangle_id_base;
|
|
||||||
|
|
||||||
out vec3 v_world_position;
|
|
||||||
out vec3 v_world_normal;
|
|
||||||
out vec2 v_texcoord;
|
|
||||||
flat out uint v_triangle_id_base;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 world_pos = u_model * vec4(a_position, 1.0);
|
|
||||||
v_world_position = world_pos.xyz;
|
|
||||||
v_world_normal = normalize(u_normal_matrix * a_normal);
|
|
||||||
v_texcoord = a_texcoord;
|
|
||||||
v_triangle_id_base = u_triangle_id_base;
|
|
||||||
|
|
||||||
gl_Position = u_projection * u_view * world_pos;
|
|
||||||
}
|
|
||||||
|
|
@ -1,512 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh.cpp
|
|
||||||
* @brief Implementation of BVH class (optimized version)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stack>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <are/acceleration/bvh.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
BVH::BVH()
|
|
||||||
: root_index_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BVH::~BVH() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::build(const std::vector<Triangle> &triangles, const BVHBuildConfig &config) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (triangles.empty()) {
|
|
||||||
ARE_LOG_WARN("BVH: Cannot build from empty triangle list");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear existing data
|
|
||||||
clear();
|
|
||||||
|
|
||||||
// Copy triangles
|
|
||||||
triangles_ = triangles;
|
|
||||||
|
|
||||||
// Build BVH
|
|
||||||
BVHBuilder builder(config);
|
|
||||||
root_index_ = builder.build(triangles_, nodes_, primitive_indices_);
|
|
||||||
|
|
||||||
// Get statistics
|
|
||||||
size_t node_count, leaf_count;
|
|
||||||
int max_depth;
|
|
||||||
builder.get_stats(node_count, leaf_count, max_depth);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("BVH: Built successfully");
|
|
||||||
ARE_LOG_INFO(" Triangles: " + std::to_string(triangles_.size()));
|
|
||||||
ARE_LOG_INFO(" Nodes: " + std::to_string(node_count));
|
|
||||||
ARE_LOG_INFO(" Leaves: " + std::to_string(leaf_count));
|
|
||||||
ARE_LOG_INFO(" Max depth: " + std::to_string(max_depth));
|
|
||||||
ARE_LOG_INFO(" Memory: " + std::to_string(get_memory_usage() / 1024) + " KB");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect(const Ray &ray, HitRecord &hit) const {
|
|
||||||
// Note: No profiling here - this is a hot path
|
|
||||||
|
|
||||||
if (!is_built()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use iterative traversal with stack for better performance
|
|
||||||
return intersect_iterative(ray, hit);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect_any(const Ray &ray, Real t_max) const {
|
|
||||||
// Note: No profiling here - this is a hot path
|
|
||||||
|
|
||||||
if (!is_built()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return intersect_any_iterative(ray, t_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect_any(const Ray& ray, Real t_max, uint32_t ignore_triangle_index) const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (!is_built()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterative traversal is safer than recursion for deep trees, but keep style consistent
|
|
||||||
std::stack<uint32_t> stack;
|
|
||||||
stack.push(root_index_);
|
|
||||||
|
|
||||||
while (!stack.empty()) {
|
|
||||||
uint32_t node_index = stack.top();
|
|
||||||
stack.pop();
|
|
||||||
|
|
||||||
const BVHNode& node = nodes_[node_index];
|
|
||||||
|
|
||||||
Real t0, t1;
|
|
||||||
if (!node.bounds_.intersect_ray(ray, t0, t1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (t0 > t_max) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
|
||||||
if (prim_idx == ignore_triangle_index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prim_idx >= triangles_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (triangles_[prim_idx].intersect_fast(ray, t_max)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stack.push(node.left_child_);
|
|
||||||
stack.push(node.right_child_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t BVH::get_memory_usage() const {
|
|
||||||
size_t total = 0;
|
|
||||||
total += nodes_.size() * sizeof(BVHNode);
|
|
||||||
total += primitive_indices_.size() * sizeof(uint32_t);
|
|
||||||
total += triangles_.size() * sizeof(Triangle);
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BVH::clear() {
|
|
||||||
nodes_.clear();
|
|
||||||
primitive_indices_.clear();
|
|
||||||
triangles_.clear();
|
|
||||||
root_index_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect_iterative(const Ray &ray, HitRecord &hit) const {
|
|
||||||
// Precompute inverse direction for faster AABB tests
|
|
||||||
Vec3 inv_dir(
|
|
||||||
1.0f / ray.direction_.x,
|
|
||||||
1.0f / ray.direction_.y,
|
|
||||||
1.0f / ray.direction_.z);
|
|
||||||
|
|
||||||
// Stack-based traversal (64 levels is enough for most scenes)
|
|
||||||
uint32_t stack[64];
|
|
||||||
int stack_ptr = 0;
|
|
||||||
stack[stack_ptr++] = root_index_;
|
|
||||||
|
|
||||||
bool hit_anything = false;
|
|
||||||
Real closest_t = ray.t_max_;
|
|
||||||
|
|
||||||
while (stack_ptr > 0) {
|
|
||||||
uint32_t node_index = stack[--stack_ptr];
|
|
||||||
|
|
||||||
if (node_index >= nodes_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BVHNode &node = nodes_[node_index];
|
|
||||||
|
|
||||||
// Fast AABB test with precomputed inverse direction
|
|
||||||
Real t_min, t_max;
|
|
||||||
if (!intersect_aabb_fast(node.bounds_, ray, inv_dir, closest_t, t_min, t_max)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
// Test all primitives in leaf
|
|
||||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
|
||||||
|
|
||||||
if (prim_idx >= triangles_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Triangle &triangle = triangles_[prim_idx];
|
|
||||||
|
|
||||||
HitRecord temp_hit;
|
|
||||||
if (intersect_triangle_fast(triangle, ray, closest_t, temp_hit)) {
|
|
||||||
closest_t = temp_hit.t_;
|
|
||||||
hit = temp_hit;
|
|
||||||
hit.triangle_index_ = prim_idx;
|
|
||||||
hit_anything = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Push children to stack (far child first, so near child is processed first)
|
|
||||||
if (node.left_child_ >= nodes_.size() || node.right_child_ >= nodes_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BVHNode &left = nodes_[node.left_child_];
|
|
||||||
const BVHNode &right = nodes_[node.right_child_];
|
|
||||||
|
|
||||||
Real t_left_min, t_left_max;
|
|
||||||
Real t_right_min, t_right_max;
|
|
||||||
|
|
||||||
bool hit_left = intersect_aabb_fast(left.bounds_, ray, inv_dir, closest_t,
|
|
||||||
t_left_min, t_left_max);
|
|
||||||
bool hit_right = intersect_aabb_fast(right.bounds_, ray, inv_dir, closest_t,
|
|
||||||
t_right_min, t_right_max);
|
|
||||||
|
|
||||||
if (hit_left && hit_right) {
|
|
||||||
// Push far child first (so near child is popped first)
|
|
||||||
if (t_left_min < t_right_min) {
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.right_child_;
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.left_child_;
|
|
||||||
} else {
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.left_child_;
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.right_child_;
|
|
||||||
}
|
|
||||||
} else if (hit_left) {
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.left_child_;
|
|
||||||
} else if (hit_right) {
|
|
||||||
if (stack_ptr < 64)
|
|
||||||
stack[stack_ptr++] = node.right_child_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hit_anything;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect_any_iterative(const Ray &ray, Real t_max) const {
|
|
||||||
// Precompute inverse direction
|
|
||||||
Vec3 inv_dir(
|
|
||||||
1.0f / ray.direction_.x,
|
|
||||||
1.0f / ray.direction_.y,
|
|
||||||
1.0f / ray.direction_.z);
|
|
||||||
|
|
||||||
// Stack-based traversal
|
|
||||||
uint32_t stack[64];
|
|
||||||
int stack_ptr = 0;
|
|
||||||
stack[stack_ptr++] = root_index_;
|
|
||||||
|
|
||||||
while (stack_ptr > 0) {
|
|
||||||
uint32_t node_index = stack[--stack_ptr];
|
|
||||||
|
|
||||||
if (node_index >= nodes_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BVHNode &node = nodes_[node_index];
|
|
||||||
|
|
||||||
// Fast AABB test
|
|
||||||
Real t_min, t_max_box;
|
|
||||||
if (!intersect_aabb_fast(node.bounds_, ray, inv_dir, t_max, t_min, t_max_box)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
// Test all primitives in leaf
|
|
||||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
|
||||||
|
|
||||||
if (prim_idx >= triangles_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Triangle &triangle = triangles_[prim_idx];
|
|
||||||
|
|
||||||
if (triangle.intersect_fast(ray, t_max)) {
|
|
||||||
return true; // Early exit on first hit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Push both children
|
|
||||||
if (node.left_child_ < nodes_.size() && stack_ptr < 64) {
|
|
||||||
stack[stack_ptr++] = node.left_child_;
|
|
||||||
}
|
|
||||||
if (node.right_child_ < nodes_.size() && stack_ptr < 64) {
|
|
||||||
stack[stack_ptr++] = node.right_child_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool BVH::intersect_aabb_fast(const AABB &bounds, const Ray &ray,
|
|
||||||
const Vec3 &inv_dir, Real t_max,
|
|
||||||
Real &t_min_out, Real &t_max_out) const {
|
|
||||||
// Optimized slab method with precomputed inverse direction
|
|
||||||
Real t_min = ray.t_min_;
|
|
||||||
Real t_max_local = t_max;
|
|
||||||
|
|
||||||
// X axis
|
|
||||||
{
|
|
||||||
Real t0 = (bounds.min_.x - ray.origin_.x) * inv_dir.x;
|
|
||||||
Real t1 = (bounds.max_.x - ray.origin_.x) * inv_dir.x;
|
|
||||||
|
|
||||||
if (inv_dir.x < 0.0f) {
|
|
||||||
Real temp = t0;
|
|
||||||
t0 = t1;
|
|
||||||
t1 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min = std::max(t_min, t0);
|
|
||||||
t_max_local = std::min(t_max_local, t1);
|
|
||||||
|
|
||||||
if (t_max_local < t_min) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Y axis
|
|
||||||
{
|
|
||||||
Real t0 = (bounds.min_.y - ray.origin_.y) * inv_dir.y;
|
|
||||||
Real t1 = (bounds.max_.y - ray.origin_.y) * inv_dir.y;
|
|
||||||
|
|
||||||
if (inv_dir.y < 0.0f) {
|
|
||||||
Real temp = t0;
|
|
||||||
t0 = t1;
|
|
||||||
t1 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min = std::max(t_min, t0);
|
|
||||||
t_max_local = std::min(t_max_local, t1);
|
|
||||||
|
|
||||||
if (t_max_local < t_min) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Z axis
|
|
||||||
{
|
|
||||||
Real t0 = (bounds.min_.z - ray.origin_.z) * inv_dir.z;
|
|
||||||
Real t1 = (bounds.max_.z - ray.origin_.z) * inv_dir.z;
|
|
||||||
|
|
||||||
if (inv_dir.z < 0.0f) {
|
|
||||||
Real temp = t0;
|
|
||||||
t0 = t1;
|
|
||||||
t1 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min = std::max(t_min, t0);
|
|
||||||
t_max_local = std::min(t_max_local, t1);
|
|
||||||
|
|
||||||
if (t_max_local < t_min) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min_out = t_min;
|
|
||||||
t_max_out = t_max_local;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool BVH::intersect_triangle_fast(const Triangle &triangle, const Ray &ray,
|
|
||||||
Real t_max, HitRecord &hit) const {
|
|
||||||
// Möller-Trumbore algorithm (inlined for performance)
|
|
||||||
const Vec3 &v0 = triangle.v0_.position_;
|
|
||||||
const Vec3 &v1 = triangle.v1_.position_;
|
|
||||||
const Vec3 &v2 = triangle.v2_.position_;
|
|
||||||
|
|
||||||
const Vec3 edge1 = v1 - v0;
|
|
||||||
const Vec3 edge2 = v2 - v0;
|
|
||||||
|
|
||||||
const Vec3 h = glm::cross(ray.direction_, edge2);
|
|
||||||
const Real a = glm::dot(edge1, h);
|
|
||||||
|
|
||||||
// Check if ray is parallel to triangle
|
|
||||||
if (a > -are_epsilon && a < are_epsilon) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Real f = 1.0f / a;
|
|
||||||
const Vec3 s = ray.origin_ - v0;
|
|
||||||
const Real u = f * glm::dot(s, h);
|
|
||||||
|
|
||||||
if (u < 0.0f || u > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3 q = glm::cross(s, edge1);
|
|
||||||
const Real v = f * glm::dot(ray.direction_, q);
|
|
||||||
|
|
||||||
if (v < 0.0f || u + v > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Real t = f * glm::dot(edge2, q);
|
|
||||||
|
|
||||||
if (t < ray.t_min_ || t >= t_max) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill hit record
|
|
||||||
const Real w = 1.0f - u - v;
|
|
||||||
|
|
||||||
hit.t_ = t;
|
|
||||||
hit.position_ = ray.origin_ + ray.direction_ * t;
|
|
||||||
hit.material_ = triangle.material_;
|
|
||||||
|
|
||||||
// Interpolate vertex attributes
|
|
||||||
hit.normal_ = glm::normalize(
|
|
||||||
w * triangle.v0_.normal_ + u * triangle.v1_.normal_ + v * triangle.v2_.normal_);
|
|
||||||
|
|
||||||
hit.texcoord_ = w * triangle.v0_.texcoord_ + u * triangle.v1_.texcoord_ + v * triangle.v2_.texcoord_;
|
|
||||||
|
|
||||||
hit.tangent_ = glm::normalize(
|
|
||||||
w * triangle.v0_.tangent_ + u * triangle.v1_.tangent_ + v * triangle.v2_.tangent_);
|
|
||||||
|
|
||||||
// Determine front face
|
|
||||||
hit.set_face_normal(ray.direction_, hit.normal_);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep recursive versions for reference/debugging
|
|
||||||
bool BVH::intersect_recursive(uint32_t node_index, const Ray &ray, HitRecord &hit) const {
|
|
||||||
if (node_index >= nodes_.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BVHNode &node = nodes_[node_index];
|
|
||||||
|
|
||||||
Real t_min, t_max;
|
|
||||||
if (!node.bounds_.intersect_ray(ray, t_min, t_max)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t_min > hit.t_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hit_anything = false;
|
|
||||||
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
|
||||||
|
|
||||||
if (prim_idx >= triangles_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Triangle &triangle = triangles_[prim_idx];
|
|
||||||
HitRecord temp_hit;
|
|
||||||
|
|
||||||
if (triangle.intersect(ray, temp_hit) && temp_hit.t_ < hit.t_) {
|
|
||||||
hit = temp_hit;
|
|
||||||
hit.triangle_index_ = prim_idx;
|
|
||||||
hit_anything = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Real t_left_min, t_left_max;
|
|
||||||
Real t_right_min, t_right_max;
|
|
||||||
|
|
||||||
bool hit_left = nodes_[node.left_child_].bounds_.intersect_ray(ray, t_left_min, t_left_max);
|
|
||||||
bool hit_right = nodes_[node.right_child_].bounds_.intersect_ray(ray, t_right_min, t_right_max);
|
|
||||||
|
|
||||||
// Traverse closer child first
|
|
||||||
if (hit_left && hit_right) {
|
|
||||||
if (t_left_min < t_right_min) {
|
|
||||||
hit_anything |= intersect_recursive(node.left_child_, ray, hit);
|
|
||||||
hit_anything |= intersect_recursive(node.right_child_, ray, hit);
|
|
||||||
} else {
|
|
||||||
hit_anything |= intersect_recursive(node.right_child_, ray, hit);
|
|
||||||
hit_anything |= intersect_recursive(node.left_child_, ray, hit);
|
|
||||||
}
|
|
||||||
} else if (hit_left) {
|
|
||||||
hit_anything |= intersect_recursive(node.left_child_, ray, hit);
|
|
||||||
} else if (hit_right) {
|
|
||||||
hit_anything |= intersect_recursive(node.right_child_, ray, hit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hit_anything;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BVH::intersect_any_recursive(uint32_t node_index, const Ray &ray, Real t_max) const {
|
|
||||||
if (node_index >= nodes_.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BVHNode &node = nodes_[node_index];
|
|
||||||
|
|
||||||
Real t_min, t_max_box;
|
|
||||||
if (!node.bounds_.intersect_ray(ray, t_min, t_max_box)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t_min > t_max) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.is_leaf()) {
|
|
||||||
for (uint32_t i = 0; i < node.primitive_count_; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices_[node.first_primitive_ + i];
|
|
||||||
|
|
||||||
if (prim_idx >= triangles_.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triangles_[prim_idx].intersect_fast(ray, t_max)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return intersect_any_recursive(node.left_child_, ray, t_max) || intersect_any_recursive(node.right_child_, ray, t_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh_builder.cpp
|
|
||||||
* @brief Implementation of BVH construction algorithms
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/acceleration/bvh_builder.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
#ifdef ARE_USE_OPENMP
|
|
||||||
#include <omp.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
BVHBuilder::BVHBuilder(const BVHBuildConfig& config)
|
|
||||||
: config_(config)
|
|
||||||
, node_count_(0)
|
|
||||||
, leaf_count_(0)
|
|
||||||
, max_depth_reached_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t BVHBuilder::build(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (triangles.empty()) {
|
|
||||||
ARE_LOG_WARN("BVHBuilder: Cannot build BVH from empty triangle list");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("BVHBuilder: Building BVH for " + std::to_string(triangles.size()) + " triangles");
|
|
||||||
|
|
||||||
// Reset statistics
|
|
||||||
node_count_ = 0;
|
|
||||||
leaf_count_ = 0;
|
|
||||||
max_depth_reached_ = 0;
|
|
||||||
|
|
||||||
// Initialize primitive indices
|
|
||||||
primitive_indices.resize(triangles.size());
|
|
||||||
for (size_t i = 0; i < triangles.size(); ++i) {
|
|
||||||
primitive_indices[i] = static_cast<uint32_t>(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve space for nodes (estimate: 2 * num_triangles)
|
|
||||||
nodes.clear();
|
|
||||||
nodes.reserve(triangles.size() * 2);
|
|
||||||
|
|
||||||
// Build BVH recursively
|
|
||||||
uint32_t root_index = build_recursive(triangles, nodes, primitive_indices,
|
|
||||||
0, static_cast<uint32_t>(triangles.size()), 0);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("BVHBuilder: Built BVH with " + std::to_string(node_count_) + " nodes, " +
|
|
||||||
std::to_string(leaf_count_) + " leaves, max depth " +
|
|
||||||
std::to_string(max_depth_reached_));
|
|
||||||
|
|
||||||
return root_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BVHBuilder::get_stats(size_t& node_count, size_t& leaf_count, int& max_depth) const {
|
|
||||||
node_count = node_count_;
|
|
||||||
leaf_count = leaf_count_;
|
|
||||||
max_depth = max_depth_reached_;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t BVHBuilder::build_recursive(const std::vector<Triangle>& triangles,
|
|
||||||
std::vector<BVHNode>& nodes,
|
|
||||||
std::vector<uint32_t>& primitive_indices,
|
|
||||||
uint32_t start, uint32_t end, int depth) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Update statistics
|
|
||||||
max_depth_reached_ = std::max(max_depth_reached_, depth);
|
|
||||||
|
|
||||||
// Create new node
|
|
||||||
uint32_t node_index = static_cast<uint32_t>(nodes.size());
|
|
||||||
nodes.emplace_back();
|
|
||||||
BVHNode& node = nodes[node_index];
|
|
||||||
node_count_++;
|
|
||||||
|
|
||||||
// Compute bounding box for all primitives in range
|
|
||||||
node.bounds_ = AABB::invalid();
|
|
||||||
for (uint32_t i = start; i < end; ++i) {
|
|
||||||
uint32_t prim_idx = primitive_indices[i];
|
|
||||||
node.bounds_.expand(triangles[prim_idx].compute_aabb());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t count = end - start;
|
|
||||||
|
|
||||||
// Check if we should create a leaf
|
|
||||||
bool should_create_leaf = (count <= static_cast<uint32_t>(config_.max_leaf_size_)) ||
|
|
||||||
(depth >= config_.max_depth_);
|
|
||||||
|
|
||||||
if (should_create_leaf) {
|
|
||||||
// Create leaf node
|
|
||||||
node.first_primitive_ = start;
|
|
||||||
node.primitive_count_ = count;
|
|
||||||
leaf_count_++;
|
|
||||||
return node_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find best split axis
|
|
||||||
int split_axis = find_best_split_axis(triangles, primitive_indices, start, end);
|
|
||||||
|
|
||||||
// Sort primitives along split axis
|
|
||||||
std::sort(primitive_indices.begin() + start,
|
|
||||||
primitive_indices.begin() + end,
|
|
||||||
[&](uint32_t a, uint32_t b) {
|
|
||||||
return triangles[a].centroid()[split_axis] <
|
|
||||||
triangles[b].centroid()[split_axis];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Find split position
|
|
||||||
uint32_t mid = start + count / 2;
|
|
||||||
|
|
||||||
// Use SAH if enabled
|
|
||||||
if (config_.split_method_ == BVHSplitMethod::ARE_BVH_SPLIT_SAH) {
|
|
||||||
Real best_cost = std::numeric_limits<Real>::max();
|
|
||||||
uint32_t best_split = mid;
|
|
||||||
|
|
||||||
// Try different split positions
|
|
||||||
const int num_buckets = 12;
|
|
||||||
for (int i = 1; i < num_buckets; ++i) {
|
|
||||||
uint32_t test_split = start + (count * i) / num_buckets;
|
|
||||||
|
|
||||||
// Compute bounding boxes for left and right
|
|
||||||
AABB left_bounds = AABB::invalid();
|
|
||||||
AABB right_bounds = AABB::invalid();
|
|
||||||
|
|
||||||
for (uint32_t j = start; j < test_split; ++j) {
|
|
||||||
left_bounds.expand(triangles[primitive_indices[j]].compute_aabb());
|
|
||||||
}
|
|
||||||
for (uint32_t j = test_split; j < end; ++j) {
|
|
||||||
right_bounds.expand(triangles[primitive_indices[j]].compute_aabb());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute SAH cost
|
|
||||||
Real left_cost = compute_sah_cost(left_bounds, test_split - start);
|
|
||||||
Real right_cost = compute_sah_cost(right_bounds, end - test_split);
|
|
||||||
Real cost = left_cost + right_cost;
|
|
||||||
|
|
||||||
if (cost < best_cost) {
|
|
||||||
best_cost = cost;
|
|
||||||
best_split = test_split;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mid = best_split;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we don't create empty children
|
|
||||||
if (mid == start || mid == end) {
|
|
||||||
mid = start + count / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create internal node
|
|
||||||
node.primitive_count_ = 0; // Mark as internal node
|
|
||||||
|
|
||||||
// Build left and right children
|
|
||||||
uint32_t left_child = build_recursive(triangles, nodes, primitive_indices,
|
|
||||||
start, mid, depth + 1);
|
|
||||||
uint32_t right_child = build_recursive(triangles, nodes, primitive_indices,
|
|
||||||
mid, end, depth + 1);
|
|
||||||
|
|
||||||
// Update node (it may have been reallocated)
|
|
||||||
nodes[node_index].left_child_ = left_child;
|
|
||||||
nodes[node_index].right_child_ = right_child;
|
|
||||||
|
|
||||||
return node_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BVHBuilder::find_best_split_axis(const std::vector<Triangle>& triangles,
|
|
||||||
const std::vector<uint32_t>& indices,
|
|
||||||
uint32_t start, uint32_t end) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Compute centroid bounds
|
|
||||||
AABB centroid_bounds = AABB::invalid();
|
|
||||||
for (uint32_t i = start; i < end; ++i) {
|
|
||||||
centroid_bounds.expand(triangles[indices[i]].centroid());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return longest axis
|
|
||||||
return centroid_bounds.longest_axis();
|
|
||||||
}
|
|
||||||
|
|
||||||
Real BVHBuilder::compute_sah_cost(const AABB& bounds, uint32_t count) {
|
|
||||||
// SAH cost = surface_area * primitive_count
|
|
||||||
// This is a simplified version; full SAH includes traversal cost
|
|
||||||
return bounds.surface_area() * static_cast<Real>(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
/**
|
|
||||||
* @file bvh_node.cpp
|
|
||||||
* @brief Implementation of BVHNode structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/acceleration/bvh_node.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// BVHNode is a POD structure, no implementation needed
|
|
||||||
// All methods are inline in the header
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
44
src/are.cpp
44
src/are.cpp
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
* @file are.cpp
|
|
||||||
* @brief Implementation of main engine interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/are.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
const char* get_version() {
|
|
||||||
return "0.1.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initialize() {
|
|
||||||
// Initialize logger first
|
|
||||||
Logger::init(LogLevel::ARE_LOG_INFO);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("===========================================");
|
|
||||||
ARE_LOG_INFO("Aurora Rendering Engine v" + std::string(get_version()));
|
|
||||||
ARE_LOG_INFO("===========================================");
|
|
||||||
ARE_LOG_INFO("Initializing engine...");
|
|
||||||
|
|
||||||
// Initialize profiler
|
|
||||||
Profiler::init();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Engine initialization complete");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown() {
|
|
||||||
ARE_LOG_INFO("Shutting down Aurora Rendering Engine...");
|
|
||||||
|
|
||||||
// Shutdown profiler
|
|
||||||
Profiler::shutdown();
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Engine shutdown complete");
|
|
||||||
|
|
||||||
// Shutdown logger last
|
|
||||||
Logger::shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
/**
|
|
||||||
* @file config.cpp
|
|
||||||
* @brief Implementation of configuration system
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/config.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
bool AreConfig::validate() const {
|
|
||||||
bool valid = true;
|
|
||||||
|
|
||||||
// Validate window config
|
|
||||||
if (window.width <= 0 || window.height <= 0) {
|
|
||||||
ARE_LOG_ERROR("Invalid window dimensions: " +
|
|
||||||
std::to_string(window.width) + "x" +
|
|
||||||
std::to_string(window.height));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.samples < 1) {
|
|
||||||
ARE_LOG_ERROR("Invalid MSAA samples: " + std::to_string(window.samples));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate ray tracing config
|
|
||||||
if (ray_tracing.spp <= 0) {
|
|
||||||
ARE_LOG_ERROR("Invalid SPP value: " + std::to_string(ray_tracing.spp));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ray_tracing.max_depth <= 0) {
|
|
||||||
ARE_LOG_ERROR("Invalid max ray depth: " + std::to_string(ray_tracing.max_depth));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ray_tracing.ao_samples < 0) {
|
|
||||||
ARE_LOG_ERROR("Invalid AO samples: " + std::to_string(ray_tracing.ao_samples));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ray_tracing.ao_radius <= 0.0f) {
|
|
||||||
ARE_LOG_ERROR("Invalid AO radius: " + std::to_string(ray_tracing.ao_radius));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate render config
|
|
||||||
if (render.exposure <= 0.0f) {
|
|
||||||
ARE_LOG_ERROR("Invalid exposure: " + std::to_string(render.exposure));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate performance config
|
|
||||||
if (performance.num_threads < 0) {
|
|
||||||
ARE_LOG_ERROR("Invalid thread count: " + std::to_string(performance.num_threads));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AreConfig::print() const {
|
|
||||||
ARE_LOG_INFO("=== Aurora Rendering Engine Configuration ===");
|
|
||||||
|
|
||||||
// Window configuration
|
|
||||||
ARE_LOG_INFO("Window:");
|
|
||||||
ARE_LOG_INFO(" Size: " + std::to_string(window.width) + "x" +
|
|
||||||
std::to_string(window.height));
|
|
||||||
ARE_LOG_INFO(" Title: " + window.title);
|
|
||||||
ARE_LOG_INFO(" Resizable: " + std::string(window.resizable ? "yes" : "no"));
|
|
||||||
ARE_LOG_INFO(" VSync: " + std::string(window.vsync ? "enabled" : "disabled"));
|
|
||||||
ARE_LOG_INFO(" MSAA: " + std::to_string(window.samples) + "x");
|
|
||||||
|
|
||||||
// Ray tracing configuration
|
|
||||||
ARE_LOG_INFO("Ray Tracing:");
|
|
||||||
std::string backend_str = (ray_tracing.backend == RayTracingBackend::ARE_RT_BACKEND_CPU)
|
|
||||||
? "CPU" : "Compute Shader";
|
|
||||||
ARE_LOG_INFO(" Backend: " + backend_str);
|
|
||||||
ARE_LOG_INFO(" SPP: " + std::to_string(ray_tracing.spp));
|
|
||||||
ARE_LOG_INFO(" Max Depth: " + std::to_string(ray_tracing.max_depth));
|
|
||||||
ARE_LOG_INFO(" Global Illumination: " + std::string(ray_tracing.enable_gi ? "enabled" : "disabled"));
|
|
||||||
ARE_LOG_INFO(" Ambient Occlusion: " + std::string(ray_tracing.enable_ao ? "enabled" : "disabled"));
|
|
||||||
ARE_LOG_INFO(" Soft Shadows: " + std::string(ray_tracing.enable_soft_shadows ? "enabled" : "disabled"));
|
|
||||||
|
|
||||||
if (ray_tracing.enable_ao) {
|
|
||||||
ARE_LOG_INFO(" AO Samples: " + std::to_string(ray_tracing.ao_samples));
|
|
||||||
ARE_LOG_INFO(" AO Radius: " + std::to_string(ray_tracing.ao_radius));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render configuration
|
|
||||||
ARE_LOG_INFO("Rendering:");
|
|
||||||
std::string tonemap_str;
|
|
||||||
switch (render.tonemap_op) {
|
|
||||||
case ToneMappingOperator::ARE_TONEMAP_NONE: tonemap_str = "None"; break;
|
|
||||||
case ToneMappingOperator::ARE_TONEMAP_REINHARD: tonemap_str = "Reinhard"; break;
|
|
||||||
case ToneMappingOperator::ARE_TONEMAP_ACES: tonemap_str = "ACES"; break;
|
|
||||||
}
|
|
||||||
ARE_LOG_INFO(" Tone Mapping: " + tonemap_str);
|
|
||||||
ARE_LOG_INFO(" Exposure: " + std::to_string(render.exposure));
|
|
||||||
ARE_LOG_INFO(" HDR: " + std::string(render.use_hdr ? "enabled" : "disabled"));
|
|
||||||
|
|
||||||
// Performance configuration
|
|
||||||
ARE_LOG_INFO("Performance:");
|
|
||||||
int num_threads = performance.num_threads == 0 ?
|
|
||||||
static_cast<int>(std::thread::hardware_concurrency()) : performance.num_threads;
|
|
||||||
ARE_LOG_INFO(" Threads: " + std::to_string(num_threads));
|
|
||||||
ARE_LOG_INFO(" BVH Multithreading: " +
|
|
||||||
std::string(performance.enable_bvh_multithreading ? "enabled" : "disabled"));
|
|
||||||
ARE_LOG_INFO(" Profiling: " +
|
|
||||||
std::string(performance.enable_profiling ? "enabled" : "disabled"));
|
|
||||||
|
|
||||||
// Path configuration
|
|
||||||
ARE_LOG_INFO("Paths:");
|
|
||||||
ARE_LOG_INFO(" Shaders: " + paths.shader_dir);
|
|
||||||
ARE_LOG_INFO(" Textures: " + paths.texture_dir);
|
|
||||||
ARE_LOG_INFO(" Output: " + paths.output_dir);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("=============================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
/**
|
|
||||||
* @file logger.cpp
|
|
||||||
* @brief Implementation of logging system
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
||||||
#include <spdlog/sinks/basic_file_sink.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
std::shared_ptr<void> Logger::logger_impl_ = nullptr;
|
|
||||||
bool Logger::initialized_ = false;
|
|
||||||
|
|
||||||
void Logger::init(LogLevel min_level) {
|
|
||||||
if (initialized_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create console sink with color support
|
|
||||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
|
||||||
console_sink->set_pattern("[%H:%M:%S.%e] [%^%l%$] %v");
|
|
||||||
|
|
||||||
// Create logger with console sink
|
|
||||||
auto logger = std::make_shared<spdlog::logger>("are", console_sink);
|
|
||||||
|
|
||||||
// Set log level
|
|
||||||
switch (min_level) {
|
|
||||||
case LogLevel::ARE_LOG_TRACE:
|
|
||||||
logger->set_level(spdlog::level::trace);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_DEBUG:
|
|
||||||
logger->set_level(spdlog::level::debug);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_INFO:
|
|
||||||
logger->set_level(spdlog::level::info);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_WARN:
|
|
||||||
logger->set_level(spdlog::level::warn);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_ERROR:
|
|
||||||
logger->set_level(spdlog::level::err);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_CRITICAL:
|
|
||||||
logger->set_level(spdlog::level::critical);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush on error or higher
|
|
||||||
logger->flush_on(spdlog::level::err);
|
|
||||||
|
|
||||||
// Set as default logger
|
|
||||||
spdlog::set_default_logger(logger);
|
|
||||||
logger_impl_ = logger;
|
|
||||||
initialized_ = true;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
fprintf(stderr, "[ARE] Failed to initialize logger: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::shutdown() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
spdlog::shutdown();
|
|
||||||
logger_impl_.reset();
|
|
||||||
initialized_ = false;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
fprintf(stderr, "[ARE] Error during logger shutdown: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::log(LogLevel level, const char* file, const char* func,
|
|
||||||
int line, const std::string& message) {
|
|
||||||
if (!initialized_) {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract filename from full path
|
|
||||||
const char* filename = file;
|
|
||||||
const char* last_slash = nullptr;
|
|
||||||
|
|
||||||
for (const char* p = file; *p; ++p) {
|
|
||||||
if (*p == '/' || *p == '\\') {
|
|
||||||
last_slash = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_slash) {
|
|
||||||
filename = last_slash + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format message with location information
|
|
||||||
std::string formatted = message + " (" + filename + ":" + std::to_string(line) + ")";
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto logger = std::static_pointer_cast<spdlog::logger>(logger_impl_);
|
|
||||||
|
|
||||||
switch (level) {
|
|
||||||
case LogLevel::ARE_LOG_TRACE:
|
|
||||||
logger->trace(formatted);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_DEBUG:
|
|
||||||
logger->debug(formatted);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_INFO:
|
|
||||||
logger->info(formatted);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_WARN:
|
|
||||||
logger->warn(formatted);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_ERROR:
|
|
||||||
logger->error(formatted);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_CRITICAL:
|
|
||||||
logger->critical(formatted);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
fprintf(stderr, "[ARE] Logging error: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::set_level(LogLevel level) {
|
|
||||||
if (!initialized_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto logger = std::static_pointer_cast<spdlog::logger>(logger_impl_);
|
|
||||||
|
|
||||||
switch (level) {
|
|
||||||
case LogLevel::ARE_LOG_TRACE:
|
|
||||||
logger->set_level(spdlog::level::trace);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_DEBUG:
|
|
||||||
logger->set_level(spdlog::level::debug);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_INFO:
|
|
||||||
logger->set_level(spdlog::level::info);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_WARN:
|
|
||||||
logger->set_level(spdlog::level::warn);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_ERROR:
|
|
||||||
logger->set_level(spdlog::level::err);
|
|
||||||
break;
|
|
||||||
case LogLevel::ARE_LOG_CRITICAL:
|
|
||||||
logger->set_level(spdlog::level::critical);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
fprintf(stderr, "[ARE] Error setting log level: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
/**
|
|
||||||
* @file profiler.cpp
|
|
||||||
* @brief Implementation of performance profiler
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
std::unordered_map<std::string, Profiler::SectionData> Profiler::sections_;
|
|
||||||
std::unordered_map<std::string, ProfileResult> Profiler::results_;
|
|
||||||
bool Profiler::enabled_ = false;
|
|
||||||
|
|
||||||
void Profiler::init() {
|
|
||||||
#ifdef ARE_ENABLE_PROFILING
|
|
||||||
enabled_ = true;
|
|
||||||
sections_.clear();
|
|
||||||
results_.clear();
|
|
||||||
ARE_LOG_INFO("Profiler initialized");
|
|
||||||
#else
|
|
||||||
enabled_ = false;
|
|
||||||
ARE_LOG_WARN("Profiler disabled (ARE_ENABLE_PROFILING not defined)");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::shutdown() {
|
|
||||||
if (!enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
print_results();
|
|
||||||
sections_.clear();
|
|
||||||
results_.clear();
|
|
||||||
enabled_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::begin(const std::string& name) {
|
|
||||||
if (!enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& section = sections_[name];
|
|
||||||
section.start_time_ = std::chrono::high_resolution_clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::end(const std::string& name) {
|
|
||||||
if (!enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end_time = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
auto it = sections_.find(name);
|
|
||||||
if (it == sections_.end()) {
|
|
||||||
ARE_LOG_WARN("Profiler::end called for unknown section: " + name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& section = it->second;
|
|
||||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
||||||
end_time - section.start_time_
|
|
||||||
);
|
|
||||||
|
|
||||||
section.total_duration_ms_ += duration.count() / 1000.0;
|
|
||||||
section.call_count_++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unordered_map<std::string, ProfileResult>& Profiler::get_results() {
|
|
||||||
if (!enabled_) {
|
|
||||||
return results_;
|
|
||||||
}
|
|
||||||
|
|
||||||
results_.clear();
|
|
||||||
|
|
||||||
for (const auto& [name, section] : sections_) {
|
|
||||||
ProfileResult result;
|
|
||||||
result.name_ = name;
|
|
||||||
result.duration_ms_ = section.total_duration_ms_;
|
|
||||||
result.call_count_ = section.call_count_;
|
|
||||||
result.avg_duration_ms_ = (section.call_count_ > 0)
|
|
||||||
? (section.total_duration_ms_ / section.call_count_)
|
|
||||||
: 0.0;
|
|
||||||
|
|
||||||
results_[name] = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::reset() {
|
|
||||||
if (!enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& [name, section] : sections_) {
|
|
||||||
section.total_duration_ms_ = 0.0;
|
|
||||||
section.call_count_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
results_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::print_results() {
|
|
||||||
if (!enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_results();
|
|
||||||
|
|
||||||
if (results_.empty()) {
|
|
||||||
ARE_LOG_INFO("No profiling data available");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort results by total duration (descending)
|
|
||||||
std::vector<ProfileResult> sorted_results;
|
|
||||||
sorted_results.reserve(results_.size());
|
|
||||||
|
|
||||||
for (const auto& [name, result] : results_) {
|
|
||||||
sorted_results.push_back(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(sorted_results.begin(), sorted_results.end(),
|
|
||||||
[](const ProfileResult& a, const ProfileResult& b) {
|
|
||||||
return a.duration_ms_ > b.duration_ms_;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Print header
|
|
||||||
ARE_LOG_INFO("=== Performance Profile ===");
|
|
||||||
ARE_LOG_INFO(std::string(80, '-'));
|
|
||||||
|
|
||||||
std::stringstream header;
|
|
||||||
header << std::left << std::setw(30) << "Section"
|
|
||||||
<< std::right << std::setw(12) << "Total (ms)"
|
|
||||||
<< std::right << std::setw(12) << "Calls"
|
|
||||||
<< std::right << std::setw(12) << "Avg (ms)"
|
|
||||||
<< std::right << std::setw(12) << "Percent";
|
|
||||||
ARE_LOG_INFO(header.str());
|
|
||||||
ARE_LOG_INFO(std::string(80, '-'));
|
|
||||||
|
|
||||||
// Calculate total time
|
|
||||||
double total_time = 0.0;
|
|
||||||
for (const auto& result : sorted_results) {
|
|
||||||
total_time += result.duration_ms_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print results
|
|
||||||
for (const auto& result : sorted_results) {
|
|
||||||
std::stringstream ss;
|
|
||||||
double percent = (total_time > 0.0) ? (result.duration_ms_ / total_time * 100.0) : 0.0;
|
|
||||||
|
|
||||||
ss << std::left << std::setw(30) << result.name_
|
|
||||||
<< std::right << std::setw(12) << std::fixed << std::setprecision(3) << result.duration_ms_
|
|
||||||
<< std::right << std::setw(12) << result.call_count_
|
|
||||||
<< std::right << std::setw(12) << std::fixed << std::setprecision(3) << result.avg_duration_ms_
|
|
||||||
<< std::right << std::setw(11) << std::fixed << std::setprecision(1) << percent << "%";
|
|
||||||
|
|
||||||
ARE_LOG_INFO(ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO(std::string(80, '-'));
|
|
||||||
|
|
||||||
std::stringstream total_ss;
|
|
||||||
total_ss << "Total: " << std::fixed << std::setprecision(3) << total_time << " ms";
|
|
||||||
ARE_LOG_INFO(total_ss.str());
|
|
||||||
ARE_LOG_INFO("===========================");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScopedProfiler implementation
|
|
||||||
ScopedProfiler::ScopedProfiler(const std::string& name) : name_(name) {
|
|
||||||
Profiler::begin(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedProfiler::~ScopedProfiler() {
|
|
||||||
Profiler::end(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
/**
|
|
||||||
* @file types.cpp
|
|
||||||
* @brief Implementation of basic types (if needed)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/types.h>
|
|
||||||
|
|
||||||
// This file is intentionally minimal as types.h is mostly type definitions
|
|
||||||
// Any future type-related utility functions can be added here
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
// Currently no implementation needed for types.h
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* @file aabb.cpp
|
|
||||||
* @brief Implementation of Axis-Aligned Bounding Box
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/geometry/aabb.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
AABB::AABB()
|
|
||||||
: min_(std::numeric_limits<float>::max())
|
|
||||||
, max_(std::numeric_limits<float>::lowest()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB::AABB(const Vec3& min, const Vec3& max)
|
|
||||||
: min_(min)
|
|
||||||
, max_(max) {
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB::AABB(const Vec3& point)
|
|
||||||
: min_(point)
|
|
||||||
, max_(point) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AABB::is_valid() const {
|
|
||||||
return min_.x <= max_.x && min_.y <= max_.y && min_.z <= max_.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 AABB::center() const {
|
|
||||||
return (min_ + max_) * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 AABB::size() const {
|
|
||||||
return max_ - min_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Real AABB::surface_area() const {
|
|
||||||
Vec3 d = size();
|
|
||||||
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
Real AABB::volume() const {
|
|
||||||
Vec3 d = size();
|
|
||||||
return d.x * d.y * d.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
int AABB::longest_axis() const {
|
|
||||||
Vec3 d = size();
|
|
||||||
if (d.x > d.y && d.x > d.z) {
|
|
||||||
return 0;
|
|
||||||
} else if (d.y > d.z) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AABB::expand(const Vec3& point) {
|
|
||||||
min_ = glm::min(min_, point);
|
|
||||||
max_ = glm::max(max_, point);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AABB::expand(const AABB& other) {
|
|
||||||
if (other.is_valid()) {
|
|
||||||
min_ = glm::min(min_, other.min_);
|
|
||||||
max_ = glm::max(max_, other.max_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AABB::contains(const Vec3& point) const {
|
|
||||||
return point.x >= min_.x && point.x <= max_.x &&
|
|
||||||
point.y >= min_.y && point.y <= max_.y &&
|
|
||||||
point.z >= min_.z && point.z <= max_.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AABB::intersects(const AABB& other) const {
|
|
||||||
return min_.x <= other.max_.x && max_.x >= other.min_.x &&
|
|
||||||
min_.y <= other.max_.y && max_.y >= other.min_.y &&
|
|
||||||
min_.z <= other.max_.z && max_.z >= other.min_.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AABB::intersect_ray(const Ray& ray, Real& t_min_out, Real& t_max_out) const {
|
|
||||||
// Slab method for ray-AABB intersection
|
|
||||||
// Reference: "An Efficient and Robust Ray-Box Intersection Algorithm" by Williams et al.
|
|
||||||
|
|
||||||
Real t_min = ray.t_min_;
|
|
||||||
Real t_max = ray.t_max_;
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
Real inv_d = 1.0f / ray.direction_[i];
|
|
||||||
Real t0 = (min_[i] - ray.origin_[i]) * inv_d;
|
|
||||||
Real t1 = (max_[i] - ray.origin_[i]) * inv_d;
|
|
||||||
|
|
||||||
if (inv_d < 0.0f) {
|
|
||||||
std::swap(t0, t1);
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min = t0 > t_min ? t0 : t_min;
|
|
||||||
t_max = t1 < t_max ? t1 : t_max;
|
|
||||||
|
|
||||||
if (t_max < t_min) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t_min_out = t_min;
|
|
||||||
t_max_out = t_max;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB AABB::merge(const AABB& a, const AABB& b) {
|
|
||||||
if (!a.is_valid()) return b;
|
|
||||||
if (!b.is_valid()) return a;
|
|
||||||
|
|
||||||
return AABB(
|
|
||||||
glm::min(a.min_, b.min_),
|
|
||||||
glm::max(a.max_, b.max_)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB AABB::invalid() {
|
|
||||||
return AABB();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
/**
|
|
||||||
* @file transform.cpp
|
|
||||||
* @brief Implementation of Transform class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <are/geometry/transform.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
#include <glm/gtx/euler_angles.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Transform::Transform()
|
|
||||||
: position_(0.0f)
|
|
||||||
, rotation_(0.0f)
|
|
||||||
, scale_(1.0f)
|
|
||||||
, matrix_(1.0f)
|
|
||||||
, inverse_matrix_(1.0f)
|
|
||||||
, dirty_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform::Transform(const Vec3& position, const Vec3& rotation, const Vec3& scale)
|
|
||||||
: position_(position)
|
|
||||||
, rotation_(rotation)
|
|
||||||
, scale_(scale)
|
|
||||||
, matrix_(1.0f)
|
|
||||||
, inverse_matrix_(1.0f)
|
|
||||||
, dirty_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::set_position(const Vec3& position) {
|
|
||||||
position_ = position;
|
|
||||||
mark_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::set_rotation(const Vec3& rotation) {
|
|
||||||
rotation_ = rotation;
|
|
||||||
mark_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::set_scale(const Vec3& scale) {
|
|
||||||
scale_ = scale;
|
|
||||||
mark_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::set_scale(Real uniform_scale) {
|
|
||||||
scale_ = Vec3(uniform_scale);
|
|
||||||
mark_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 Transform::get_matrix() const {
|
|
||||||
if (dirty_) {
|
|
||||||
update_matrix();
|
|
||||||
}
|
|
||||||
return matrix_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 Transform::get_inverse_matrix() const {
|
|
||||||
if (dirty_) {
|
|
||||||
update_matrix();
|
|
||||||
}
|
|
||||||
return inverse_matrix_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 Transform::get_normal_matrix() const {
|
|
||||||
if (dirty_) {
|
|
||||||
update_matrix();
|
|
||||||
}
|
|
||||||
// Normal matrix is the transpose of the inverse of the upper-left 3x3
|
|
||||||
return glm::transpose(glm::inverse(Mat3(matrix_)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Transform::transform_point(const Vec3& point) const {
|
|
||||||
if (dirty_) {
|
|
||||||
update_matrix();
|
|
||||||
}
|
|
||||||
Vec4 result = matrix_ * Vec4(point, 1.0f);
|
|
||||||
return Vec3(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Transform::transform_direction(const Vec3& direction) const {
|
|
||||||
if (dirty_) {
|
|
||||||
update_matrix();
|
|
||||||
}
|
|
||||||
Vec4 result = matrix_ * Vec4(direction, 0.0f);
|
|
||||||
return Vec3(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Transform::transform_normal(const Vec3& normal) const {
|
|
||||||
Mat3 normal_matrix = get_normal_matrix();
|
|
||||||
return glm::normalize(normal_matrix * normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Transform::operator*(const Transform& other) const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Combine transforms by multiplying matrices
|
|
||||||
// Note: This is an approximation; for exact results,
|
|
||||||
// we would need to decompose the combined matrix
|
|
||||||
Transform result;
|
|
||||||
|
|
||||||
Mat4 combined = get_matrix() * other.get_matrix();
|
|
||||||
|
|
||||||
// Extract translation
|
|
||||||
result.position_ = Vec3(combined[3]);
|
|
||||||
|
|
||||||
// Extract scale (approximate)
|
|
||||||
result.scale_.x = glm::length(Vec3(combined[0]));
|
|
||||||
result.scale_.y = glm::length(Vec3(combined[1]));
|
|
||||||
result.scale_.z = glm::length(Vec3(combined[2]));
|
|
||||||
|
|
||||||
// Remove scale from matrix to extract rotation
|
|
||||||
Mat3 rotation_matrix;
|
|
||||||
rotation_matrix[0] = Vec3(combined[0]) / result.scale_.x;
|
|
||||||
rotation_matrix[1] = Vec3(combined[1]) / result.scale_.y;
|
|
||||||
rotation_matrix[2] = Vec3(combined[2]) / result.scale_.z;
|
|
||||||
|
|
||||||
// Extract Euler angles (approximate, may have gimbal lock issues)
|
|
||||||
result.rotation_.x = std::atan2(rotation_matrix[2][1], rotation_matrix[2][2]);
|
|
||||||
result.rotation_.y = std::atan2(-rotation_matrix[2][0],std::sqrt(rotation_matrix[2][1] * rotation_matrix[2][1] +
|
|
||||||
rotation_matrix[2][2] * rotation_matrix[2][2]));
|
|
||||||
result.rotation_.z = std::atan2(rotation_matrix[1][0], rotation_matrix[0][0]);
|
|
||||||
|
|
||||||
result.dirty_ = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Transform::identity() {
|
|
||||||
return Transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Transform::translate(const Vec3& translation) {
|
|
||||||
return Transform(translation, Vec3(0.0f), Vec3(1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Transform::rotate(const Vec3& rotation) {
|
|
||||||
return Transform(Vec3(0.0f), rotation, Vec3(1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Transform::scale(const Vec3& scale) {
|
|
||||||
return Transform(Vec3(0.0f), Vec3(0.0f), scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::mark_dirty() {
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transform::update_matrix() const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Build transformation matrix: T * R * S
|
|
||||||
// Translation
|
|
||||||
Mat4 translation_matrix = glm::translate(Mat4(1.0f), position_);
|
|
||||||
|
|
||||||
// Rotation (using Euler angles: YXZ order for typical camera/object rotation)
|
|
||||||
Mat4 rotation_matrix = glm::eulerAngleYXZ(rotation_.y, rotation_.x, rotation_.z);
|
|
||||||
|
|
||||||
// Scale
|
|
||||||
Mat4 scale_matrix = glm::scale(Mat4(1.0f), scale_);
|
|
||||||
|
|
||||||
// Combine: T * R * S
|
|
||||||
matrix_ = translation_matrix * rotation_matrix * scale_matrix;
|
|
||||||
|
|
||||||
// Compute inverse
|
|
||||||
inverse_matrix_ = glm::inverse(matrix_);
|
|
||||||
|
|
||||||
dirty_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
/**
|
|
||||||
* @file triangle.cpp
|
|
||||||
* @brief Implementation of Triangle primitive
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/geometry/triangle.h>
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Triangle::Triangle()
|
|
||||||
: material_(are_invalid_handle) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Triangle::Triangle(const Vertex &v0, const Vertex &v1, const Vertex &v2, MaterialHandle material)
|
|
||||||
: v0_(v0)
|
|
||||||
, v1_(v1)
|
|
||||||
, v2_(v2)
|
|
||||||
, material_(material) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Triangle::centroid() const {
|
|
||||||
return (v0_.position_ + v1_.position_ + v2_.position_) / 3.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Triangle::normal() const {
|
|
||||||
Vec3 edge1 = v1_.position_ - v0_.position_;
|
|
||||||
Vec3 edge2 = v2_.position_ - v0_.position_;
|
|
||||||
return glm::normalize(glm::cross(edge1, edge2));
|
|
||||||
}
|
|
||||||
|
|
||||||
Real Triangle::area() const {
|
|
||||||
Vec3 edge1 = v1_.position_ - v0_.position_;
|
|
||||||
Vec3 edge2 = v2_.position_ - v0_.position_;
|
|
||||||
return 0.5f * glm::length(glm::cross(edge1, edge2));
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB Triangle::compute_aabb() const {
|
|
||||||
AABB aabb(v0_.position_);
|
|
||||||
aabb.expand(v1_.position_);
|
|
||||||
aabb.expand(v2_.position_);
|
|
||||||
return aabb;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Triangle::intersect(const Ray &ray, HitRecord &hit) const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Möller-Trumbore algorithm
|
|
||||||
// Reference: "Fast, Minimum Storage Ray/Triangle Intersection"
|
|
||||||
|
|
||||||
const Vec3 edge1 = v1_.position_ - v0_.position_;
|
|
||||||
const Vec3 edge2 = v2_.position_ - v0_.position_;
|
|
||||||
|
|
||||||
const Vec3 h = glm::cross(ray.direction_, edge2);
|
|
||||||
const Real a = glm::dot(edge1, h);
|
|
||||||
|
|
||||||
// Check if ray is parallel to triangle
|
|
||||||
if (a > -are_epsilon && a < are_epsilon) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Real f = 1.0f / a;
|
|
||||||
const Vec3 s = ray.origin_ - v0_.position_;
|
|
||||||
const Real u = f * glm::dot(s, h);
|
|
||||||
|
|
||||||
// Check barycentric coordinate u
|
|
||||||
if (u < 0.0f || u > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3 q = glm::cross(s, edge1);
|
|
||||||
const Real v = f * glm::dot(ray.direction_, q);
|
|
||||||
|
|
||||||
// Check barycentric coordinate v
|
|
||||||
if (v < 0.0f || u + v > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate t parameter
|
|
||||||
const Real t = f * glm::dot(edge2, q);
|
|
||||||
|
|
||||||
// Check if intersection is within ray bounds
|
|
||||||
if (!ray.is_valid_t(t)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill hit record
|
|
||||||
const Real w = 1.0f - u - v;
|
|
||||||
|
|
||||||
hit.t_ = t;
|
|
||||||
hit.position_ = ray.at(t);
|
|
||||||
hit.material_ = material_;
|
|
||||||
|
|
||||||
// Interpolate vertex attributes using barycentric coordinates
|
|
||||||
hit.normal_ = glm::normalize(
|
|
||||||
w * v0_.normal_ + u * v1_.normal_ + v * v2_.normal_);
|
|
||||||
hit.texcoord_ = w * v0_.texcoord_ + u * v1_.texcoord_ + v * v2_.texcoord_;
|
|
||||||
hit.tangent_ = glm::normalize(
|
|
||||||
w * v0_.tangent_ + u * v1_.tangent_ + v * v2_.tangent_);
|
|
||||||
|
|
||||||
// Determine front face
|
|
||||||
hit.set_face_normal(ray.direction_, hit.normal_);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Triangle::intersect_fast(const Ray &ray, Real t_max) const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Simplified Möller-Trumbore without hit record computation
|
|
||||||
const Vec3 edge1 = v1_.position_ - v0_.position_;
|
|
||||||
const Vec3 edge2 = v2_.position_ - v0_.position_;
|
|
||||||
|
|
||||||
const Vec3 h = glm::cross(ray.direction_, edge2);
|
|
||||||
const Real a = glm::dot(edge1, h);
|
|
||||||
|
|
||||||
if (a > -are_epsilon && a < are_epsilon) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Real f = 1.0f / a;
|
|
||||||
const Vec3 s = ray.origin_ - v0_.position_;
|
|
||||||
const Real u = f * glm::dot(s, h);
|
|
||||||
|
|
||||||
if (u < 0.0f || u > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3 q = glm::cross(s, edge1);
|
|
||||||
const Real v = f * glm::dot(ray.direction_, q);
|
|
||||||
|
|
||||||
if (v < 0.0f || u + v > 1.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Real t = f * glm::dot(edge2, q);
|
|
||||||
|
|
||||||
return t > ray.t_min_ && t < t_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/**
|
|
||||||
* @file vertex.cpp
|
|
||||||
* @brief Implementation of Vertex structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Vertex::Vertex(const Vec3& pos)
|
|
||||||
: position_(pos)
|
|
||||||
, normal_(0.0f, 1.0f, 0.0f)
|
|
||||||
, texcoord_(0.0f, 0.0f)
|
|
||||||
, tangent_(1.0f, 0.0f, 0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex::Vertex(const Vec3& pos, const Vec3& norm)
|
|
||||||
: position_(pos)
|
|
||||||
, normal_(norm)
|
|
||||||
, texcoord_(0.0f, 0.0f)
|
|
||||||
, tangent_(1.0f, 0.0f, 0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex::Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv)
|
|
||||||
: position_(pos)
|
|
||||||
, normal_(norm)
|
|
||||||
, texcoord_(uv)
|
|
||||||
, tangent_(1.0f, 0.0f, 0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex::Vertex(const Vec3& pos, const Vec3& norm, const Vec2& uv, const Vec3& tan)
|
|
||||||
: position_(pos)
|
|
||||||
, normal_(norm)
|
|
||||||
, texcoord_(uv)
|
|
||||||
, tangent_(tan) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex Vertex::lerp(const Vertex& a, const Vertex& b, Real t) {
|
|
||||||
Vertex result;
|
|
||||||
result.position_ = glm::mix(a.position_, b.position_, t);
|
|
||||||
result.normal_ = glm::normalize(glm::mix(a.normal_, b.normal_, t));
|
|
||||||
result.texcoord_ = glm::mix(a.texcoord_, b.texcoord_, t);
|
|
||||||
result.tangent_ = glm::normalize(glm::mix(a.tangent_, b.tangent_, t));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
/**
|
|
||||||
* @file gl_context.cpp
|
|
||||||
* @brief Implementation of OpenGL context management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/platform/gl_context.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
bool GLContext::initialized_ = false;
|
|
||||||
|
|
||||||
bool GLContext::initialize() {
|
|
||||||
if (initialized_) {
|
|
||||||
ARE_LOG_WARN("OpenGL context already initialized");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load OpenGL function pointers using GLAD
|
|
||||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to initialize GLAD");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_INFO("OpenGL context initialized successfully");
|
|
||||||
print_info();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLContext::is_initialized() {
|
|
||||||
return initialized_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GLContext::get_version() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return "Not initialized";
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLubyte* version = glGetString(GL_VERSION);
|
|
||||||
return version ? std::string(reinterpret_cast<const char*>(version)) : "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GLContext::get_renderer() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return "Not initialized";
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLubyte* renderer = glGetString(GL_RENDERER);
|
|
||||||
return renderer ? std::string(reinterpret_cast<const char*>(renderer)) : "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GLContext::get_vendor() {
|
|
||||||
if (!initialized_) {
|
|
||||||
return "Not initialized";
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLubyte* vendor = glGetString(GL_VENDOR);
|
|
||||||
return vendor ? std::string(reinterpret_cast<const char*>(vendor)) : "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLContext::is_extension_supported(const std::string& extension) {
|
|
||||||
if (!initialized_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLint num_extensions = 0;
|
|
||||||
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
|
||||||
|
|
||||||
for (GLint i = 0; i < num_extensions; ++i) {
|
|
||||||
const GLubyte* ext = glGetStringi(GL_EXTENSIONS, i);
|
|
||||||
if (ext && extension == reinterpret_cast<const char*>(ext)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLContext::print_info() {
|
|
||||||
if (!initialized_) {
|
|
||||||
ARE_LOG_WARN("Cannot print OpenGL info: context not initialized");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("=== OpenGL Information ===");
|
|
||||||
ARE_LOG_INFO("Version: " + get_version());
|
|
||||||
ARE_LOG_INFO("Renderer: " + get_renderer());
|
|
||||||
ARE_LOG_INFO("Vendor: " + get_vendor());
|
|
||||||
|
|
||||||
// Get GLSL version
|
|
||||||
const GLubyte* glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
|
|
||||||
if (glsl_version) {
|
|
||||||
ARE_LOG_INFO("GLSL Version: " + std::string(reinterpret_cast<const char*>(glsl_version)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get max texture size
|
|
||||||
GLint max_texture_size = 0;
|
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
|
||||||
ARE_LOG_INFO("Max Texture Size: " + std::to_string(max_texture_size));
|
|
||||||
|
|
||||||
// Get max compute work group size
|
|
||||||
GLint max_compute_work_group_invocations = 0;
|
|
||||||
glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &max_compute_work_group_invocations);
|
|
||||||
ARE_LOG_INFO("Max Compute Work Group Invocations: " +
|
|
||||||
std::to_string(max_compute_work_group_invocations));
|
|
||||||
|
|
||||||
ARE_LOG_INFO("==========================");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLContext::check_error(const char* file, int line) {
|
|
||||||
GLenum error = glGetError();
|
|
||||||
|
|
||||||
if (error == GL_NO_ERROR) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string error_string;
|
|
||||||
switch (error) {
|
|
||||||
case GL_INVALID_ENUM:
|
|
||||||
error_string = "GL_INVALID_ENUM";
|
|
||||||
break;
|
|
||||||
case GL_INVALID_VALUE:
|
|
||||||
error_string = "GL_INVALID_VALUE";
|
|
||||||
break;
|
|
||||||
case GL_INVALID_OPERATION:
|
|
||||||
error_string = "GL_INVALID_OPERATION";
|
|
||||||
break;
|
|
||||||
case GL_OUT_OF_MEMORY:
|
|
||||||
error_string = "GL_OUT_OF_MEMORY";
|
|
||||||
break;
|
|
||||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
|
||||||
error_string = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error_string = "Unknown error " + std::to_string(error);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract filename from path
|
|
||||||
const char* filename = file;
|
|
||||||
for (const char* p = file; *p; ++p) {
|
|
||||||
if (*p == '/' || *p == '\\') {
|
|
||||||
filename = p + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_ERROR("OpenGL Error: " + error_string + " at " +
|
|
||||||
filename + ":" + std::to_string(line));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLContext::clear_errors() {
|
|
||||||
while (glGetError() != GL_NO_ERROR) {
|
|
||||||
// Clear all errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
/**
|
|
||||||
* @file window.cpp
|
|
||||||
* @brief Implementation of GLFW window wrapper
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/platform/window.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
int Window::instance_count_ = 0;
|
|
||||||
|
|
||||||
Window::Window(const WindowConfig& config)
|
|
||||||
: window_(nullptr)
|
|
||||||
, config_(config)
|
|
||||||
, vsync_enabled_(config.vsync) {
|
|
||||||
|
|
||||||
initialize_glfw();
|
|
||||||
create_window();
|
|
||||||
setup_callbacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::~Window() {
|
|
||||||
if (window_) {
|
|
||||||
glfwDestroyWindow(window_);
|
|
||||||
window_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
instance_count_--;
|
|
||||||
if (instance_count_ == 0) {
|
|
||||||
glfwTerminate();
|
|
||||||
ARE_LOG_INFO("GLFW terminated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::initialize_glfw() {
|
|
||||||
if (instance_count_ == 0) {
|
|
||||||
glfwSetErrorCallback(error_callback);
|
|
||||||
|
|
||||||
if (!glfwInit()) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to initialize GLFW");
|
|
||||||
throw std::runtime_error("GLFW initialization failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("GLFW initialized successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
instance_count_++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::create_window() {
|
|
||||||
// Set OpenGL version hints
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set window hints
|
|
||||||
glfwWindowHint(GLFW_RESIZABLE, config_.resizable ? GLFW_TRUE : GLFW_FALSE);
|
|
||||||
glfwWindowHint(GLFW_SAMPLES, config_.samples);
|
|
||||||
|
|
||||||
// Create window
|
|
||||||
window_ = glfwCreateWindow(
|
|
||||||
config_.width,
|
|
||||||
config_.height,
|
|
||||||
config_.title.c_str(),
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!window_) {
|
|
||||||
ARE_LOG_CRITICAL("Failed to create GLFW window");
|
|
||||||
throw std::runtime_error("Window creation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make context current
|
|
||||||
glfwMakeContextCurrent(window_);
|
|
||||||
|
|
||||||
// Set VSync
|
|
||||||
glfwSwapInterval(vsync_enabled_ ? 1 : 0);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Window created: " + std::to_string(config_.width) + "x" +
|
|
||||||
std::to_string(config_.height) + " - " + config_.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::setup_callbacks() {
|
|
||||||
// Store this pointer in window user pointer
|
|
||||||
glfwSetWindowUserPointer(window_, this);
|
|
||||||
|
|
||||||
// Set framebuffer size callback
|
|
||||||
glfwSetFramebufferSizeCallback(window_, framebuffer_size_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::framebuffer_size_callback(GLFWwindow* window, int width, int height) {
|
|
||||||
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
|
||||||
if (win) {
|
|
||||||
win->config_.width = width;
|
|
||||||
win->config_.height = height;
|
|
||||||
ARE_LOG_DEBUG("Framebuffer resized: " + std::to_string(width) + "x" + std::to_string(height));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::error_callback(int error, const char* description) {
|
|
||||||
ARE_LOG_ERROR("GLFW Error " + std::to_string(error) + ": " + description);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::should_close() const {
|
|
||||||
return glfwWindowShouldClose(window_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::set_should_close(bool should_close) {
|
|
||||||
glfwSetWindowShouldClose(window_, should_close ? GLFW_TRUE : GLFW_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::swap_buffers() {
|
|
||||||
glfwSwapBuffers(window_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::poll_events() {
|
|
||||||
glfwPollEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Window::get_width() const {
|
|
||||||
return config_.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Window::get_height() const {
|
|
||||||
return config_.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
Real Window::get_aspect_ratio() const {
|
|
||||||
return static_cast<Real>(config_.width) / static_cast<Real>(config_.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& Window::get_title() const {
|
|
||||||
return config_.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::set_title(const std::string& title) {
|
|
||||||
config_.title = title;
|
|
||||||
glfwSetWindowTitle(window_, title.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::set_size(int width, int height) {
|
|
||||||
config_.width = width;
|
|
||||||
config_.height = height;
|
|
||||||
glfwSetWindowSize(window_, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::get_framebuffer_size(int& width, int& height) const {
|
|
||||||
glfwGetFramebufferSize(window_, &width, &height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::set_vsync(bool enabled) {
|
|
||||||
vsync_enabled_ = enabled;
|
|
||||||
glfwSwapInterval(enabled ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::get_vsync() const {
|
|
||||||
return vsync_enabled_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::is_key_pressed(int key) const {
|
|
||||||
return glfwGetKey(window_, key) == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::is_mouse_button_pressed(int button) const {
|
|
||||||
return glfwGetMouseButton(window_, button) == GLFW_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::get_cursor_pos(double& x, double& y) const {
|
|
||||||
glfwGetCursorPos(window_, &x, &y);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,255 +0,0 @@
|
||||||
/**
|
|
||||||
* @file gbuffer.cpp
|
|
||||||
* @brief Implementation of GBuffer class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/rasterizer/gbuffer.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
GBuffer::GBuffer(int width, int height)
|
|
||||||
: fbo_(0)
|
|
||||||
, rbo_depth_(0)
|
|
||||||
, position_texture_(0)
|
|
||||||
, normal_texture_(0)
|
|
||||||
, albedo_texture_(0)
|
|
||||||
, material_texture_(0)
|
|
||||||
, depth_texture_(0)
|
|
||||||
, primitive_id_texture_(0)
|
|
||||||
, width_(width)
|
|
||||||
, height_(height) {
|
|
||||||
|
|
||||||
create_textures();
|
|
||||||
create_framebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
GBuffer::~GBuffer() {
|
|
||||||
delete_textures();
|
|
||||||
|
|
||||||
if (rbo_depth_ != 0) {
|
|
||||||
glDeleteRenderbuffers(1, &rbo_depth_);
|
|
||||||
rbo_depth_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fbo_ != 0) {
|
|
||||||
glDeleteFramebuffers(1, &fbo_);
|
|
||||||
fbo_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::resize(int width, int height) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (width == width_ && height == height_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
width_ = width;
|
|
||||||
height_ = height;
|
|
||||||
|
|
||||||
delete_textures();
|
|
||||||
|
|
||||||
if (rbo_depth_ != 0) {
|
|
||||||
glDeleteRenderbuffers(1, &rbo_depth_);
|
|
||||||
rbo_depth_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fbo_ != 0) {
|
|
||||||
glDeleteFramebuffers(1, &fbo_);
|
|
||||||
fbo_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
create_textures();
|
|
||||||
create_framebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::bind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
|
||||||
glViewport(0, 0, width_, height_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::unbind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::clear() {
|
|
||||||
bind();
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::bind_texture(int index, int texture_unit) {
|
|
||||||
glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
||||||
|
|
||||||
switch (index) {
|
|
||||||
case 0: glBindTexture(GL_TEXTURE_2D, position_texture_); break;
|
|
||||||
case 1: glBindTexture(GL_TEXTURE_2D, normal_texture_); break;
|
|
||||||
case 2: glBindTexture(GL_TEXTURE_2D, albedo_texture_); break;
|
|
||||||
case 3: glBindTexture(GL_TEXTURE_2D, material_texture_); break;
|
|
||||||
case 4: glBindTexture(GL_TEXTURE_2D, depth_texture_); break;
|
|
||||||
case 5: glBindTexture(GL_TEXTURE_2D, primitive_id_texture_); break;
|
|
||||||
default:
|
|
||||||
ARE_LOG_WARN("GBuffer: Invalid texture index " + std::to_string(index));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::read_pixels(int index, void* data) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Robust: read from texture object (not from FBO read buffer)
|
|
||||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
||||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
||||||
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
|
||||||
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
|
||||||
|
|
||||||
uint32_t tex = 0;
|
|
||||||
GLenum format = GL_RGBA;
|
|
||||||
GLenum type = GL_UNSIGNED_BYTE;
|
|
||||||
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
tex = position_texture_;
|
|
||||||
format = GL_RGB;
|
|
||||||
type = GL_FLOAT;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
tex = normal_texture_;
|
|
||||||
format = GL_RGB;
|
|
||||||
type = GL_FLOAT;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
tex = albedo_texture_;
|
|
||||||
format = GL_RGBA;
|
|
||||||
type = GL_UNSIGNED_BYTE;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
tex = material_texture_;
|
|
||||||
format = GL_RG;
|
|
||||||
type = GL_UNSIGNED_BYTE;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
tex = depth_texture_;
|
|
||||||
format = GL_DEPTH_COMPONENT;
|
|
||||||
type = GL_FLOAT;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
tex = primitive_id_texture_;
|
|
||||||
format = GL_RED_INTEGER;
|
|
||||||
type = GL_UNSIGNED_INT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ARE_LOG_ERROR("GBuffer: Invalid buffer index for read_pixels");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
|
||||||
glGetTexImage(GL_TEXTURE_2D, 0, format, type, data);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::create_textures() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
glGenTextures(1, &position_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, position_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glGenTextures(1, &normal_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, normal_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width_, height_, 0, GL_RGB, GL_FLOAT, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glGenTextures(1, &albedo_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, albedo_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glGenTextures(1, &material_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, material_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width_, height_, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glGenTextures(1, &depth_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width_, height_, 0, GL_DEPTH_COMPONENT, 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);
|
|
||||||
|
|
||||||
glGenTextures(1, &primitive_id_texture_);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, primitive_id_texture_);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, width_, height_, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::delete_textures() {
|
|
||||||
if (position_texture_ != 0) glDeleteTextures(1, &position_texture_);
|
|
||||||
if (normal_texture_ != 0) glDeleteTextures(1, &normal_texture_);
|
|
||||||
if (albedo_texture_ != 0) glDeleteTextures(1, &albedo_texture_);
|
|
||||||
if (material_texture_ != 0) glDeleteTextures(1, &material_texture_);
|
|
||||||
if (depth_texture_ != 0) glDeleteTextures(1, &depth_texture_);
|
|
||||||
if (primitive_id_texture_ != 0) glDeleteTextures(1, &primitive_id_texture_);
|
|
||||||
|
|
||||||
position_texture_ = 0;
|
|
||||||
normal_texture_ = 0;
|
|
||||||
albedo_texture_ = 0;
|
|
||||||
material_texture_ = 0;
|
|
||||||
depth_texture_ = 0;
|
|
||||||
primitive_id_texture_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBuffer::create_framebuffer() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo_);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, position_texture_, 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normal_texture_, 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, albedo_texture_, 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, material_texture_, 0);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, primitive_id_texture_, 0);
|
|
||||||
|
|
||||||
GLenum draw_buffers[] = {
|
|
||||||
GL_COLOR_ATTACHMENT0,
|
|
||||||
GL_COLOR_ATTACHMENT1,
|
|
||||||
GL_COLOR_ATTACHMENT2,
|
|
||||||
GL_COLOR_ATTACHMENT3,
|
|
||||||
GL_COLOR_ATTACHMENT4
|
|
||||||
};
|
|
||||||
glDrawBuffers(5, draw_buffers);
|
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture_, 0);
|
|
||||||
|
|
||||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
||||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|
||||||
ARE_LOG_ERROR("GBuffer: Framebuffer incomplete. Status=" + std::to_string(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,197 +0,0 @@
|
||||||
/**
|
|
||||||
* @file rasterizer.cpp
|
|
||||||
* @brief Implementation of Rasterizer class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/rasterizer/rasterizer.h>
|
|
||||||
#include <are/rasterizer/gbuffer.h>
|
|
||||||
#include <are/rasterizer/shader_program.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/geometry/vertex.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/platform/gl_context.h>
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <glm/gtc/matrix_inverse.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Rasterizer::Rasterizer(int width, int height)
|
|
||||||
: gbuffer_(std::make_unique<GBuffer>(width, height))
|
|
||||||
, gbuffer_shader_(std::make_unique<ShaderProgram>())
|
|
||||||
, triangle_base_provider_(nullptr)
|
|
||||||
, state_()
|
|
||||||
, width_(width)
|
|
||||||
, height_(height) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Rasterizer::~Rasterizer() = default;
|
|
||||||
|
|
||||||
void Rasterizer::set_state(const RasterizerState& state) {
|
|
||||||
state_ = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::set_triangle_base_provider(std::function<uint32_t(size_t)> provider) {
|
|
||||||
triangle_base_provider_ = std::move(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::resize(int width, int height) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
if (width == width_ && height == height_) return;
|
|
||||||
width_ = width;
|
|
||||||
height_ = height;
|
|
||||||
gbuffer_->resize(width_, height_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (!gbuffer_shader_ || !gbuffer_shader_->is_valid()) {
|
|
||||||
ARE_LOG_ERROR("Rasterizer: gbuffer shader not ready");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gbuffer_->bind();
|
|
||||||
|
|
||||||
glClearColor(0, 0, 0, 0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
if (state_.enable_depth_test) {
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glDepthFunc(GL_LESS);
|
|
||||||
} else {
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state_.enable_cull_face) {
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glCullFace(static_cast<GLenum>(state_.cull_face_mode));
|
|
||||||
glFrontFace(static_cast<GLenum>(state_.front_face));
|
|
||||||
} else {
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
gbuffer_shader_->use();
|
|
||||||
gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix());
|
|
||||||
gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix());
|
|
||||||
|
|
||||||
const auto& meshes = scene.get_all_meshes();
|
|
||||||
|
|
||||||
for (size_t mi = 0; mi < meshes.size(); ++mi) {
|
|
||||||
const auto& mesh = meshes[mi];
|
|
||||||
if (mesh.is_empty() || !mesh.has_gpu_resources()) continue;
|
|
||||||
|
|
||||||
Mat4 model = Mat4(1.0f);
|
|
||||||
gbuffer_shader_->set_uniform("u_model", model);
|
|
||||||
|
|
||||||
Mat3 normal_matrix = glm::inverseTranspose(Mat3(model));
|
|
||||||
gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix);
|
|
||||||
|
|
||||||
uint32_t tri_base = triangle_base_provider_ ? triangle_base_provider_(mi) : 0u;
|
|
||||||
// IMPORTANT: u_triangle_id_base is uint in GLSL, must use glUniform1ui
|
|
||||||
gbuffer_shader_->set_uniform("u_triangle_id_base", tri_base);
|
|
||||||
|
|
||||||
const Material* mat = scene.get_material(mesh.get_material());
|
|
||||||
if (mat) {
|
|
||||||
gbuffer_shader_->set_uniform("u_albedo", mat->get_albedo());
|
|
||||||
gbuffer_shader_->set_uniform("u_metallic", mat->get_metallic());
|
|
||||||
gbuffer_shader_->set_uniform("u_roughness", mat->get_roughness());
|
|
||||||
} else {
|
|
||||||
gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f));
|
|
||||||
gbuffer_shader_->set_uniform("u_metallic", 0.0f);
|
|
||||||
gbuffer_shader_->set_uniform("u_roughness", 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(mesh.get_vao());
|
|
||||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.get_index_count()), GL_UNSIGNED_INT, nullptr);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gbuffer_->unbind();
|
|
||||||
ARE_GL_CHECK();
|
|
||||||
}
|
|
||||||
|
|
||||||
GBuffer& Rasterizer::get_gbuffer() { return *gbuffer_; }
|
|
||||||
const GBuffer& Rasterizer::get_gbuffer() const { return *gbuffer_; }
|
|
||||||
|
|
||||||
void Rasterizer::upload_mesh(Mesh& mesh) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
if (mesh.is_empty()) {
|
|
||||||
ARE_LOG_WARN("Rasterizer: upload_mesh on empty mesh");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mesh.has_gpu_resources()) delete_mesh(mesh);
|
|
||||||
setup_mesh_buffers(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::delete_mesh(Mesh& mesh) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
uint32_t vao = mesh.get_vao();
|
|
||||||
uint32_t vbo = mesh.get_vbo();
|
|
||||||
uint32_t ebo = mesh.get_ebo();
|
|
||||||
|
|
||||||
if (vao) glDeleteVertexArrays(1, &vao);
|
|
||||||
if (vbo) glDeleteBuffers(1, &vbo);
|
|
||||||
if (ebo) glDeleteBuffers(1, &ebo);
|
|
||||||
|
|
||||||
mesh.set_vao(0);
|
|
||||||
mesh.set_vbo(0);
|
|
||||||
mesh.set_ebo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::initialize_shaders(const std::string& shader_dir) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert");
|
|
||||||
ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag");
|
|
||||||
ok &= gbuffer_shader_->link();
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
ARE_LOG_ERROR("Rasterizer: Failed to init gbuffer shaders");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rasterizer::setup_mesh_buffers(Mesh& mesh) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
uint32_t vao = 0, vbo = 0, ebo = 0;
|
|
||||||
glGenVertexArrays(1, &vao);
|
|
||||||
glBindVertexArray(vao);
|
|
||||||
|
|
||||||
glGenBuffers(1, &vbo);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, mesh.get_vertex_count() * sizeof(Vertex), mesh.get_vertices().data(), GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glGenBuffers(1, &ebo);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.get_index_count() * sizeof(uint32_t), mesh.get_indices().data(), GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
||||||
reinterpret_cast<void*>(get_position_offset()));
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
||||||
reinterpret_cast<void*>(get_normal_offset()));
|
|
||||||
glEnableVertexAttribArray(2);
|
|
||||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
||||||
reinterpret_cast<void*>(get_texcoord_offset()));
|
|
||||||
glEnableVertexAttribArray(3);
|
|
||||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
||||||
reinterpret_cast<void*>(get_tangent_offset()));
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
mesh.set_vao(vao);
|
|
||||||
mesh.set_vbo(vbo);
|
|
||||||
mesh.set_ebo(ebo);
|
|
||||||
|
|
||||||
ARE_GL_CHECK();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,255 +0,0 @@
|
||||||
/**
|
|
||||||
* @file shader_program.cpp
|
|
||||||
* @brief Implementation of ShaderProgram class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/rasterizer/shader_program.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/utils/file_utils.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
ShaderProgram::ShaderProgram()
|
|
||||||
: program_(0)
|
|
||||||
, vertex_shader_(0)
|
|
||||||
, fragment_shader_(0)
|
|
||||||
, compute_shader_(0)
|
|
||||||
, linked_(false) {
|
|
||||||
program_ = glCreateProgram();
|
|
||||||
if (program_ == 0) {
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Failed to create OpenGL program");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderProgram::~ShaderProgram() {
|
|
||||||
if (vertex_shader_ != 0) {
|
|
||||||
glDeleteShader(vertex_shader_);
|
|
||||||
}
|
|
||||||
if (fragment_shader_ != 0) {
|
|
||||||
glDeleteShader(fragment_shader_);
|
|
||||||
}
|
|
||||||
if (compute_shader_ != 0) {
|
|
||||||
glDeleteShader(compute_shader_);
|
|
||||||
}
|
|
||||||
if (program_ != 0) {
|
|
||||||
glDeleteProgram(program_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderProgram::load_shader(ShaderType type, const std::string& filepath) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Read shader source from file
|
|
||||||
std::string source = read_file_to_string(filepath);
|
|
||||||
if (source.empty()) {
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Failed to read shader file: " + filepath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("ShaderProgram: Loaded shader from " + filepath);
|
|
||||||
return compile_shader(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderProgram::compile_shader(ShaderType type, const std::string& source) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
GLenum gl_type;
|
|
||||||
uint32_t* shader_id;
|
|
||||||
std::string type_name;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case ShaderType::ARE_SHADER_VERTEX:
|
|
||||||
gl_type = GL_VERTEX_SHADER;
|
|
||||||
shader_id = &vertex_shader_;
|
|
||||||
type_name = "vertex";
|
|
||||||
break;
|
|
||||||
case ShaderType::ARE_SHADER_FRAGMENT:
|
|
||||||
gl_type = GL_FRAGMENT_SHADER;
|
|
||||||
shader_id = &fragment_shader_;
|
|
||||||
type_name = "fragment";
|
|
||||||
break;
|
|
||||||
case ShaderType::ARE_SHADER_COMPUTE:
|
|
||||||
gl_type = GL_COMPUTE_SHADER;
|
|
||||||
shader_id = &compute_shader_;
|
|
||||||
type_name = "compute";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Unknown shader type");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete existing shader if any
|
|
||||||
if (*shader_id != 0) {
|
|
||||||
glDeleteShader(*shader_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and compile shader
|
|
||||||
*shader_id = glCreateShader(gl_type);
|
|
||||||
const char* source_cstr = source.c_str();
|
|
||||||
glShaderSource(*shader_id, 1, &source_cstr, nullptr);
|
|
||||||
glCompileShader(*shader_id);
|
|
||||||
|
|
||||||
// Check compilation errors
|
|
||||||
if (!check_compile_errors(*shader_id, type)) {
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Failed to compile " + type_name + " shader");
|
|
||||||
glDeleteShader(*shader_id);
|
|
||||||
*shader_id = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach shader to program
|
|
||||||
glAttachShader(program_, *shader_id);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("ShaderProgram: Compiled " + type_name + " shader successfully");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderProgram::link() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (program_ == 0) {
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Cannot link invalid program");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link program
|
|
||||||
glLinkProgram(program_);
|
|
||||||
|
|
||||||
// Check link errors
|
|
||||||
if (!check_link_errors()) {
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Failed to link shader program");
|
|
||||||
linked_ = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detach and delete shaders after successful link
|
|
||||||
if (vertex_shader_ != 0) {
|
|
||||||
glDetachShader(program_, vertex_shader_);
|
|
||||||
glDeleteShader(vertex_shader_);
|
|
||||||
vertex_shader_ = 0;
|
|
||||||
}
|
|
||||||
if (fragment_shader_ != 0) {
|
|
||||||
glDetachShader(program_, fragment_shader_);
|
|
||||||
glDeleteShader(fragment_shader_);
|
|
||||||
fragment_shader_ = 0;
|
|
||||||
}
|
|
||||||
if (compute_shader_ != 0) {
|
|
||||||
glDetachShader(program_, compute_shader_);
|
|
||||||
glDeleteShader(compute_shader_);
|
|
||||||
compute_shader_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
linked_ = true;
|
|
||||||
ARE_LOG_INFO("ShaderProgram: Linked shader program successfully");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::use() const {
|
|
||||||
if (is_valid()) {
|
|
||||||
glUseProgram(program_);
|
|
||||||
} else {
|
|
||||||
ARE_LOG_WARN("ShaderProgram: Attempting to use invalid program");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, uint32_t value) {
|
|
||||||
glUniform1ui(get_uniform_location(name), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, int value) {
|
|
||||||
glUniform1ui(get_uniform_location(name), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, float value) {
|
|
||||||
glUniform1f(get_uniform_location(name), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, const Vec2& value) {
|
|
||||||
glUniform2fv(get_uniform_location(name), 1, glm::value_ptr(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, const Vec3& value) {
|
|
||||||
glUniform3fv(get_uniform_location(name), 1, glm::value_ptr(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, const Vec4& value) {
|
|
||||||
glUniform4fv(get_uniform_location(name), 1, glm::value_ptr(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, const Mat3& value) {
|
|
||||||
glUniformMatrix3fv(get_uniform_location(name), 1, GL_FALSE, glm::value_ptr(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderProgram::set_uniform(const std::string& name, const Mat4& value) {
|
|
||||||
glUniformMatrix4fv(get_uniform_location(name), 1, GL_FALSE, glm::value_ptr(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShaderProgram::get_uniform_location(const std::string& name) {
|
|
||||||
// Check cache first
|
|
||||||
auto it = uniform_cache_.find(name);
|
|
||||||
if (it != uniform_cache_.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query OpenGL
|
|
||||||
int location = glGetUniformLocation(program_, name.c_str());
|
|
||||||
if (location == -1) {
|
|
||||||
ARE_LOG_WARN("ShaderProgram: Uniform '" + name + "' not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the location
|
|
||||||
uniform_cache_[name] = location;
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderProgram::check_compile_errors(uint32_t shader, ShaderType type) {
|
|
||||||
GLint success;
|
|
||||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
GLint log_length;
|
|
||||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
|
|
||||||
|
|
||||||
std::string info_log;
|
|
||||||
info_log.resize(log_length);
|
|
||||||
glGetShaderInfoLog(shader, log_length, nullptr, &info_log[0]);
|
|
||||||
|
|
||||||
std::string type_name;
|
|
||||||
switch (type) {
|
|
||||||
case ShaderType::ARE_SHADER_VERTEX: type_name = "VERTEX"; break;
|
|
||||||
case ShaderType::ARE_SHADER_FRAGMENT: type_name = "FRAGMENT"; break;
|
|
||||||
case ShaderType::ARE_SHADER_COMPUTE: type_name = "COMPUTE"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: " + type_name + " shader compilation failed:");
|
|
||||||
ARE_LOG_ERROR(info_log);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderProgram::check_link_errors() {
|
|
||||||
GLint success;
|
|
||||||
glGetProgramiv(program_, GL_LINK_STATUS, &success);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
GLint log_length;
|
|
||||||
glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length);
|
|
||||||
|
|
||||||
std::string info_log;
|
|
||||||
info_log.resize(log_length);
|
|
||||||
glGetProgramInfoLog(program_, log_length, nullptr, &info_log[0]);
|
|
||||||
|
|
||||||
ARE_LOG_ERROR("ShaderProgram: Program linking failed:");
|
|
||||||
ARE_LOG_ERROR(info_log);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,334 +0,0 @@
|
||||||
/**
|
|
||||||
* @file cpu_raytracer.cpp
|
|
||||||
* @brief CPU hybrid ray tracer (GBuffer-driven) with geometric normal offset
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/raytracer/cpu_raytracer.h>
|
|
||||||
|
|
||||||
#include <are/acceleration/bvh.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
#include <are/scene/directional_light.h>
|
|
||||||
#include <are/scene/point_light.h>
|
|
||||||
#include <are/scene/spot_light.h>
|
|
||||||
#include <are/rasterizer/gbuffer.h>
|
|
||||||
|
|
||||||
#include <are/utils/random.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
inline Real compute_ray_epsilon(const Vec3& p) {
|
|
||||||
Real s = std::max({std::abs(p.x), std::abs(p.y), std::abs(p.z), 1.0f});
|
|
||||||
return 1e-4f * s;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vec3 offset_ray_origin(const Vec3& p, const Vec3& ng) {
|
|
||||||
Real eps = compute_ray_epsilon(p);
|
|
||||||
return p + ng * (eps * 4.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vec3 tonemap_reinhard(const Vec3& c, Real exposure) {
|
|
||||||
Vec3 x = c * exposure;
|
|
||||||
return x / (Vec3(1.0f) + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Vec3 decode_albedo_from_rgba8(uint8_t r, uint8_t g, uint8_t b) {
|
|
||||||
return Vec3(r, g, b) / 255.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Real decode_01_from_u8(uint8_t v) {
|
|
||||||
return static_cast<Real>(v) / 255.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool finite_vec3(const Vec3& v) {
|
|
||||||
return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
CPURayTracer::CPURayTracer(const RayTracingConfig& config)
|
|
||||||
: RayTracer(config)
|
|
||||||
, bvh_(nullptr)
|
|
||||||
, scene_(nullptr)
|
|
||||||
, framebuffer_()
|
|
||||||
, width_(0)
|
|
||||||
, height_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
CPURayTracer::~CPURayTracer() = default;
|
|
||||||
|
|
||||||
void CPURayTracer::update_bvh(const BVH& bvh) {
|
|
||||||
bvh_ = &bvh;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPURayTracer::render(const SceneManager& scene,
|
|
||||||
const Camera& camera,
|
|
||||||
const GBuffer* gbuffer,
|
|
||||||
uint32_t output_texture) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
(void)camera;
|
|
||||||
|
|
||||||
if (!bvh_ || !bvh_->is_built()) {
|
|
||||||
ARE_LOG_ERROR("CPURayTracer: BVH is null or not built");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gbuffer) {
|
|
||||||
ARE_LOG_CRITICAL("CPURayTracer: GBuffer is null (hybrid requires it)");
|
|
||||||
throw std::runtime_error("CPURayTracer requires GBuffer in hybrid mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output_texture == 0) {
|
|
||||||
ARE_LOG_ERROR("CPURayTracer: output_texture is 0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scene_ = &scene;
|
|
||||||
width_ = gbuffer->get_width();
|
|
||||||
height_ = gbuffer->get_height();
|
|
||||||
|
|
||||||
if (width_ <= 0 || height_ <= 0) {
|
|
||||||
ARE_LOG_ERROR("CPURayTracer: Invalid resolution");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Vec3> pos(static_cast<size_t>(width_ * height_));
|
|
||||||
std::vector<Vec3> nrm(static_cast<size_t>(width_ * height_));
|
|
||||||
std::vector<uint8_t> albedo_metallic(static_cast<size_t>(width_ * height_ * 4));
|
|
||||||
std::vector<uint8_t> rough_ao(static_cast<size_t>(width_ * height_ * 2));
|
|
||||||
std::vector<Real> depth(static_cast<size_t>(width_ * height_));
|
|
||||||
std::vector<uint32_t> prim_id(static_cast<size_t>(width_ * height_));
|
|
||||||
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(0, pos.data());
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(1, nrm.data());
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(2, albedo_metallic.data());
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(3, rough_ao.data());
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(4, depth.data());
|
|
||||||
const_cast<GBuffer *>(gbuffer)->read_pixels(5, prim_id.data());
|
|
||||||
|
|
||||||
framebuffer_.assign(static_cast<size_t>(width_ * height_), Vec3(0.0f));
|
|
||||||
|
|
||||||
const int spp = std::max(1, config_.spp);
|
|
||||||
const int max_depth = std::max(1, config_.max_depth);
|
|
||||||
const auto& triangles = bvh_->get_triangles();
|
|
||||||
|
|
||||||
for (int y = 0; y < height_; ++y) {
|
|
||||||
RandomGenerator& rng = get_thread_random();
|
|
||||||
|
|
||||||
for (int x = 0; x < width_; ++x) {
|
|
||||||
const size_t idx = static_cast<size_t>(y * width_ + x);
|
|
||||||
|
|
||||||
// Depth validity
|
|
||||||
if (!(depth[idx] > 0.0f && depth[idx] < 0.999999f)) {
|
|
||||||
framebuffer_[idx] = Vec3(0.0f);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 P = pos[idx];
|
|
||||||
Vec3 Ns = glm::normalize(nrm[idx]);
|
|
||||||
|
|
||||||
if (!finite_vec3(P) || !finite_vec3(Ns) || glm::length(Ns) < 0.1f) {
|
|
||||||
framebuffer_[idx] = Vec3(0.0f);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Geometric normal from primitive id
|
|
||||||
Vec3 Ng = Ns;
|
|
||||||
uint32_t pid = prim_id[idx];
|
|
||||||
if (pid < triangles.size()) {
|
|
||||||
Ng = triangles[pid].normal();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* am = &albedo_metallic[idx * 4];
|
|
||||||
Vec3 albedo = decode_albedo_from_rgba8(am[0], am[1], am[2]);
|
|
||||||
(void)decode_01_from_u8(am[3]);
|
|
||||||
|
|
||||||
const uint8_t* ra = &rough_ao[idx * 2];
|
|
||||||
(void)decode_01_from_u8(ra[0]);
|
|
||||||
Real ao_gbuffer = decode_01_from_u8(ra[1]);
|
|
||||||
|
|
||||||
Vec3 accum(0.0f);
|
|
||||||
|
|
||||||
for (int s = 0; s < spp; ++s) {
|
|
||||||
HitRecord surf;
|
|
||||||
surf.position_ = P;
|
|
||||||
surf.normal_ = Ns;
|
|
||||||
surf.t_ = 1.0f;
|
|
||||||
surf.material_ = are_invalid_handle;
|
|
||||||
|
|
||||||
// Direct lighting (shadow uses robust epsilon)
|
|
||||||
Vec3 direct = compute_direct_lighting(surf);
|
|
||||||
|
|
||||||
// AO
|
|
||||||
Real ao = 1.0f;
|
|
||||||
if (config_.enable_ao) {
|
|
||||||
ao = compute_ambient_occlusion(surf);
|
|
||||||
}
|
|
||||||
ao *= ao_gbuffer;
|
|
||||||
|
|
||||||
// GI (simplified)
|
|
||||||
Vec3 gi(0.0f);
|
|
||||||
if (config_.enable_gi && max_depth > 1) {
|
|
||||||
Vec3 bounce_dir = rng.random_cosine_direction(Ns);
|
|
||||||
Vec3 origin = offset_ray_origin(P, Ng);
|
|
||||||
Real eps = compute_ray_epsilon(P);
|
|
||||||
Ray bounce(origin, bounce_dir, eps * 4.0f, 1e30f);
|
|
||||||
gi = trace_ray(bounce, max_depth - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 c = albedo * direct * ao + albedo * gi;
|
|
||||||
accum += c;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 hdr = accum / static_cast<Real>(spp);
|
|
||||||
framebuffer_[idx] = tonemap_reinhard(hdr, 1.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Vec4> rgba(static_cast<size_t>(width_ * height_));
|
|
||||||
for (size_t i = 0; i < rgba.size(); ++i) {
|
|
||||||
rgba[i] = Vec4(framebuffer_[i], 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, output_texture);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
||||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
|
||||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_FLOAT, rgba.data());
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 CPURayTracer::trace_ray(const Ray& ray, int depth) {
|
|
||||||
if (depth <= 0) {
|
|
||||||
return Vec3(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
HitRecord hit;
|
|
||||||
if (!bvh_->intersect(ray, hit)) {
|
|
||||||
return Vec3(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Ng = hit.normal_;
|
|
||||||
if (hit.triangle_index_ < bvh_->get_triangles().size()) {
|
|
||||||
Ng = bvh_->get_triangles()[hit.triangle_index_].normal();
|
|
||||||
}
|
|
||||||
|
|
||||||
RandomGenerator& rng = get_thread_random();
|
|
||||||
Vec3 dir = rng.random_cosine_direction(hit.normal_);
|
|
||||||
Vec3 origin = offset_ray_origin(hit.position_, Ng);
|
|
||||||
Real eps = compute_ray_epsilon(hit.position_);
|
|
||||||
|
|
||||||
Ray bounce(origin, dir, eps * 4.0f, 1e30f);
|
|
||||||
return trace_ray(bounce, depth - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 CPURayTracer::compute_direct_lighting(const HitRecord& hit) {
|
|
||||||
Vec3 lighting(0.0f);
|
|
||||||
if (!scene_) {
|
|
||||||
return lighting;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& lights = scene_->get_all_lights();
|
|
||||||
for (const auto& light_ptr : lights) {
|
|
||||||
if (!light_ptr) continue;
|
|
||||||
|
|
||||||
Vec3 L(0.0f);
|
|
||||||
Real max_distance = 1e30f;
|
|
||||||
Real attenuation = 1.0f;
|
|
||||||
|
|
||||||
LightType type = light_ptr->get_type();
|
|
||||||
|
|
||||||
if (type == LightType::ARE_LIGHT_DIRECTIONAL) {
|
|
||||||
const auto* dl = static_cast<const DirectionalLight*>(light_ptr.get());
|
|
||||||
L = -glm::normalize(dl->get_direction());
|
|
||||||
} else if (type == LightType::ARE_LIGHT_POINT) {
|
|
||||||
const auto* pl = static_cast<const PointLight*>(light_ptr.get());
|
|
||||||
Vec3 to_light = pl->get_position() - hit.position_;
|
|
||||||
Real dist = glm::length(to_light);
|
|
||||||
if (dist < are_epsilon) continue;
|
|
||||||
if (!pl->affects_point(hit.position_)) continue;
|
|
||||||
L = to_light / dist;
|
|
||||||
max_distance = dist;
|
|
||||||
attenuation = pl->calculate_attenuation(dist);
|
|
||||||
} else if (type == LightType::ARE_LIGHT_SPOT) {
|
|
||||||
const auto* sl = static_cast<const SpotLight*>(light_ptr.get());
|
|
||||||
Vec3 to_light = sl->get_position() - hit.position_;
|
|
||||||
Real dist = glm::length(to_light);
|
|
||||||
if (dist < are_epsilon) continue;
|
|
||||||
if (!sl->affects_point(hit.position_)) continue;
|
|
||||||
L = to_light / dist;
|
|
||||||
max_distance = dist;
|
|
||||||
Vec3 light_to_point = glm::normalize(hit.position_ - sl->get_position());
|
|
||||||
attenuation *= sl->calculate_spot_factor(light_to_point);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (light_ptr->get_cast_shadows()) {
|
|
||||||
Real eps = compute_ray_epsilon(hit.position_);
|
|
||||||
Vec3 origin = hit.position_ + hit.normal_ * (eps * 4.0f);
|
|
||||||
Ray shadow(origin, L, eps * 4.0f, max_distance);
|
|
||||||
if (bvh_ && bvh_->intersect_any(shadow, max_distance)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Real n_dot_l = std::max(0.0f, glm::dot(hit.normal_, L));
|
|
||||||
if (n_dot_l <= 0.0f) continue;
|
|
||||||
|
|
||||||
Vec3 radiance = light_ptr->get_color() * light_ptr->get_intensity();
|
|
||||||
lighting += radiance * n_dot_l * attenuation;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lighting;
|
|
||||||
}
|
|
||||||
|
|
||||||
Real CPURayTracer::compute_ambient_occlusion(const HitRecord& hit) {
|
|
||||||
if (!bvh_) {
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int ao_samples = std::max(1, config_.ao_samples);
|
|
||||||
const Real radius = std::max(are_epsilon, config_.ao_radius);
|
|
||||||
|
|
||||||
RandomGenerator& rng = get_thread_random();
|
|
||||||
|
|
||||||
int occluded = 0;
|
|
||||||
for (int i = 0; i < ao_samples; ++i) {
|
|
||||||
Vec3 dir = rng.random_in_hemisphere(hit.normal_);
|
|
||||||
Real eps = compute_ray_epsilon(hit.position_);
|
|
||||||
Vec3 origin = hit.position_ + hit.normal_ * (eps * 4.0f);
|
|
||||||
Ray ao_ray(origin, dir, eps * 4.0f, radius);
|
|
||||||
if (bvh_->intersect_any(ao_ray, radius)) {
|
|
||||||
occluded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Real occ = static_cast<Real>(occluded) / static_cast<Real>(ao_samples);
|
|
||||||
return 1.0f - occ;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPURayTracer::is_in_shadow(const Vec3& origin, const Vec3& direction, Real max_distance, uint32_t ignore_triangle) {
|
|
||||||
if (!bvh_) return false;
|
|
||||||
|
|
||||||
Real t_max = (max_distance > 0.0f) ? max_distance : 1e30f;
|
|
||||||
Real eps = compute_ray_epsilon(origin);
|
|
||||||
Ray shadow(origin, direction, eps * 4.0f, t_max);
|
|
||||||
return bvh_->intersect_any(shadow, t_max, ignore_triangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/**
|
|
||||||
* @file hit_record.cpp
|
|
||||||
* @brief Implementation of HitRecord structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/raytracer/hit_record.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
HitRecord::HitRecord()
|
|
||||||
: position_(0.0f)
|
|
||||||
, normal_(0.0f, 1.0f, 0.0f)
|
|
||||||
, texcoord_(0.0f)
|
|
||||||
, tangent_(1.0f, 0.0f, 0.0f)
|
|
||||||
, t_(std::numeric_limits<Real>::max())
|
|
||||||
, material_(are_invalid_handle)
|
|
||||||
, triangle_index_(0)
|
|
||||||
, front_face_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void HitRecord::set_face_normal(const Vec3& ray_direction, const Vec3& outward_normal) {
|
|
||||||
front_face_ = glm::dot(ray_direction, outward_normal) < 0.0f;
|
|
||||||
normal_ = front_face_ ? outward_normal : -outward_normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HitRecord::is_valid() const {
|
|
||||||
return t_ > 0.0f && t_ < std::numeric_limits<Real>::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* @file ray.cpp
|
|
||||||
* @brief Implementation of Ray structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/raytracer/ray.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Ray::Ray()
|
|
||||||
: origin_(0.0f)
|
|
||||||
, direction_(0.0f, 0.0f, 1.0f)
|
|
||||||
, t_min_(are_epsilon)
|
|
||||||
, t_max_(1e30f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Ray::Ray(const Vec3& origin, const Vec3& direction, Real t_min, Real t_max)
|
|
||||||
: origin_(origin)
|
|
||||||
, t_min_(t_min)
|
|
||||||
, t_max_(t_max) {
|
|
||||||
// Normalize direction vector
|
|
||||||
Real length = glm::length(direction);
|
|
||||||
if (length < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("Ray: Direction vector has zero length, using default (0,0,1)");
|
|
||||||
direction_ = Vec3(0.0f, 0.0f, 1.0f);
|
|
||||||
} else {
|
|
||||||
direction_ = direction / length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Ray::at(Real t) const {
|
|
||||||
return origin_ + direction_ * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Ray::is_valid_t(Real t) const {
|
|
||||||
return t >= t_min_ && t <= t_max_;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
/**
|
|
||||||
* @file raytracer.cpp
|
|
||||||
* @brief Implementation of RayTracer interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/raytracer/raytracer.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
RayTracer::RayTracer(const RayTracingConfig& config)
|
|
||||||
: config_(config) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void RayTracer::set_config(const RayTracingConfig& config) {
|
|
||||||
config_ = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
/**
|
|
||||||
* @file geometry_cache.cpp
|
|
||||||
* @brief Implementation of GeometryCache
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/renderer/geometry_cache.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
static std::vector<Triangle> mesh_to_triangles(const Mesh& mesh) {
|
|
||||||
std::vector<Triangle> tris;
|
|
||||||
if (mesh.is_empty()) {
|
|
||||||
return tris;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& v = mesh.get_vertices();
|
|
||||||
const auto& idx = mesh.get_indices();
|
|
||||||
if (idx.size() % 3 != 0) {
|
|
||||||
ARE_LOG_ERROR("GeometryCache: Mesh index count is not multiple of 3");
|
|
||||||
return tris;
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialHandle material = mesh.get_material();
|
|
||||||
tris.reserve(idx.size() / 3);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < idx.size(); i += 3) {
|
|
||||||
uint32_t i0 = idx[i + 0];
|
|
||||||
uint32_t i1 = idx[i + 1];
|
|
||||||
uint32_t i2 = idx[i + 2];
|
|
||||||
|
|
||||||
if (i0 >= v.size() || i1 >= v.size() || i2 >= v.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tris.emplace_back(v[i0], v[i1], v[i2], material);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tris;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeometryCache::build_from_scene(const SceneManager& scene, const BVHBuildConfig& bvh_config) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
triangles_.clear();
|
|
||||||
mesh_triangle_base_.clear();
|
|
||||||
|
|
||||||
const auto& meshes = scene.get_all_meshes();
|
|
||||||
mesh_triangle_base_.reserve(meshes.size());
|
|
||||||
|
|
||||||
uint32_t base = 0;
|
|
||||||
|
|
||||||
for (size_t mi = 0; mi < meshes.size(); ++mi) {
|
|
||||||
mesh_triangle_base_.push_back(base);
|
|
||||||
|
|
||||||
auto tris = mesh_to_triangles(meshes[mi]);
|
|
||||||
base += static_cast<uint32_t>(tris.size());
|
|
||||||
|
|
||||||
triangles_.insert(triangles_.end(), tris.begin(), tris.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triangles_.empty()) {
|
|
||||||
ARE_LOG_WARN("GeometryCache: No triangles in scene");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bvh_.build(triangles_, bvh_config)) {
|
|
||||||
ARE_LOG_ERROR("GeometryCache: BVH build failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("GeometryCache: Built triangles=" + std::to_string(triangles_.size()) +
|
|
||||||
", meshes=" + std::to_string(meshes.size()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GeometryCache::get_mesh_triangle_base(size_t mesh_index) const {
|
|
||||||
if (mesh_index >= mesh_triangle_base_.size()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return mesh_triangle_base_[mesh_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,200 +0,0 @@
|
||||||
/**
|
|
||||||
* @file camera.cpp
|
|
||||||
* @brief Implementation of Camera class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/camera.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/utils/math_utils.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)
|
|
||||||
, fov_(45.0f)
|
|
||||||
, aspect_ratio_(16.0f / 9.0f)
|
|
||||||
, near_plane_(0.1f)
|
|
||||||
, far_plane_(1000.0f)
|
|
||||||
, view_matrix_(1.0f)
|
|
||||||
, projection_matrix_(1.0f)
|
|
||||||
, view_dirty_(true)
|
|
||||||
, projection_dirty_(true)
|
|
||||||
, dirty_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera::Camera(const Vec3& position, const Vec3& target, const Vec3& up)
|
|
||||||
: position_(position)
|
|
||||||
, target_(target)
|
|
||||||
, up_(glm::normalize(up))
|
|
||||||
, fov_(45.0f)
|
|
||||||
, aspect_ratio_(16.0f / 9.0f)
|
|
||||||
, near_plane_(0.1f)
|
|
||||||
, far_plane_(1000.0f)
|
|
||||||
, view_matrix_(1.0f)
|
|
||||||
, projection_matrix_(1.0f)
|
|
||||||
, view_dirty_(true)
|
|
||||||
, projection_dirty_(true)
|
|
||||||
, dirty_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_position(const Vec3& position) {
|
|
||||||
position_ = position;
|
|
||||||
view_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_target(const Vec3& target) {
|
|
||||||
target_ = target;
|
|
||||||
view_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_up(const Vec3& up) {
|
|
||||||
Real length = glm::length(up);
|
|
||||||
if (length < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("Camera: Invalid up vector (zero length), using default");
|
|
||||||
up_ = Vec3(0.0f, 1.0f, 0.0f);
|
|
||||||
} else {
|
|
||||||
up_ = up / length;
|
|
||||||
}
|
|
||||||
view_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::look_at(const Vec3& position, const Vec3& target, const Vec3& up) {
|
|
||||||
position_ = position;
|
|
||||||
target_ = target;
|
|
||||||
set_up(up);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_fov(Real fov_degrees) {
|
|
||||||
// Clamp FOV to reasonable range
|
|
||||||
fov_ = clamp(fov_degrees, 1.0f, 179.0f);
|
|
||||||
projection_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_aspect_ratio(Real aspect) {
|
|
||||||
if (aspect <= 0.0f) {
|
|
||||||
ARE_LOG_ERROR("Camera: Invalid aspect ratio (must be positive)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aspect_ratio_ = aspect;
|
|
||||||
projection_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_near_plane(Real near) {
|
|
||||||
if (near <= 0.0f) {
|
|
||||||
ARE_LOG_ERROR("Camera: Invalid near plane (must be positive)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
near_plane_ = near;
|
|
||||||
projection_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_far_plane(Real far) {
|
|
||||||
if (far <= near_plane_) {
|
|
||||||
ARE_LOG_ERROR("Camera: Invalid far plane (must be greater than near plane)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
far_plane_ = far;
|
|
||||||
projection_dirty_ = true;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::set_perspective(Real fov_degrees, Real aspect, Real near, Real far) {
|
|
||||||
set_fov(fov_degrees);
|
|
||||||
set_aspect_ratio(aspect);
|
|
||||||
set_near_plane(near);
|
|
||||||
set_far_plane(far);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Camera::get_forward() const {
|
|
||||||
return glm::normalize(target_ - position_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Camera::get_right() const {
|
|
||||||
return glm::normalize(glm::cross(get_forward(), up_));
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mat4& Camera::get_view_matrix() const {
|
|
||||||
if (view_dirty_) {
|
|
||||||
update_view_matrix();
|
|
||||||
}
|
|
||||||
return view_matrix_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mat4& Camera::get_projection_matrix() const {
|
|
||||||
if (projection_dirty_) {
|
|
||||||
update_projection_matrix();
|
|
||||||
}
|
|
||||||
return projection_matrix_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 Camera::get_view_projection_matrix() const {
|
|
||||||
return get_projection_matrix() * get_view_matrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Ray origin is camera position
|
|
||||||
origin = position_;
|
|
||||||
|
|
||||||
// Calculate ray direction in camera space
|
|
||||||
// u, v are in [0, 1], convert to [-1, 1]
|
|
||||||
Real x = 2.0f * u - 1.0f;
|
|
||||||
Real y = 1.0f - 2.0f * v; // Flip y for screen coordinates
|
|
||||||
|
|
||||||
// Calculate direction based on FOV and aspect ratio
|
|
||||||
Real tan_half_fov = std::tan(degrees_to_radians(fov_ * 0.5f));
|
|
||||||
Real viewport_height = 2.0f * tan_half_fov;
|
|
||||||
Real viewport_width = viewport_height * aspect_ratio_;
|
|
||||||
|
|
||||||
// Get camera basis vectors
|
|
||||||
Vec3 forward = get_forward();
|
|
||||||
Vec3 right = get_right();
|
|
||||||
Vec3 up = glm::normalize(glm::cross(right, forward));
|
|
||||||
|
|
||||||
// Calculate ray direction
|
|
||||||
direction = glm::normalize(
|
|
||||||
forward +
|
|
||||||
right * (x * viewport_width * 0.5f) +
|
|
||||||
up * (y * viewport_height * 0.5f)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::update_view_matrix() const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Check if camera is looking at itself
|
|
||||||
if (glm::length(target_ - position_) < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("Camera: Position and target are too close, using default view");
|
|
||||||
view_matrix_ = Mat4(1.0f);
|
|
||||||
} else {
|
|
||||||
view_matrix_ = glm::lookAt(position_, target_, up_);
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dirty_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::update_projection_matrix() const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
projection_matrix_ = glm::perspective(
|
|
||||||
degrees_to_radians(fov_),
|
|
||||||
aspect_ratio_,
|
|
||||||
near_plane_,
|
|
||||||
far_plane_
|
|
||||||
);
|
|
||||||
|
|
||||||
projection_dirty_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
/**
|
|
||||||
* @file directional_light.cpp
|
|
||||||
* @brief Implementation of DirectionalLight class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/directional_light.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
DirectionalLight::DirectionalLight()
|
|
||||||
: Light(LightType::ARE_LIGHT_DIRECTIONAL)
|
|
||||||
, direction_(0.0f, -1.0f, 0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectionalLight::DirectionalLight(const Vec3& direction, const Vec3& color, Real intensity)
|
|
||||||
: Light(LightType::ARE_LIGHT_DIRECTIONAL)
|
|
||||||
, direction_(glm::normalize(direction)) {
|
|
||||||
set_color(color);
|
|
||||||
set_intensity(intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectionalLight::set_direction(const Vec3& direction) {
|
|
||||||
Real length = glm::length(direction);
|
|
||||||
if (length < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("DirectionalLight: Invalid direction vector (zero length), using default");
|
|
||||||
direction_ = Vec3(0.0f, -1.0f, 0.0f);
|
|
||||||
} else {
|
|
||||||
direction_ = direction / length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LightData DirectionalLight::pack() const {
|
|
||||||
LightData data;
|
|
||||||
|
|
||||||
// position_type_: xyz unused for directional, w = light type
|
|
||||||
data.position_type_ = Vec4(0.0f, 0.0f, 0.0f,
|
|
||||||
static_cast<float>(LightType::ARE_LIGHT_DIRECTIONAL));
|
|
||||||
|
|
||||||
// direction_range_: xyz = direction, w = range (unused for directional)
|
|
||||||
data.direction_range_ = Vec4(direction_, 0.0f);
|
|
||||||
|
|
||||||
// color_intensity_: xyz = color, w = intensity
|
|
||||||
data.color_intensity_ = Vec4(color_, intensity_);
|
|
||||||
|
|
||||||
// params_: x = cast_shadows
|
|
||||||
data.params_ = Vec4(cast_shadows_ ? 1.0f : 0.0f, 0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DirectionalLight::affects_point(const Vec3& point) const {
|
|
||||||
// Directional light affects all points
|
|
||||||
(void)point; // Suppress unused parameter warning
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
/**
|
|
||||||
* @file light.cpp
|
|
||||||
* @brief Implementation of base Light class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/light.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Light::Light(LightType type)
|
|
||||||
: type_(type)
|
|
||||||
, color_(1.0f, 1.0f, 1.0f)
|
|
||||||
, intensity_(1.0f)
|
|
||||||
, cast_shadows_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Light::set_color(const Vec3& color) {
|
|
||||||
// Clamp color to valid range [0, 1]
|
|
||||||
color_.x = clamp(color.x, 0.0f, 1.0f);
|
|
||||||
color_.y = clamp(color.y, 0.0f, 1.0f);
|
|
||||||
color_.z = clamp(color.z, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Light::set_intensity(Real intensity) {
|
|
||||||
// Intensity can be HDR, so only clamp to non-negative
|
|
||||||
intensity_ = std::max(0.0f, intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Light::set_cast_shadows(bool cast) {
|
|
||||||
cast_shadows_ = cast;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/**
|
|
||||||
* @file material.cpp
|
|
||||||
* @brief Implementation of PBR Material class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/material.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Material::Material()
|
|
||||||
: albedo_(1.0f, 1.0f, 1.0f)
|
|
||||||
, metallic_(0.0f)
|
|
||||||
, roughness_(0.5f)
|
|
||||||
, emissive_(0.0f, 0.0f, 0.0f)
|
|
||||||
, albedo_tex_handle_(are_invalid_handle)
|
|
||||||
, metallic_tex_handle_(are_invalid_handle)
|
|
||||||
, roughness_tex_handle_(are_invalid_handle)
|
|
||||||
, normal_tex_handle_(are_invalid_handle)
|
|
||||||
, ao_tex_handle_(are_invalid_handle)
|
|
||||||
, emissive_tex_handle_(are_invalid_handle) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_albedo(const Vec3& albedo) {
|
|
||||||
// Clamp albedo to valid range [0, 1]
|
|
||||||
albedo_.x = clamp(albedo.x, 0.0f, 1.0f);
|
|
||||||
albedo_.y = clamp(albedo.y, 0.0f, 1.0f);
|
|
||||||
albedo_.z = clamp(albedo.z, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_albedo_map(const std::string& path) {
|
|
||||||
albedo_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_metallic(Real metallic) {
|
|
||||||
metallic_ = clamp(metallic, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_metallic_map(const std::string& path) {
|
|
||||||
metallic_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_roughness(Real roughness) {
|
|
||||||
// Clamp roughness to avoid division by zero in BRDF calculations
|
|
||||||
roughness_ = clamp(roughness, 0.04f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_roughness_map(const std::string& path) {
|
|
||||||
roughness_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_normal_map(const std::string& path) {
|
|
||||||
normal_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_ao_map(const std::string& path) {
|
|
||||||
ao_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_emissive(const Vec3& emissive) {
|
|
||||||
// Emissive can be HDR, so no upper clamp
|
|
||||||
emissive_.x = std::max(0.0f, emissive.x);
|
|
||||||
emissive_.y = std::max(0.0f, emissive.y);
|
|
||||||
emissive_.z = std::max(0.0f, emissive.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::set_emissive_map(const std::string& path) {
|
|
||||||
emissive_map_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Material::is_emissive() const {
|
|
||||||
// Check if material has significant emission
|
|
||||||
const Real threshold = 0.001f;
|
|
||||||
return (emissive_.x > threshold ||
|
|
||||||
emissive_.y > threshold ||
|
|
||||||
emissive_.z > threshold) ||
|
|
||||||
has_emissive_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mesh.cpp
|
|
||||||
* @brief Implementation of Mesh class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/mesh.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
Mesh::Mesh()
|
|
||||||
: material_id_(are_invalid_handle)
|
|
||||||
, vao_(0)
|
|
||||||
, vbo_(0)
|
|
||||||
, ebo_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh::Mesh(const std::vector<Vertex>& vertices,
|
|
||||||
const std::vector<uint32_t>& indices,
|
|
||||||
MaterialHandle material_id)
|
|
||||||
: vertices_(vertices)
|
|
||||||
, indices_(indices)
|
|
||||||
, material_id_(material_id)
|
|
||||||
, vao_(0)
|
|
||||||
, vbo_(0)
|
|
||||||
, ebo_(0) {
|
|
||||||
compute_aabb();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh::Mesh(const Vertex* vertices, size_t vertex_count,
|
|
||||||
const uint32_t* indices, size_t index_count,
|
|
||||||
MaterialHandle material_id)
|
|
||||||
: material_id_(material_id)
|
|
||||||
, vao_(0)
|
|
||||||
, vbo_(0)
|
|
||||||
, ebo_(0) {
|
|
||||||
if (vertices && vertex_count > 0) {
|
|
||||||
vertices_.assign(vertices, vertices + vertex_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indices && index_count > 0) {
|
|
||||||
indices_.assign(indices, indices + index_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_aabb();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::set_vertices(const std::vector<Vertex>& vertices) {
|
|
||||||
vertices_ = vertices;
|
|
||||||
compute_aabb();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::set_indices(const std::vector<uint32_t>& indices) {
|
|
||||||
indices_ = indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::set_material(MaterialHandle material_id) {
|
|
||||||
material_id_ = material_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::compute_aabb() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (vertices_.empty()) {
|
|
||||||
aabb_ = AABB::invalid();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aabb_ = AABB(vertices_[0].position_);
|
|
||||||
|
|
||||||
for (size_t i = 1; i < vertices_.size(); ++i) {
|
|
||||||
aabb_.expand(vertices_[i].position_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::compute_tangents() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (vertices_.empty() || indices_.empty()) {
|
|
||||||
ARE_LOG_WARN("Mesh: Cannot compute tangents for empty mesh");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indices_.size() % 3 != 0) {
|
|
||||||
ARE_LOG_ERROR("Mesh: Index count is not a multiple of 3");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize tangents to zero
|
|
||||||
std::vector<Vec3> tangents(vertices_.size(), Vec3(0.0f));
|
|
||||||
std::vector<Vec3> bitangents(vertices_.size(), Vec3(0.0f));
|
|
||||||
|
|
||||||
// Calculate tangents for each triangle
|
|
||||||
for (size_t i = 0; i < indices_.size(); i += 3) {
|
|
||||||
uint32_t i0 = indices_[i];
|
|
||||||
uint32_t i1 = indices_[i + 1];
|
|
||||||
uint32_t i2 = indices_[i + 2];
|
|
||||||
|
|
||||||
if (i0 >= vertices_.size() || i1 >= vertices_.size() || i2 >= vertices_.size()) {
|
|
||||||
ARE_LOG_ERROR("Mesh: Invalid index in compute_tangents");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vertex& v0 = vertices_[i0];
|
|
||||||
const Vertex& v1 = vertices_[i1];
|
|
||||||
const Vertex& v2 = vertices_[i2];
|
|
||||||
|
|
||||||
// Calculate edges
|
|
||||||
Vec3 edge1 = v1.position_ - v0.position_;
|
|
||||||
Vec3 edge2 = v2.position_ - v0.position_;
|
|
||||||
|
|
||||||
Vec2 delta_uv1 = v1.texcoord_ - v0.texcoord_;
|
|
||||||
Vec2 delta_uv2 = v2.texcoord_ - v0.texcoord_;
|
|
||||||
|
|
||||||
// Calculate tangent and bitangent
|
|
||||||
Real f = delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y;
|
|
||||||
|
|
||||||
if (std::abs(f) < are_epsilon) {
|
|
||||||
// Degenerate UV coordinates, use arbitrary tangent
|
|
||||||
Vec3 tangent, bitangent;
|
|
||||||
create_orthonormal_basis(v0.normal_, tangent, bitangent);
|
|
||||||
|
|
||||||
tangents[i0] += tangent;
|
|
||||||
tangents[i1] += tangent;
|
|
||||||
tangents[i2] += tangent;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
f = 1.0f / f;
|
|
||||||
|
|
||||||
Vec3 tangent;
|
|
||||||
tangent.x = f * (delta_uv2.y * edge1.x - delta_uv1.y * edge2.x);
|
|
||||||
tangent.y = f * (delta_uv2.y * edge1.y - delta_uv1.y * edge2.y);
|
|
||||||
tangent.z = f * (delta_uv2.y * edge1.z - delta_uv1.y * edge2.z);
|
|
||||||
|
|
||||||
Vec3 bitangent;
|
|
||||||
bitangent.x = f * (-delta_uv2.x * edge1.x + delta_uv1.x * edge2.x);
|
|
||||||
bitangent.y = f * (-delta_uv2.x * edge1.y + delta_uv1.x * edge2.y);
|
|
||||||
bitangent.z = f * (-delta_uv2.x * edge1.z + delta_uv1.x * edge2.z);
|
|
||||||
|
|
||||||
// Accumulate tangents for each vertex
|
|
||||||
tangents[i0] += tangent;
|
|
||||||
tangents[i1] += tangent;
|
|
||||||
tangents[i2] += tangent;
|
|
||||||
|
|
||||||
bitangents[i0] += bitangent;
|
|
||||||
bitangents[i1] += bitangent;
|
|
||||||
bitangents[i2] += bitangent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Orthogonalize and normalize tangents (Gram-Schmidt)
|
|
||||||
for (size_t i = 0; i < vertices_.size(); ++i) {
|
|
||||||
const Vec3& n = vertices_[i].normal_;
|
|
||||||
const Vec3& t = tangents[i];
|
|
||||||
|
|
||||||
// Gram-Schmidt orthogonalize
|
|
||||||
Vec3 tangent = t - n * glm::dot(n, t);
|
|
||||||
|
|
||||||
Real length = glm::length(tangent);
|
|
||||||
if (length < are_epsilon) {
|
|
||||||
// If tangent is parallel to normal, create arbitrary tangent
|
|
||||||
create_orthonormal_basis(n, tangent, bitangents[i]);
|
|
||||||
} else {
|
|
||||||
tangent /= length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check handedness
|
|
||||||
Real handedness = glm::dot(glm::cross(n, t), bitangents[i]);
|
|
||||||
if (handedness < 0.0f) {
|
|
||||||
tangent = -tangent;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices_[i].tangent_ = tangent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Mesh::get_triangle(size_t triangle_index, Vertex& v0, Vertex& v1, Vertex& v2) const {
|
|
||||||
if (triangle_index >= get_triangle_count()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t base_index = triangle_index * 3;
|
|
||||||
uint32_t i0 = indices_[base_index];
|
|
||||||
uint32_t i1 = indices_[base_index + 1];
|
|
||||||
uint32_t i2 = indices_[base_index + 2];
|
|
||||||
|
|
||||||
if (i0 >= vertices_.size() || i1 >= vertices_.size() || i2 >= vertices_.size()) {
|
|
||||||
ARE_LOG_ERROR("Mesh: Invalid indices in get_triangle");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
v0 = vertices_[i0];
|
|
||||||
v1 = vertices_[i1];
|
|
||||||
v2 = vertices_[i2];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
/**
|
|
||||||
* @file point_light.cpp
|
|
||||||
* @brief Implementation of PointLight class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/point_light.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
PointLight::PointLight()
|
|
||||||
: Light(LightType::ARE_LIGHT_POINT)
|
|
||||||
, position_(0.0f)
|
|
||||||
, range_(10.0f)
|
|
||||||
, attenuation_constant_(1.0f)
|
|
||||||
, attenuation_linear_(0.09f)
|
|
||||||
, attenuation_quadratic_(0.032f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PointLight::PointLight(const Vec3& position, const Vec3& color, Real intensity, Real range)
|
|
||||||
: Light(LightType::ARE_LIGHT_POINT)
|
|
||||||
, position_(position)
|
|
||||||
, range_(range)
|
|
||||||
, attenuation_constant_(1.0f)
|
|
||||||
, attenuation_linear_(0.09f)
|
|
||||||
, attenuation_quadratic_(0.032f) {
|
|
||||||
set_color(color);
|
|
||||||
set_intensity(intensity);
|
|
||||||
set_range(range);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PointLight::set_position(const Vec3& position) {
|
|
||||||
position_ = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PointLight::set_range(Real range) {
|
|
||||||
if (range <= 0.0f) {
|
|
||||||
ARE_LOG_WARN("PointLight: Invalid range (must be positive), using default");
|
|
||||||
range_ = 10.0f;
|
|
||||||
} else {
|
|
||||||
range_ = range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PointLight::set_attenuation(Real constant, Real linear, Real quadratic) {
|
|
||||||
attenuation_constant_ = std::max(0.0f, constant);
|
|
||||||
attenuation_linear_ = std::max(0.0f, linear);
|
|
||||||
attenuation_quadratic_ = std::max(0.0f, quadratic);
|
|
||||||
|
|
||||||
// Ensure at least some attenuation to avoid division issues
|
|
||||||
if (attenuation_constant_ < are_epsilon &&
|
|
||||||
attenuation_linear_ < are_epsilon &&
|
|
||||||
attenuation_quadratic_ < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("PointLight: All attenuation factors near zero, setting constant to 1.0");
|
|
||||||
attenuation_constant_ = 1.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Real PointLight::calculate_attenuation(Real distance) const {
|
|
||||||
// Standard attenuation formula: 1 / (constant + linear*d + quadratic*d^2)
|
|
||||||
Real attenuation = attenuation_constant_ +
|
|
||||||
attenuation_linear_ * distance +
|
|
||||||
attenuation_quadratic_ * distance * distance;
|
|
||||||
return 1.0f / std::max(attenuation, are_epsilon);
|
|
||||||
}
|
|
||||||
|
|
||||||
LightData PointLight::pack() const {
|
|
||||||
LightData data;
|
|
||||||
|
|
||||||
// position_type_: xyz = position, w = light type
|
|
||||||
data.position_type_ = Vec4(position_,
|
|
||||||
static_cast<float>(LightType::ARE_LIGHT_POINT));
|
|
||||||
|
|
||||||
// direction_range_: xyz unused for point light, w = range
|
|
||||||
data.direction_range_ = Vec4(0.0f, 0.0f, 0.0f, range_);
|
|
||||||
|
|
||||||
// color_intensity_: xyz = color, w = intensity
|
|
||||||
data.color_intensity_ = Vec4(color_, intensity_);
|
|
||||||
|
|
||||||
// params_: x = cast_shadows, y = constant, z = linear, w = quadratic
|
|
||||||
data.params_ = Vec4(
|
|
||||||
cast_shadows_ ? 1.0f : 0.0f,
|
|
||||||
attenuation_constant_,
|
|
||||||
attenuation_linear_,
|
|
||||||
attenuation_quadratic_
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PointLight::affects_point(const Vec3& point) const {
|
|
||||||
Real distance = glm::length(point - position_);
|
|
||||||
return distance <= range_;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
||||||
/**
|
|
||||||
* @file scene_manager.cpp
|
|
||||||
* @brief Implementation of SceneManager class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/core/profiler.h>
|
|
||||||
#include <are/scene/scene_manager.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
SceneManager::SceneManager()
|
|
||||||
: next_mesh_handle_(1)
|
|
||||||
, next_material_handle_(1)
|
|
||||||
, next_light_handle_(1)
|
|
||||||
, dirty_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneManager::~SceneManager() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
MeshHandle SceneManager::add_mesh(const Mesh &mesh) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (mesh.is_empty()) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to add empty mesh");
|
|
||||||
return are_invalid_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
MeshHandle handle = next_mesh_handle_++;
|
|
||||||
meshes_.push_back(mesh);
|
|
||||||
mesh_handle_map_[handle] = meshes_.size() - 1;
|
|
||||||
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Added mesh with handle " + std::to_string(handle));
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::remove_mesh(MeshHandle handle) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
auto it = mesh_handle_map_.find(handle);
|
|
||||||
if (it == mesh_handle_map_.end()) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to remove invalid mesh handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
|
|
||||||
// Swap with last element and pop
|
|
||||||
if (index < meshes_.size() - 1) {
|
|
||||||
meshes_[index] = meshes_.back();
|
|
||||||
|
|
||||||
// Update handle map for swapped element
|
|
||||||
for (auto &pair : mesh_handle_map_) {
|
|
||||||
if (pair.second == meshes_.size() - 1) {
|
|
||||||
pair.second = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meshes_.pop_back();
|
|
||||||
mesh_handle_map_.erase(it);
|
|
||||||
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Removed mesh with handle " + std::to_string(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::update_mesh(MeshHandle handle, const Mesh &mesh) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
Mesh *existing = get_mesh(handle);
|
|
||||||
if (!existing) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to update invalid mesh handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*existing = mesh;
|
|
||||||
dirty_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh *SceneManager::get_mesh(MeshHandle handle) {
|
|
||||||
auto it = mesh_handle_map_.find(handle);
|
|
||||||
if (it == mesh_handle_map_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
if (index >= meshes_.size()) {
|
|
||||||
ARE_LOG_ERROR("SceneManager: Mesh handle map corrupted");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &meshes_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mesh *SceneManager::get_mesh(MeshHandle handle) const {
|
|
||||||
auto it = mesh_handle_map_.find(handle);
|
|
||||||
if (it == mesh_handle_map_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
if (index >= meshes_.size()) {
|
|
||||||
ARE_LOG_ERROR("SceneManager: Mesh handle map corrupted");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &meshes_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialHandle SceneManager::add_material(const Material &material) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
MaterialHandle handle = next_material_handle_++;
|
|
||||||
materials_.push_back(material);
|
|
||||||
material_handle_map_[handle] = materials_.size() - 1;
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Added material with handle " + std::to_string(handle));
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::remove_material(MaterialHandle handle) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
auto it = material_handle_map_.find(handle);
|
|
||||||
if (it == material_handle_map_.end()) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to remove invalid material handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
|
|
||||||
// Swap with last element and pop
|
|
||||||
if (index < materials_.size() - 1) {
|
|
||||||
materials_[index] = materials_.back();
|
|
||||||
|
|
||||||
// Update handle map for swapped element
|
|
||||||
for (auto &pair : material_handle_map_) {
|
|
||||||
if (pair.second == materials_.size() - 1) {
|
|
||||||
pair.second = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
materials_.pop_back();
|
|
||||||
material_handle_map_.erase(it);
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Removed material with handle " + std::to_string(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::update_material(MaterialHandle handle, const Material &material) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
Material *existing = get_material(handle);
|
|
||||||
if (!existing) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to update invalid material handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*existing = material;
|
|
||||||
}
|
|
||||||
|
|
||||||
Material *SceneManager::get_material(MaterialHandle handle) {
|
|
||||||
auto it = material_handle_map_.find(handle);
|
|
||||||
if (it == material_handle_map_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
if (index >= materials_.size()) {
|
|
||||||
ARE_LOG_ERROR("SceneManager: Material handle map corrupted");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &materials_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Material *SceneManager::get_material(MaterialHandle handle) const {
|
|
||||||
auto it = material_handle_map_.find(handle);
|
|
||||||
if (it == material_handle_map_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
if (index >= materials_.size()) {
|
|
||||||
ARE_LOG_ERROR("SceneManager: Material handle map corrupted");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &materials_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
LightHandle SceneManager::add_light(const std::shared_ptr<Light> &light) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
if (!light) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to add null light");
|
|
||||||
return are_invalid_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
LightHandle handle = next_light_handle_++;
|
|
||||||
lights_.push_back(light);
|
|
||||||
light_handle_map_[handle] = lights_.size() - 1;
|
|
||||||
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Added light with handle " + std::to_string(handle));
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::remove_light(LightHandle handle) {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
auto it = light_handle_map_.find(handle);
|
|
||||||
if (it == light_handle_map_.end()) {
|
|
||||||
ARE_LOG_WARN("SceneManager: Attempting to remove invalid light handle");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
|
|
||||||
// Swap with last element and pop
|
|
||||||
if (index < lights_.size() - 1) {
|
|
||||||
lights_[index] = lights_.back();
|
|
||||||
|
|
||||||
// Update handle map for swapped element
|
|
||||||
for (auto &pair : light_handle_map_) {
|
|
||||||
if (pair.second == lights_.size() - 1) {
|
|
||||||
pair.second = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lights_.pop_back();
|
|
||||||
light_handle_map_.erase(it);
|
|
||||||
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Removed light with handle " + std::to_string(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Light> SceneManager::get_light(LightHandle handle) {
|
|
||||||
auto it = light_handle_map_.find(handle);
|
|
||||||
if (it == light_handle_map_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = it->second;
|
|
||||||
if (index >= lights_.size()) {
|
|
||||||
ARE_LOG_ERROR("SceneManager: Light handle map corrupted");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lights_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SceneManager::get_total_triangle_count() const {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
size_t total = 0;
|
|
||||||
for (const auto &mesh : meshes_) {
|
|
||||||
total += mesh.get_triangle_count();
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::clear() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
meshes_.clear();
|
|
||||||
materials_.clear();
|
|
||||||
lights_.clear();
|
|
||||||
|
|
||||||
mesh_handle_map_.clear();
|
|
||||||
material_handle_map_.clear();
|
|
||||||
light_handle_map_.clear();
|
|
||||||
|
|
||||||
next_mesh_handle_ = 1;
|
|
||||||
next_material_handle_ = 1;
|
|
||||||
next_light_handle_ = 1;
|
|
||||||
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
ARE_LOG_INFO("SceneManager: Cleared all scene data");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneManager::compact() {
|
|
||||||
ARE_PROFILE_FUNCTION();
|
|
||||||
|
|
||||||
// Remove invalid entries (this is a placeholder for future optimization)
|
|
||||||
// Currently, the handle-based system ensures no invalid entries exist
|
|
||||||
|
|
||||||
ARE_LOG_DEBUG("SceneManager: Compacted scene data");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
/**
|
|
||||||
* @file spot_light.cpp
|
|
||||||
* @brief Implementation of SpotLight class
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/scene/spot_light.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <are/utils/math_utils.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
SpotLight::SpotLight()
|
|
||||||
: Light(LightType::ARE_LIGHT_SPOT)
|
|
||||||
, position_(0.0f)
|
|
||||||
, direction_(0.0f, -1.0f, 0.0f)
|
|
||||||
, inner_angle_(30.0f)
|
|
||||||
, outer_angle_(45.0f)
|
|
||||||
, range_(10.0f)
|
|
||||||
, cos_inner_(std::cos(degrees_to_radians(30.0f)))
|
|
||||||
, cos_outer_(std::cos(degrees_to_radians(45.0f))) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SpotLight::SpotLight(const Vec3& position, const Vec3& direction,
|
|
||||||
Real inner_angle, Real outer_angle,
|
|
||||||
const Vec3& color, Real intensity)
|
|
||||||
: Light(LightType::ARE_LIGHT_SPOT)
|
|
||||||
, position_(position)
|
|
||||||
, range_(10.0f) {
|
|
||||||
set_direction(direction);
|
|
||||||
set_inner_angle(inner_angle);
|
|
||||||
set_outer_angle(outer_angle);
|
|
||||||
set_color(color);
|
|
||||||
set_intensity(intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpotLight::set_position(const Vec3& position) {
|
|
||||||
position_ = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpotLight::set_direction(const Vec3& direction) {
|
|
||||||
Real length = glm::length(direction);
|
|
||||||
if (length < are_epsilon) {
|
|
||||||
ARE_LOG_WARN("SpotLight: Invalid direction vector (zero length), using default");
|
|
||||||
direction_ = Vec3(0.0f, -1.0f, 0.0f);
|
|
||||||
} else {
|
|
||||||
direction_ = direction / length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpotLight::set_inner_angle(Real angle) {
|
|
||||||
// Clamp to valid range [0, 90] degrees
|
|
||||||
inner_angle_ = clamp(angle, 0.0f, 90.0f);
|
|
||||||
cos_inner_ = std::cos(degrees_to_radians(inner_angle_));
|
|
||||||
|
|
||||||
// Ensure inner angle is not larger than outer angle
|
|
||||||
if (inner_angle_ > outer_angle_) {
|
|
||||||
ARE_LOG_WARN("SpotLight: Inner angle larger than outer angle, adjusting outer angle");
|
|
||||||
outer_angle_ = inner_angle_;
|
|
||||||
cos_outer_ = cos_inner_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpotLight::set_outer_angle(Real angle) {
|
|
||||||
// Clamp to valid range [0, 90] degrees
|
|
||||||
outer_angle_ = clamp(angle, 0.0f, 90.0f);
|
|
||||||
cos_outer_ = std::cos(degrees_to_radians(outer_angle_));
|
|
||||||
|
|
||||||
// Ensure outer angle is not smaller than inner angle
|
|
||||||
if (outer_angle_ < inner_angle_) {
|
|
||||||
ARE_LOG_WARN("SpotLight: Outer angle smaller than inner angle, adjusting inner angle");
|
|
||||||
inner_angle_ = outer_angle_;
|
|
||||||
cos_inner_ = cos_outer_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpotLight::set_range(Real range) {
|
|
||||||
if (range <= 0.0f) {
|
|
||||||
ARE_LOG_WARN("SpotLight: Invalid range (must be positive), using default");
|
|
||||||
range_ = 10.0f;
|
|
||||||
} else {
|
|
||||||
range_ = range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Real SpotLight::calculate_spot_factor(const Vec3& to_point) const {
|
|
||||||
// Calculate angle between light direction and direction to point
|
|
||||||
Real cos_angle = glm::dot(direction_, glm::normalize(to_point));
|
|
||||||
|
|
||||||
// Outside outer cone
|
|
||||||
if (cos_angle < cos_outer_) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inside inner cone
|
|
||||||
if (cos_angle > cos_inner_) {
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smooth transition between inner and outer cone
|
|
||||||
Real delta = cos_inner_ - cos_outer_;
|
|
||||||
if (delta < are_epsilon) {
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (cos_angle - cos_outer_) / delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
LightData SpotLight::pack() const {
|
|
||||||
LightData data;
|
|
||||||
|
|
||||||
// position_type_: xyz = position, w = light type
|
|
||||||
data.position_type_ = Vec4(position_,
|
|
||||||
static_cast<float>(LightType::ARE_LIGHT_SPOT));
|
|
||||||
|
|
||||||
// direction_range_: xyz = direction, w = range
|
|
||||||
data.direction_range_ = Vec4(direction_, range_);
|
|
||||||
|
|
||||||
// color_intensity_: xyz = color, w = intensity
|
|
||||||
data.color_intensity_ = Vec4(color_, intensity_);
|
|
||||||
|
|
||||||
// params_: x = cast_shadows, y = cos_inner, z = cos_outer, w unused
|
|
||||||
data.params_ = Vec4(
|
|
||||||
cast_shadows_ ? 1.0f : 0.0f,
|
|
||||||
cos_inner_,
|
|
||||||
cos_outer_,
|
|
||||||
0.0f
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpotLight::affects_point(const Vec3& point) const {
|
|
||||||
// Check if point is within range
|
|
||||||
Vec3 to_point = point - position_;
|
|
||||||
Real distance = glm::length(to_point);
|
|
||||||
|
|
||||||
if (distance > range_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if point is within spotlight cone
|
|
||||||
if (distance > are_epsilon) {
|
|
||||||
to_point /= distance;
|
|
||||||
Real cos_angle = glm::dot(direction_, to_point);
|
|
||||||
return cos_angle >= cos_outer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
/**
|
|
||||||
* @file file_utils.cpp
|
|
||||||
* @brief Implementation of file system utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/utils/file_utils.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <direct.h>
|
|
||||||
#define MKDIR(path) _mkdir(path)
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
|
||||||
#define MKDIR(path) mkdir(path, 0755)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
std::string read_file_to_string(const std::string& filepath) {
|
|
||||||
std::ifstream file(filepath, std::ios::in | std::ios::binary);
|
|
||||||
|
|
||||||
if (!file.is_open()) {
|
|
||||||
ARE_LOG_ERROR("Failed to open file: " + filepath);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << file.rdbuf();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return buffer.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> read_file_to_bytes(const std::string& filepath) {
|
|
||||||
std::ifstream file(filepath, std::ios::in | std::ios::binary);
|
|
||||||
|
|
||||||
if (!file.is_open()) {
|
|
||||||
ARE_LOG_ERROR("Failed to open file: " + filepath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get file size
|
|
||||||
file.seekg(0, std::ios::end);
|
|
||||||
size_t size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
// Read data
|
|
||||||
std::vector<uint8_t> data(size);
|
|
||||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_string_to_file(const std::string& filepath, const std::string& content) {
|
|
||||||
std::ofstream file(filepath, std::ios::out | std::ios::binary);
|
|
||||||
|
|
||||||
if (!file.is_open()) {
|
|
||||||
ARE_LOG_ERROR("Failed to open file for writing: " + filepath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
file << content;
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_bytes_to_file(const std::string& filepath, const void* data, size_t size) {
|
|
||||||
std::ofstream file(filepath, std::ios::out | std::ios::binary);
|
|
||||||
|
|
||||||
if (!file.is_open()) {
|
|
||||||
ARE_LOG_ERROR("Failed to open file for writing: " + filepath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(static_cast<const char*>(data), size);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool file_exists(const std::string& filepath) {
|
|
||||||
struct stat buffer;
|
|
||||||
return (stat(filepath.c_str(), &buffer) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_directory(const std::string& path) {
|
|
||||||
struct stat buffer;
|
|
||||||
if (stat(path.c_str(), &buffer) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (buffer.st_mode & S_IFDIR) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool create_directory(const std::string& path) {
|
|
||||||
if (path.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_directory(path)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create parent directories recursively
|
|
||||||
size_t pos = 0;
|
|
||||||
std::string current_path;
|
|
||||||
|
|
||||||
while ((pos = path.find_first_of("/\\", pos)) != std::string::npos) {
|
|
||||||
current_path = path.substr(0, pos++);
|
|
||||||
|
|
||||||
if (!current_path.empty() && !is_directory(current_path)) {
|
|
||||||
if (MKDIR(current_path.c_str()) != 0 && !is_directory(current_path)) {
|
|
||||||
ARE_LOG_ERROR("Failed to create directory: " + current_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create final directory
|
|
||||||
if (MKDIR(path.c_str()) != 0 && !is_directory(path)) {
|
|
||||||
ARE_LOG_ERROR("Failed to create directory: " + path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_file_extension(const std::string& filepath) {
|
|
||||||
size_t dot_pos = filepath.find_last_of('.');
|
|
||||||
|
|
||||||
if (dot_pos == std::string::npos || dot_pos == filepath.length() - 1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ext = filepath.substr(dot_pos + 1);
|
|
||||||
|
|
||||||
// Convert to lowercase
|
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(),
|
|
||||||
[](unsigned char c) { return std::tolower(c); });
|
|
||||||
|
|
||||||
return ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_filename(const std::string& filepath) {
|
|
||||||
size_t slash_pos = filepath.find_last_of("/\\");
|
|
||||||
|
|
||||||
if (slash_pos == std::string::npos) {
|
|
||||||
return filepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.substr(slash_pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_directory(const std::string& filepath) {
|
|
||||||
size_t slash_pos = filepath.find_last_of("/\\");
|
|
||||||
|
|
||||||
if (slash_pos == std::string::npos) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.substr(0, slash_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string join_path(const std::vector<std::string>& parts) {
|
|
||||||
if (parts.empty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string result = parts[0];
|
|
||||||
|
|
||||||
for (size_t i = 1; i < parts.size(); ++i) {
|
|
||||||
if (!result.empty() && result.back() != '/' && result.back() != '\\') {
|
|
||||||
result += '/';
|
|
||||||
}
|
|
||||||
result += parts[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string normalize_path(const std::string& path) {
|
|
||||||
if (path.empty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> components;
|
|
||||||
std::string current;
|
|
||||||
|
|
||||||
for (char c : path) {
|
|
||||||
if (c == '/' || c == '\\') {
|
|
||||||
if (!current.empty()) {
|
|
||||||
if (current == "..") {
|
|
||||||
if (!components.empty() && components.back() != "..") {
|
|
||||||
components.pop_back();
|
|
||||||
} else {
|
|
||||||
components.push_back(current);
|
|
||||||
}
|
|
||||||
} else if (current != ".") {
|
|
||||||
components.push_back(current);
|
|
||||||
}
|
|
||||||
current.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
current += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current.empty()) {
|
|
||||||
if (current == "..") {
|
|
||||||
if (!components.empty() && components.back() != "..") {
|
|
||||||
components.pop_back();
|
|
||||||
} else {
|
|
||||||
components.push_back(current);
|
|
||||||
}
|
|
||||||
} else if (current != ".") {
|
|
||||||
components.push_back(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return join_path(components);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
/**
|
|
||||||
* @file image_io.cpp
|
|
||||||
* @brief Implementation of image I/O utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <are/utils/image_io.h>
|
|
||||||
#include <are/core/logger.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb_image.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include <stb_image_write.h>
|
|
||||||
|
|
||||||
namespace are {
|
|
||||||
|
|
||||||
bool ImageData::is_valid() const {
|
|
||||||
return width_ > 0 && height_ > 0 && channels_ > 0 && !data_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* ImageData::get_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = (y * width_ + x) * channels_;
|
|
||||||
return &data_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageData::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
|
||||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = (y * width_ + x) * channels_;
|
|
||||||
|
|
||||||
data_[index + 0] = r;
|
|
||||||
data_[index + 1] = g;
|
|
||||||
data_[index + 2] = b;
|
|
||||||
|
|
||||||
if (channels_ == 4) {
|
|
||||||
data_[index + 3] = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageData load_image(const std::string& filename, bool flip_vertically) {
|
|
||||||
ImageData image;
|
|
||||||
|
|
||||||
// Set flip flag
|
|
||||||
stbi_set_flip_vertically_on_load(flip_vertically ? 1 : 0);
|
|
||||||
|
|
||||||
// Load image
|
|
||||||
int width, height, channels;
|
|
||||||
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, 0);
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
ARE_LOG_ERROR("Failed to load image: " + filename + " - " + stbi_failure_reason());
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy data to ImageData
|
|
||||||
image.width_ = width;
|
|
||||||
image.height_ = height;
|
|
||||||
image.channels_ = channels;
|
|
||||||
|
|
||||||
size_t data_size = width * height * channels;
|
|
||||||
image.data_.resize(data_size);
|
|
||||||
std::memcpy(image.data_.data(), data, data_size);
|
|
||||||
|
|
||||||
// Free stb_image data
|
|
||||||
stbi_image_free(data);
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Loaded image: " + filename + " (" +
|
|
||||||
std::to_string(width) + "x" + std::to_string(height) +
|
|
||||||
", " + std::to_string(channels) + " channels)");
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageFormat detect_format(const std::string& filename) {
|
|
||||||
std::string ext;
|
|
||||||
size_t dot_pos = filename.find_last_of('.');
|
|
||||||
|
|
||||||
if (dot_pos != std::string::npos) {
|
|
||||||
ext = filename.substr(dot_pos + 1);
|
|
||||||
|
|
||||||
// Convert to lowercase
|
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(),
|
|
||||||
[](unsigned char c) { return std::tolower(c); });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ext == "ppm") return ImageFormat::ARE_IMAGE_FORMAT_PPM;
|
|
||||||
if (ext == "bmp") return ImageFormat::ARE_IMAGE_FORMAT_BMP;
|
|
||||||
if (ext == "png") return ImageFormat::ARE_IMAGE_FORMAT_PNG;
|
|
||||||
if (ext == "jpg" || ext == "jpeg") return ImageFormat::ARE_IMAGE_FORMAT_JPG;
|
|
||||||
|
|
||||||
// Default to PNG
|
|
||||||
return ImageFormat::ARE_IMAGE_FORMAT_PNG;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool save_image(const std::string& filename, const ImageData& data, ImageFormat format) {
|
|
||||||
if (!data.is_valid()) {
|
|
||||||
ARE_LOG_ERROR("Cannot save invalid image data");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-detect format if not specified
|
|
||||||
if (format == ImageFormat::ARE_IMAGE_FORMAT_PNG) {
|
|
||||||
format = detect_format(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case ImageFormat::ARE_IMAGE_FORMAT_PPM: {
|
|
||||||
// Write PPM manually (stb doesn't support it)
|
|
||||||
std::ofstream file(filename, std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
ARE_LOG_ERROR("Failed to open file for writing: " + filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
file << "P6\n" << data.width_ << " " << data.height_ << "\n255\n";
|
|
||||||
|
|
||||||
for (int i = 0; i < data.width_ * data.height_; ++i) {
|
|
||||||
int idx = i * data.channels_;
|
|
||||||
file.put(data.data_[idx + 0]); // R
|
|
||||||
file.put(data.data_[idx + 1]); // G
|
|
||||||
file.put(data.data_[idx + 2]); // B
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
result = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ImageFormat::ARE_IMAGE_FORMAT_BMP:
|
|
||||||
result = stbi_write_bmp(filename.c_str(), data.width_, data.height_,
|
|
||||||
data.channels_, data.data_.data());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ImageFormat::ARE_IMAGE_FORMAT_PNG:
|
|
||||||
result = stbi_write_png(filename.c_str(), data.width_, data.height_,
|
|
||||||
data.channels_, data.data_.data(),
|
|
||||||
data.width_ * data.channels_);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ImageFormat::ARE_IMAGE_FORMAT_JPG:
|
|
||||||
result = stbi_write_jpg(filename.c_str(), data.width_, data.height_,
|
|
||||||
data.channels_, data.data_.data(), 90);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == 0) {
|
|
||||||
ARE_LOG_ERROR("Failed to save image: " + filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARE_LOG_INFO("Saved image: " + filename);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool save_image(const std::string& filename, const uint8_t* pixels,
|
|
||||||
int width, int height, int channels, ImageFormat format) {
|
|
||||||
ImageData data;
|
|
||||||
data.width_ = width;
|
|
||||||
data.height_ = height;
|
|
||||||
data.channels_ = channels;
|
|
||||||
|
|
||||||
size_t data_size = width * height * channels;
|
|
||||||
data.data_.resize(data_size);
|
|
||||||
std::memcpy(data.data_.data(), pixels, data_size);
|
|
||||||
|
|
||||||
return save_image(filename, data, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace are
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue