/** * @file main.cpp * @brief Phase 3 verification program - G-Buffer rendering test */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../lib/glad/glad/glad.h" #include using namespace are; /** * @brief Create a simple cube mesh */ Mesh create_cube_mesh() { std::vector vertices = { // Front face Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 0)), Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 0)), Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(1, 1)), Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 0, 1), Vec2(0, 1)), // Back face Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 0)), Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 0)), Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(1, 1)), Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 0, -1), Vec2(0, 1)), // Top face Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(0, 0)), Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(0, 1, 0), Vec2(1, 0)), Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(1, 1)), Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(0, 1, 0), Vec2(0, 1)), // Bottom face Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(0, 0)), Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(0, -1, 0), Vec2(1, 0)), Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(1, 1)), Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(0, -1, 0), Vec2(0, 1)), // Right face Vertex(Vec3(0.5f, -0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 0)), Vertex(Vec3(0.5f, -0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 0)), Vertex(Vec3(0.5f, 0.5f, -0.5f), Vec3(1, 0, 0), Vec2(1, 1)), Vertex(Vec3(0.5f, 0.5f, 0.5f), Vec3(1, 0, 0), Vec2(0, 1)), // Left face Vertex(Vec3(-0.5f, -0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 0)), Vertex(Vec3(-0.5f, -0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 0)), Vertex(Vec3(-0.5f, 0.5f, 0.5f), Vec3(-1, 0, 0), Vec2(1, 1)), Vertex(Vec3(-0.5f, 0.5f, -0.5f), Vec3(-1, 0, 0), Vec2(0, 1)) }; std::vector indices = { // Front 0, 1, 2, 2, 3, 0, // Back 4, 5, 6, 6, 7, 4, // Top 8, 9, 10, 10, 11, 8, // Bottom 12, 13, 14, 14, 15, 12, // Right 16, 17, 18, 18, 19, 16, // Left 20, 21, 22, 22, 23, 20 }; return Mesh(vertices, indices); } /** * @brief Create a simple triangle mesh (positioned in front of cube) */ Mesh create_triangle_mesh() { std::vector vertices = { Vertex(Vec3(-0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0, 0)), // Z = 1.0 Vertex(Vec3( 0.5f, -0.5f, 1.0f), Vec3(0, 0, 1), Vec2(1, 0)), // Z = 1.0 Vertex(Vec3( 0.0f, 0.5f, 1.0f), Vec3(0, 0, 1), Vec2(0.5f, 1)) // Z = 1.0 }; std::vector indices = {0, 1, 2}; return Mesh(vertices, indices); } /** * @brief Fullscreen quad shader for G-Buffer visualization */ const char *fullscreen_vert_source = R"( #version 430 core layout(location = 0) in vec2 a_position; layout(location = 1) in vec2 a_texcoord; out vec2 v_texcoord; void main() { v_texcoord = a_texcoord; gl_Position = vec4(a_position, 0.0, 1.0); } )"; const char *visualize_frag_source = R"( #version 430 core in vec2 v_texcoord; out vec4 frag_color; uniform sampler2D u_texture; uniform int u_mode; // 0=position, 1=normal, 2=albedo, 3=material void main() { vec4 value = texture(u_texture, v_texcoord); if (u_mode == 0) { // Position: normalize to [0,1] range for visualization frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0); } else if (u_mode == 1) { // Normal: normalize to [0,1] range frag_color = vec4(value.xyz * 0.5 + 0.5, 1.0); } else if (u_mode == 2) { // Albedo: direct output frag_color = vec4(value.rgb, 1.0); } else if (u_mode == 3) { // Material: roughness in R, AO in G frag_color = vec4(value.r, value.g, 0.0, 1.0); } else { frag_color = value; } } )"; /** * @brief Create fullscreen quad VAO */ uint32_t create_fullscreen_quad() { float vertices[] = { // Position // Texcoord -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f }; uint32_t indices[] = { 0, 1, 2, 2, 3, 0 }; uint32_t vao, vbo, ebo; glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glGenBuffers(1, &ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); glBindVertexArray(0); return vao; } int main() { // Initialize logger Logger::init(LogLevel::ARE_LOG_DEBUG); Profiler::init(); ARE_LOG_INFO("========================================"); ARE_LOG_INFO("Phase 3 Verification Program"); ARE_LOG_INFO("G-Buffer Rendering Test"); ARE_LOG_INFO("========================================"); // Create window configuration WindowConfig window_config; window_config.width = 800; window_config.height = 600; window_config.title = "Phase 3 - G-Buffer Test"; window_config.vsync = true; // Create window Window window(window_config); // Initialize OpenGL if (!GLContext::initialize()) { ARE_LOG_CRITICAL("Failed to initialize OpenGL context"); return -1; } GLContext::print_info(); // Get shader directory (relative to executable) std::string shader_dir = "shaders/"; // Create rasterizer int fb_width, fb_height; window.get_framebuffer_size(fb_width, fb_height); Rasterizer rasterizer(fb_width, fb_height); // Initialize shaders // First, create G-Buffer shader manually for testing ShaderProgram gbuffer_shader; // Try to load from file first bool shader_loaded = false; if (file_exists(shader_dir + "gbuffer/gbuffer.vert") && file_exists(shader_dir + "gbuffer/gbuffer.frag")) { if (gbuffer_shader.load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert") && gbuffer_shader.load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag") && gbuffer_shader.link()) { shader_loaded = true; ARE_LOG_INFO("Loaded G-Buffer shaders from files"); } } // Fallback to embedded shaders if (!shader_loaded) { ARE_LOG_WARN("Shader files not found, using embedded shaders"); const char *gbuffer_vert = R"( #version 430 core layout(location = 0) in vec3 a_position; layout(location = 1) in vec3 a_normal; layout(location = 2) in vec2 a_texcoord; layout(location = 3) in vec3 a_tangent; uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform mat3 u_normal_matrix; out vec3 v_world_position; out vec3 v_world_normal; out vec2 v_texcoord; out vec3 v_world_tangent; void main() { vec4 world_pos = u_model * vec4(a_position, 1.0); v_world_position = world_pos.xyz; v_world_normal = normalize(u_normal_matrix * a_normal); v_world_tangent = normalize(u_normal_matrix * a_tangent); v_texcoord = a_texcoord; gl_Position = u_projection * u_view * world_pos; } )"; const char *gbuffer_frag = R"( #version 430 core in vec3 v_world_position; in vec3 v_world_normal; in vec2 v_texcoord; in vec3 v_world_tangent; uniform vec3 u_albedo; uniform float u_metallic; uniform float u_roughness; layout(location = 0) out vec3 g_position; layout(location = 1) out vec3 g_normal; layout(location = 2) out vec4 g_albedo_metallic; layout(location = 3) out vec2 g_roughness_ao; void main() { g_position = v_world_position; g_normal = normalize(v_world_normal); g_albedo_metallic = vec4(u_albedo, u_metallic); g_roughness_ao = vec2(u_roughness, 1.0); } )"; if (!gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, gbuffer_vert) || !gbuffer_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, gbuffer_frag) || !gbuffer_shader.link()) { ARE_LOG_CRITICAL("Failed to compile embedded G-Buffer shaders"); return -1; } } // Create visualization shader ShaderProgram vis_shader; if (!vis_shader.compile_shader(ShaderType::ARE_SHADER_VERTEX, fullscreen_vert_source) || !vis_shader.compile_shader(ShaderType::ARE_SHADER_FRAGMENT, visualize_frag_source) || !vis_shader.link()) { ARE_LOG_CRITICAL("Failed to compile visualization shaders"); return -1; } // Create fullscreen quad uint32_t fullscreen_quad_vao = create_fullscreen_quad(); // Create scene SceneManager scene; // Create materials Material red_material; red_material.set_albedo(Vec3(0.8f, 0.2f, 0.2f)); red_material.set_metallic(0.0f); red_material.set_roughness(0.5f); MaterialHandle red_mat_handle = scene.add_material(red_material); Material green_material; green_material.set_albedo(Vec3(0.2f, 0.8f, 0.2f)); green_material.set_metallic(0.5f); green_material.set_roughness(0.3f); MaterialHandle green_mat_handle = scene.add_material(green_material); // Create meshes Mesh cube = create_cube_mesh(); cube.set_material(red_mat_handle); cube.compute_tangents(); Mesh triangle = create_triangle_mesh(); triangle.set_material(green_mat_handle); triangle.compute_tangents(); // Upload meshes to GPU rasterizer.upload_mesh(cube); rasterizer.upload_mesh(triangle); // Add meshes to scene scene.add_mesh(cube); scene.add_mesh(triangle); // Create camera Camera camera(Vec3(0, 0, 3), Vec3(0, 0, 0)); camera.set_perspective(45.0f, static_cast(fb_width) / fb_height, 0.1f, 100.0f); // Visualization mode (0=position, 1=normal, 2=albedo, 3=material) int vis_mode = 2; // Start with albedo ARE_LOG_INFO("Controls:"); ARE_LOG_INFO(" 1 - View Position buffer"); ARE_LOG_INFO(" 2 - View Normal buffer"); ARE_LOG_INFO(" 3 - View Albedo buffer"); ARE_LOG_INFO(" 4 - View Material buffer (Roughness/AO)"); ARE_LOG_INFO(" ESC - Exit"); // Main loop float time = 0.0f; while (!window.should_close()) { ARE_PROFILE_SCOPE("Frame"); // Poll events window.poll_events(); // Handle input if (window.is_key_pressed(256)) { // ESC window.set_should_close(true); } if (window.is_key_pressed(49)) vis_mode = 0; // 1 - Position if (window.is_key_pressed(50)) vis_mode = 1; // 2 - Normal if (window.is_key_pressed(51)) vis_mode = 2; // 3 - Albedo if (window.is_key_pressed(52)) vis_mode = 3; // 4 - Material // Update camera position (orbit around origin) time += 0.016f; float cam_x = std::sin(time * 0.5f) * 3.0f; float cam_z = std::cos(time * 0.5f) * 3.0f; camera.set_position(Vec3(cam_x, 1.5f, cam_z)); camera.set_target(Vec3(0, 0, 0)); // Render to G-Buffer { ARE_PROFILE_SCOPE("Render G-Buffer"); // Manually render since we're using our own shader GBuffer &gbuffer = rasterizer.get_gbuffer(); gbuffer.bind(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); gbuffer_shader.use(); gbuffer_shader.set_uniform("u_view", camera.get_view_matrix()); gbuffer_shader.set_uniform("u_projection", camera.get_projection_matrix()); // Render all meshes const auto &meshes = scene.get_all_meshes(); const auto &materials = scene.get_all_materials(); for (const auto &mesh : meshes) { if (!mesh.has_gpu_resources()) continue; Mat4 model = Mat4(1.0f); gbuffer_shader.set_uniform("u_model", model); Mat3 normal_matrix = glm::transpose(glm::inverse(Mat3(model))); gbuffer_shader.set_uniform("u_normal_matrix", normal_matrix); MaterialHandle mat_handle = mesh.get_material(); if (mat_handle != are_invalid_handle && mat_handle <= materials.size()) { const Material &mat = materials[mat_handle - 1]; gbuffer_shader.set_uniform("u_albedo", mat.get_albedo()); gbuffer_shader.set_uniform("u_metallic", mat.get_metallic()); gbuffer_shader.set_uniform("u_roughness", mat.get_roughness()); } else { gbuffer_shader.set_uniform("u_albedo", Vec3(0.8f)); gbuffer_shader.set_uniform("u_metallic", 0.0f); gbuffer_shader.set_uniform("u_roughness", 0.5f); } glBindVertexArray(mesh.get_vao()); glDrawElements(GL_TRIANGLES, static_cast(mesh.get_index_count()), GL_UNSIGNED_INT, nullptr); } glBindVertexArray(0); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); gbuffer.unbind(); } // Visualize G-Buffer { ARE_PROFILE_SCOPE("Visualize G-Buffer"); glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, fb_width, fb_height); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); vis_shader.use(); vis_shader.set_uniform("u_mode", vis_mode); vis_shader.set_uniform("u_texture", 0); // Bind appropriate G-Buffer texture GBuffer &gbuffer = rasterizer.get_gbuffer(); gbuffer.bind_texture(vis_mode, 0); glBindVertexArray(fullscreen_quad_vao); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } // Swap buffers window.swap_buffers(); } // Cleanup glDeleteVertexArrays(1, &fullscreen_quad_vao); // Print profiling results Profiler::print_results(); ARE_LOG_INFO("========================================"); ARE_LOG_INFO("Phase 3 test completed successfully!"); ARE_LOG_INFO("========================================"); Profiler::shutdown(); Logger::shutdown(); return 0; }