aurora-rendering-engine/examples/03_phase4_test/main.cpp

411 lines
10 KiB
C++

/**
* @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;
}