feat: 实现完整PBR材质系统及修复
- 扩展Material类,添加PBR纹理槽(Albedo/Normal/Metallic/Roughness/AO/Emission) - 添加Mesh::compute_tangents()方法用于法线贴图计算 - 扩展RayTracer材质上传,支持纹理句柄传递 - 更新raytracing compute shader,添加PBR纹理采样和法线贴图TBN变换 - 修复GLSL/C++结构体内存对齐问题 - 添加ACES色调映射解决自发光过曝问题 - 修复累积缓冲区应在色调映射前存储HDR值 - 修复G-Buffer材质类型未传递给光线追踪的问题 - 添加玻璃材质折射逻辑(折射比例、法线翻转、全内反射) - Cornell Box示例添加玻璃球(折射)、发光球(自发光)和金属球测试master
parent
fab45b52a3
commit
0c48d53d5c
|
|
@ -0,0 +1,133 @@
|
||||||
|
# AGENTS.md - Aurora Rendering Engine
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
Aurora Rendering Engine (ARE) is a high-performance path tracing library in C++ developed by NanoEra Studio. It provides a rendering framework using OpenGL 4.3 with support for GPU-accelerated ray tracing, BVH acceleration, and denoising.
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
### Standard Build
|
||||||
|
```bash
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Types
|
||||||
|
- **Debug**: `cmake -DCMAKE_BUILD_TYPE=Debug ..`
|
||||||
|
- **Release**: `cmake -DCMAKE_BUILD_TYPE=Release ..` (default)
|
||||||
|
|
||||||
|
### Running Examples
|
||||||
|
The main example is the Cornell Box demo:
|
||||||
|
```bash
|
||||||
|
./examples/cornell_box
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
This project currently has **no built-in unit tests**. Testing is performed manually by running the example executables. There are no test-specific build targets or test frameworks configured.
|
||||||
|
|
||||||
|
### Linting/Code Formatting
|
||||||
|
- **Format**: Use `.clang-format` configuration in project root
|
||||||
|
```bash
|
||||||
|
clang-format -i src/*.cpp include/**/*.h
|
||||||
|
```
|
||||||
|
- **Clangd**: IDE integration via `.clangd` file (C++20, includes paths configured)
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### General Conventions
|
||||||
|
- **C++ Standard**: C++17 (project CMake), C++20 (clangd for IDE)
|
||||||
|
- **Indentation**: Tabs (see `.clang-format`: `UseTab: Always`)
|
||||||
|
- **Line Length**: Unlimited (`ColumnLimit: 0`)
|
||||||
|
- **Brace Style**: Attach (see `.clang-format`: `BreakBeforeBraces: Attach`)
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
- **Headers**: `include/` - Public API
|
||||||
|
- **Source**: `src/` - Implementation
|
||||||
|
- **Include format**: `#include "path/to/header.h"` (quotes for project headers)
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- **Classes**: `PascalCase` (e.g., `Renderer`, `Scene`)
|
||||||
|
- **Functions**: `PascalCase` (e.g., `initialize()`, `render()`)
|
||||||
|
- **Member variables**: `snake_case_` with trailing underscore (e.g., `config_`, `frame_count_`)
|
||||||
|
- **Types (aliases)**: `PascalCase` (e.g., `Vec3`, `Mat4`, `TextureHandle`)
|
||||||
|
- **Enums**: `PascalCase` with `k` prefix for values (e.g., `LogLevel::ARE_LOG_INFO`)
|
||||||
|
|
||||||
|
### Header Guards
|
||||||
|
```cpp
|
||||||
|
#ifndef ARE_INCLUDE_PATH_TO_FILENAME_H
|
||||||
|
#define ARE_INCLUDE_PATH_TO_FILENAME_H
|
||||||
|
// ... content ...
|
||||||
|
#endif // ARE_INCLUDE_PATH_TO_FILENAME_H
|
||||||
|
```
|
||||||
|
|
||||||
|
### Namespace
|
||||||
|
- All code lives in `are` namespace
|
||||||
|
- Namespace closing comment: `} // namespace are`
|
||||||
|
|
||||||
|
### Imports/Includes Order
|
||||||
|
1. Corresponding header (for .cpp files)
|
||||||
|
2. Project headers (alphabetically within group)
|
||||||
|
3. External library headers (e.g., `<glm/glm.hpp>`, `<glad/glad.h>`)
|
||||||
|
4. Standard library headers
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
- Use Doxygen-style `/** */` or `/* */` for function documentation
|
||||||
|
- Brief descriptions in header files, implementation details in .cpp
|
||||||
|
- Avoid unnecessary inline comments
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Use `ARE_LOG_ERROR()`, `ARE_LOG_WARN()`, `ARE_LOG_CRITICAL()` macros
|
||||||
|
- Return `false` on failure, `true` on success
|
||||||
|
- Check initialization states before operations
|
||||||
|
- Log with context: `"Failed to initialize X"`
|
||||||
|
|
||||||
|
### Smart Pointers
|
||||||
|
- Use `std::unique_ptr` for exclusive ownership
|
||||||
|
- Use `std::shared_ptr` for shared ownership
|
||||||
|
- Avoid raw `new`/`delete`
|
||||||
|
|
||||||
|
### GLM Types
|
||||||
|
Use GLM for all math types:
|
||||||
|
```cpp
|
||||||
|
using Vec2 = glm::vec2;
|
||||||
|
using Vec3 = glm::vec3;
|
||||||
|
using Vec4 = glm::vec4;
|
||||||
|
using Mat3 = glm::mat3;
|
||||||
|
using Mat4 = glm::mat4;
|
||||||
|
```
|
||||||
|
|
||||||
|
### OpenGL Resources
|
||||||
|
- Follow OpenGL best practices
|
||||||
|
- Check OpenGL errors during development (debug builds)
|
||||||
|
- Clean up resources in destructors or `shutdown()` methods
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
```
|
||||||
|
ARE/
|
||||||
|
├── include/ # Public headers
|
||||||
|
│ ├── basic/ # Types, constants, math
|
||||||
|
│ ├── core/ # Renderer, raytracer, BVH
|
||||||
|
│ ├── scene/ # Scene objects (mesh, material, camera)
|
||||||
|
│ ├── resource/ # GPU resources (shader, texture, buffer)
|
||||||
|
│ └── utils/ # Utilities (logger, config)
|
||||||
|
├── src/ # Implementation files
|
||||||
|
├── examples/ # Example applications
|
||||||
|
├── shaders/ # GLSL shaders
|
||||||
|
├── lib/ # External libraries (glad, stb, spdlog)
|
||||||
|
└── build/ # Build output (generated)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Classes
|
||||||
|
- `Renderer` - Main rendering engine interface
|
||||||
|
- `Scene` - Scene container (meshes, materials, lights, camera)
|
||||||
|
- `RayTracer` - GPU ray tracing implementation
|
||||||
|
- `BVH` - Acceleration structure
|
||||||
|
- `GBuffer` - Geometry buffer for deferred rendering
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- OpenGL 4.3
|
||||||
|
- GLFW 3
|
||||||
|
- GLAD (OpenGL loader)
|
||||||
|
- GLM (math library)
|
||||||
|
- stb-image (image loading)
|
||||||
|
- spdlog (logging)
|
||||||
Binary file not shown.
|
|
@ -1,17 +1,17 @@
|
||||||
#include <core/renderer.h>
|
#include <core/renderer.h>
|
||||||
#include <scene/scene.h>
|
|
||||||
#include <scene/camera.h>
|
|
||||||
#include <scene/mesh.h>
|
|
||||||
#include <scene/material.h>
|
|
||||||
#include <scene/light.h>
|
|
||||||
#include <utils/logger.h>
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <scene/camera.h>
|
||||||
|
#include <scene/light.h>
|
||||||
|
#include <scene/material.h>
|
||||||
|
#include <scene/mesh.h>
|
||||||
|
#include <scene/scene.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
using namespace are;
|
using namespace are;
|
||||||
|
|
||||||
|
|
@ -130,6 +130,53 @@ std::shared_ptr<Mesh> create_box(const Vec3& min, const Vec3& max, uint material
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Create a sphere mesh
|
||||||
|
std::shared_ptr<Mesh> create_sphere(float radius, uint segments, uint rings, uint material_id) {
|
||||||
|
auto mesh = std::make_shared<Mesh>();
|
||||||
|
|
||||||
|
std::vector<Vertex> vertices;
|
||||||
|
std::vector<uint> indices;
|
||||||
|
|
||||||
|
for (uint ring = 0; ring <= rings; ++ring) {
|
||||||
|
float theta = ring * glm::pi<float>() / rings;
|
||||||
|
float sin_theta = sin(theta);
|
||||||
|
float cos_theta = cos(theta);
|
||||||
|
|
||||||
|
for (uint seg = 0; seg <= segments; ++seg) {
|
||||||
|
float phi = seg * 2.0f * glm::pi<float>() / segments;
|
||||||
|
float x = cos(phi) * sin_theta;
|
||||||
|
float y = cos_theta;
|
||||||
|
float z = sin(phi) * sin_theta;
|
||||||
|
|
||||||
|
Vec3 pos = Vec3(x, y, z) * radius;
|
||||||
|
Vec3 normal = Vec3(x, y, z);
|
||||||
|
Vec2 uv = Vec2((float)seg / segments, (float)ring / rings);
|
||||||
|
Vec3 tangent = Vec3(-sin(phi), 0.0f, cos(phi));
|
||||||
|
|
||||||
|
vertices.push_back({ pos, normal, uv, tangent });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint ring = 0; ring < rings; ++ring) {
|
||||||
|
for (uint seg = 0; seg < segments; ++seg) {
|
||||||
|
uint current = ring * (segments + 1) + seg;
|
||||||
|
indices.push_back(current);
|
||||||
|
indices.push_back(current + segments + 1);
|
||||||
|
indices.push_back(current + 1);
|
||||||
|
indices.push_back(current + 1);
|
||||||
|
indices.push_back(current + segments + 1);
|
||||||
|
indices.push_back(current + segments + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh->set_vertices(vertices);
|
||||||
|
mesh->set_indices(indices);
|
||||||
|
mesh->set_material(material_id);
|
||||||
|
mesh->compute_tangents();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Setup Cornell Box scene
|
/// @brief Setup Cornell Box scene
|
||||||
void setup_cornell_box() {
|
void setup_cornell_box() {
|
||||||
g_scene = std::make_unique<Scene>();
|
g_scene = std::make_unique<Scene>();
|
||||||
|
|
@ -164,10 +211,25 @@ void setup_cornell_box() {
|
||||||
auto metal_material = std::make_shared<Material>();
|
auto metal_material = std::make_shared<Material>();
|
||||||
metal_material->set_albedo(Vec3(0.95f, 0.93f, 0.88f));
|
metal_material->set_albedo(Vec3(0.95f, 0.93f, 0.88f));
|
||||||
metal_material->set_metallic(1.0f);
|
metal_material->set_metallic(1.0f);
|
||||||
metal_material->set_roughness(0.1f);
|
metal_material->set_roughness(0.0f);
|
||||||
metal_material->set_type(MaterialType::METAL);
|
metal_material->set_type(MaterialType::METAL);
|
||||||
uint metal_id = g_scene->add_material(metal_material);
|
uint metal_id = g_scene->add_material(metal_material);
|
||||||
|
|
||||||
|
// 5: Glass/Dielectric (refraction)
|
||||||
|
auto glass_material = std::make_shared<Material>();
|
||||||
|
glass_material->set_albedo(Vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
glass_material->set_ior(0.5f);
|
||||||
|
glass_material->set_roughness(0.0f);
|
||||||
|
glass_material->set_type(MaterialType::DIELECTRIC);
|
||||||
|
uint glass_id = g_scene->add_material(glass_material);
|
||||||
|
|
||||||
|
// 6: Yellow emissive sphere
|
||||||
|
auto emissive_sphere_mat = std::make_shared<Material>();
|
||||||
|
emissive_sphere_mat->set_albedo(Vec3(1.0f, 0.8f, 0.2f));
|
||||||
|
emissive_sphere_mat->set_emission(Vec3(5.0f, 4.0f, 1.0f));
|
||||||
|
emissive_sphere_mat->set_type(MaterialType::EMISSIVE);
|
||||||
|
uint emissive_sphere_id = g_scene->add_material(emissive_sphere_mat);
|
||||||
|
|
||||||
// Create room (Cornell Box)
|
// Create room (Cornell Box)
|
||||||
float room_size = 2.0f;
|
float room_size = 2.0f;
|
||||||
|
|
||||||
|
|
@ -178,8 +240,7 @@ void setup_cornell_box() {
|
||||||
Vec3(room_size, -room_size, room_size),
|
Vec3(room_size, -room_size, room_size),
|
||||||
Vec3(-room_size, -room_size, room_size),
|
Vec3(-room_size, -room_size, room_size),
|
||||||
Vec3(0.0f, 1.0f, 0.0f),
|
Vec3(0.0f, 1.0f, 0.0f),
|
||||||
white_id
|
white_id);
|
||||||
);
|
|
||||||
floor->upload_to_gpu();
|
floor->upload_to_gpu();
|
||||||
g_scene->add_mesh(floor);
|
g_scene->add_mesh(floor);
|
||||||
|
|
||||||
|
|
@ -190,8 +251,7 @@ void setup_cornell_box() {
|
||||||
Vec3(room_size, room_size, -room_size),
|
Vec3(room_size, room_size, -room_size),
|
||||||
Vec3(-room_size, room_size, -room_size),
|
Vec3(-room_size, room_size, -room_size),
|
||||||
Vec3(0.0f, -1.0f, 0.0f),
|
Vec3(0.0f, -1.0f, 0.0f),
|
||||||
white_id
|
white_id);
|
||||||
);
|
|
||||||
ceiling->upload_to_gpu();
|
ceiling->upload_to_gpu();
|
||||||
g_scene->add_mesh(ceiling);
|
g_scene->add_mesh(ceiling);
|
||||||
|
|
||||||
|
|
@ -202,8 +262,7 @@ void setup_cornell_box() {
|
||||||
Vec3(room_size, room_size, -room_size),
|
Vec3(room_size, room_size, -room_size),
|
||||||
Vec3(room_size, -room_size, -room_size),
|
Vec3(room_size, -room_size, -room_size),
|
||||||
Vec3(0.0f, 0.0f, 1.0f),
|
Vec3(0.0f, 0.0f, 1.0f),
|
||||||
white_id
|
white_id);
|
||||||
);
|
|
||||||
back_wall->upload_to_gpu();
|
back_wall->upload_to_gpu();
|
||||||
g_scene->add_mesh(back_wall);
|
g_scene->add_mesh(back_wall);
|
||||||
|
|
||||||
|
|
@ -214,8 +273,7 @@ void setup_cornell_box() {
|
||||||
Vec3(-room_size, room_size, -room_size),
|
Vec3(-room_size, room_size, -room_size),
|
||||||
Vec3(-room_size, -room_size, -room_size),
|
Vec3(-room_size, -room_size, -room_size),
|
||||||
Vec3(1.0f, 0.0f, 0.0f),
|
Vec3(1.0f, 0.0f, 0.0f),
|
||||||
red_id
|
red_id);
|
||||||
);
|
|
||||||
left_wall->upload_to_gpu();
|
left_wall->upload_to_gpu();
|
||||||
g_scene->add_mesh(left_wall);
|
g_scene->add_mesh(left_wall);
|
||||||
|
|
||||||
|
|
@ -226,8 +284,7 @@ void setup_cornell_box() {
|
||||||
Vec3(room_size, room_size, room_size),
|
Vec3(room_size, room_size, room_size),
|
||||||
Vec3(room_size, -room_size, room_size),
|
Vec3(room_size, -room_size, room_size),
|
||||||
Vec3(-1.0f, 0.0f, 0.0f),
|
Vec3(-1.0f, 0.0f, 0.0f),
|
||||||
green_id
|
green_id);
|
||||||
);
|
|
||||||
right_wall->upload_to_gpu();
|
right_wall->upload_to_gpu();
|
||||||
g_scene->add_mesh(right_wall);
|
g_scene->add_mesh(right_wall);
|
||||||
|
|
||||||
|
|
@ -239,8 +296,7 @@ void setup_cornell_box() {
|
||||||
Vec3(light_size, room_size - 0.01f, light_size),
|
Vec3(light_size, room_size - 0.01f, light_size),
|
||||||
Vec3(-light_size, room_size - 0.01f, light_size),
|
Vec3(-light_size, room_size - 0.01f, light_size),
|
||||||
Vec3(0.0f, -1.0f, 0.0f),
|
Vec3(0.0f, -1.0f, 0.0f),
|
||||||
light_id
|
light_id);
|
||||||
);
|
|
||||||
area_light->upload_to_gpu();
|
area_light->upload_to_gpu();
|
||||||
g_scene->add_mesh(area_light);
|
g_scene->add_mesh(area_light);
|
||||||
|
|
||||||
|
|
@ -250,10 +306,22 @@ void setup_cornell_box() {
|
||||||
g_scene->add_mesh(tall_box);
|
g_scene->add_mesh(tall_box);
|
||||||
|
|
||||||
// Short box (metal, right side)
|
// Short box (metal, right side)
|
||||||
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), white_id);
|
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), glass_id);
|
||||||
short_box->upload_to_gpu();
|
short_box->upload_to_gpu();
|
||||||
g_scene->add_mesh(short_box);
|
g_scene->add_mesh(short_box);
|
||||||
|
|
||||||
|
// // Glass sphere (dielectric/refraction test)
|
||||||
|
// auto glass_sphere = create_sphere(0.4f, 32, 16, glass_id);
|
||||||
|
// glass_sphere->set_position(Vec3(-0.5f, -0.6f, 0.0f));
|
||||||
|
// glass_sphere->upload_to_gpu();
|
||||||
|
// g_scene->add_mesh(glass_sphere);
|
||||||
|
//
|
||||||
|
// // Yellow emissive sphere (emission test)
|
||||||
|
// auto emissive_sphere = create_sphere(0.25f, 32, 16, emissive_sphere_id);
|
||||||
|
// emissive_sphere->set_position(Vec3(0.5f, -0.75f, 0.3f));
|
||||||
|
// emissive_sphere->upload_to_gpu();
|
||||||
|
// g_scene->add_mesh(emissive_sphere);
|
||||||
|
|
||||||
// Setup camera
|
// Setup camera
|
||||||
g_camera = std::make_shared<Camera>();
|
g_camera = std::make_shared<Camera>();
|
||||||
g_camera->set_position(g_cameraPos);
|
g_camera->set_position(g_cameraPos);
|
||||||
|
|
@ -346,13 +414,14 @@ void process_input() {
|
||||||
g_pitch += yoffset;
|
g_pitch += yoffset;
|
||||||
|
|
||||||
// Constrain pitch
|
// Constrain pitch
|
||||||
if (g_pitch > 89.0f) g_pitch = 89.0f;
|
if (g_pitch > 89.0f)
|
||||||
if (g_pitch < -89.0f) g_pitch = -89.0f;
|
g_pitch = 89.0f;
|
||||||
|
if (g_pitch < -89.0f)
|
||||||
|
g_pitch = -89.0f;
|
||||||
|
|
||||||
camera_changed = true;
|
camera_changed = true;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
g_firstMouse = true; // Reset when released
|
g_firstMouse = true; // Reset when released
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,8 +495,7 @@ void render_loop() {
|
||||||
|
|
||||||
if (delta >= 1.0) {
|
if (delta >= 1.0) {
|
||||||
double fps = frame_count / delta;
|
double fps = frame_count / delta;
|
||||||
std::string title = "Aurora - Cornell Box | FPS: " + std::to_string((int)fps) +
|
std::string title = "Aurora - Cornell Box | FPS: " + std::to_string((int)fps) + " | Frame: " + std::to_string((int)stats.frame_time_ms_) + "ms";
|
||||||
" | Frame: " + std::to_string((int)stats.frame_time_ms_) + "ms";
|
|
||||||
glfwSetWindowTitle(g_window, title.c_str());
|
glfwSetWindowTitle(g_window, title.c_str());
|
||||||
|
|
||||||
frame_count = 0;
|
frame_count = 0;
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,22 @@
|
||||||
|
|
||||||
#include "basic/types.h"
|
#include "basic/types.h"
|
||||||
#include "resource/texture.h"
|
#include "resource/texture.h"
|
||||||
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
||||||
|
// Texture slot enumeration for PBR materials
|
||||||
|
enum class TextureSlot {
|
||||||
|
ALBEDO = 0,
|
||||||
|
NORMAL = 1,
|
||||||
|
METALLIC = 2,
|
||||||
|
ROUGHNESS = 3,
|
||||||
|
AO = 4,
|
||||||
|
EMISSION = 5,
|
||||||
|
COUNT = 6
|
||||||
|
};
|
||||||
|
|
||||||
// Material type enumeration
|
// Material type enumeration
|
||||||
enum class MaterialType {
|
enum class MaterialType {
|
||||||
DIFFUSE = 0,
|
DIFFUSE = 0,
|
||||||
|
|
@ -72,6 +84,37 @@ public:
|
||||||
*/
|
*/
|
||||||
void set_normal_texture(std::shared_ptr<Texture> texture);
|
void set_normal_texture(std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set metallic map
|
||||||
|
* @param texture Metallic texture (R channel)
|
||||||
|
*/
|
||||||
|
void set_metallic_texture(std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set roughness map
|
||||||
|
* @param texture Roughness texture (G channel)
|
||||||
|
*/
|
||||||
|
void set_roughness_texture(std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set ambient occlusion map
|
||||||
|
* @param texture AO texture (R channel)
|
||||||
|
*/
|
||||||
|
void set_ao_texture(std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set emission map
|
||||||
|
* @param texture Emission texture (RGB)
|
||||||
|
*/
|
||||||
|
void set_emission_texture(std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set texture for a specific slot
|
||||||
|
* @param slot Texture slot
|
||||||
|
* @param texture Texture to set
|
||||||
|
*/
|
||||||
|
void set_texture(TextureSlot slot, std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Get albedo color
|
* @brief Get albedo color
|
||||||
* @return Albedo color
|
* @return Albedo color
|
||||||
|
|
@ -125,7 +168,7 @@ public:
|
||||||
* @return Albedo texture (nullptr if none)
|
* @return Albedo texture (nullptr if none)
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<Texture> get_albedo_texture() const {
|
std::shared_ptr<Texture> get_albedo_texture() const {
|
||||||
return albedo_texture_;
|
return textures_[static_cast<int>(TextureSlot::ALBEDO)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -133,7 +176,57 @@ public:
|
||||||
* @return Normal texture (nullptr if none)
|
* @return Normal texture (nullptr if none)
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<Texture> get_normal_texture() const {
|
std::shared_ptr<Texture> get_normal_texture() const {
|
||||||
return normal_texture_;
|
return textures_[static_cast<int>(TextureSlot::NORMAL)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get metallic texture
|
||||||
|
* @return Metallic texture (nullptr if none)
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Texture> get_metallic_texture() const {
|
||||||
|
return textures_[static_cast<int>(TextureSlot::METALLIC)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get roughness texture
|
||||||
|
* @return Roughness texture (nullptr if none)
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Texture> get_roughness_texture() const {
|
||||||
|
return textures_[static_cast<int>(TextureSlot::ROUGHNESS)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get AO texture
|
||||||
|
* @return AO texture (nullptr if none)
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Texture> get_ao_texture() const {
|
||||||
|
return textures_[static_cast<int>(TextureSlot::AO)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get emission texture
|
||||||
|
* @return Emission texture (nullptr if none)
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Texture> get_emission_texture() const {
|
||||||
|
return textures_[static_cast<int>(TextureSlot::EMISSION)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Get texture for a specific slot
|
||||||
|
* @param slot Texture slot
|
||||||
|
* @return Texture (nullptr if none)
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Texture> get_texture(TextureSlot slot) const {
|
||||||
|
return textures_[static_cast<int>(slot)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Check if material has texture for slot
|
||||||
|
* @param slot Texture slot
|
||||||
|
* @return True if texture exists
|
||||||
|
*/
|
||||||
|
bool has_texture(TextureSlot slot) const {
|
||||||
|
return textures_[static_cast<int>(slot)] != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -144,8 +237,7 @@ private:
|
||||||
float ior_;
|
float ior_;
|
||||||
MaterialType type_;
|
MaterialType type_;
|
||||||
|
|
||||||
std::shared_ptr<Texture> albedo_texture_;
|
std::array<std::shared_ptr<Texture>, static_cast<int>(TextureSlot::COUNT)> textures_;
|
||||||
std::shared_ptr<Texture> normal_texture_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define ARE_INCLUDE_SCENE_MESH_H
|
#define ARE_INCLUDE_SCENE_MESH_H
|
||||||
|
|
||||||
#include "basic/types.h"
|
#include "basic/types.h"
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
@ -39,6 +40,14 @@ public:
|
||||||
*/
|
*/
|
||||||
void set_transform(const Mat4 &transform);
|
void set_transform(const Mat4 &transform);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Set position (sugar for transform)
|
||||||
|
* @param position Position vector
|
||||||
|
*/
|
||||||
|
void set_position(const Vec3 &position) {
|
||||||
|
transform_ = glm::translate(Mat4(1.0f), position);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Get vertices
|
* @brief Get vertices
|
||||||
* @return Vertex array
|
* @return Vertex array
|
||||||
|
|
@ -77,6 +86,12 @@ public:
|
||||||
*/
|
*/
|
||||||
bool upload_to_gpu();
|
bool upload_to_gpu();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Compute tangents from positions, normals and texcoords
|
||||||
|
* Should be called after set_vertices and set_indices
|
||||||
|
*/
|
||||||
|
void compute_tangents();
|
||||||
|
|
||||||
// Release GPU resources
|
// Release GPU resources
|
||||||
void release_gpu_resources();
|
void release_gpu_resources();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,14 @@ layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||||
|
|
||||||
struct Material {
|
struct Material {
|
||||||
vec3 albedo;
|
vec3 albedo;
|
||||||
float metallic;
|
|
||||||
vec3 emission;
|
vec3 emission;
|
||||||
|
float metallic;
|
||||||
float roughness;
|
float roughness;
|
||||||
int type;
|
int type;
|
||||||
float ior;
|
float ior;
|
||||||
vec2 padding;
|
float padding1;
|
||||||
|
float padding2;
|
||||||
|
uint texture_handles[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Light {
|
struct Light {
|
||||||
|
|
@ -63,6 +65,7 @@ struct HitInfo {
|
||||||
vec3 normal;
|
vec3 normal;
|
||||||
vec2 texcoord;
|
vec2 texcoord;
|
||||||
uint material_id;
|
uint material_id;
|
||||||
|
int material_type; // material type from G-Buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScatterResult {
|
struct ScatterResult {
|
||||||
|
|
@ -100,6 +103,15 @@ uniform mat4 u_inv_view_projection;
|
||||||
uniform bool u_enable_accumulation;
|
uniform bool u_enable_accumulation;
|
||||||
uniform bool u_use_bvh;
|
uniform bool u_use_bvh;
|
||||||
uniform uint u_bvh_node_count;
|
uniform uint u_bvh_node_count;
|
||||||
|
uniform bool u_enable_textures;
|
||||||
|
|
||||||
|
// Texture samplers for PBR
|
||||||
|
layout(binding = 10) uniform sampler2D u_texture_albedo;
|
||||||
|
layout(binding = 11) uniform sampler2D u_texture_normal;
|
||||||
|
layout(binding = 12) uniform sampler2D u_texture_metallic;
|
||||||
|
layout(binding = 13) uniform sampler2D u_texture_roughness;
|
||||||
|
layout(binding = 14) uniform sampler2D u_texture_ao;
|
||||||
|
layout(binding = 15) uniform sampler2D u_texture_emission;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Utility
|
// Utility
|
||||||
|
|
@ -353,6 +365,7 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||||
hit.normal = vec3(0.0, 1.0, 0.0);
|
hit.normal = vec3(0.0, 1.0, 0.0);
|
||||||
hit.texcoord = vec2(0.0);
|
hit.texcoord = vec2(0.0);
|
||||||
hit.material_id = 0u;
|
hit.material_id = 0u;
|
||||||
|
hit.material_type = 0;
|
||||||
|
|
||||||
vec4 pos = imageLoad(g_position, pixel_coords);
|
vec4 pos = imageLoad(g_position, pixel_coords);
|
||||||
if (pos.w <= 0.5) {
|
if (pos.w <= 0.5) {
|
||||||
|
|
@ -365,10 +378,15 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||||
// integer material id
|
// integer material id
|
||||||
uint mid = imageLoad(g_material_id, pixel_coords).r;
|
uint mid = imageLoad(g_material_id, pixel_coords).r;
|
||||||
|
|
||||||
|
// material type stored in g_material.w
|
||||||
|
vec4 mat = imageLoad(g_material, pixel_coords);
|
||||||
|
int mtype = int(mat.w);
|
||||||
|
|
||||||
hit.hit = true;
|
hit.hit = true;
|
||||||
hit.position = p;
|
hit.position = p;
|
||||||
hit.normal = n;
|
hit.normal = n;
|
||||||
hit.material_id = mid;
|
hit.material_id = mid;
|
||||||
|
hit.material_type = mtype;
|
||||||
|
|
||||||
// For RR/any debug usage; path tracing uses this as starting point only.
|
// For RR/any debug usage; path tracing uses this as starting point only.
|
||||||
hit.t = length(p - ray.origin);
|
hit.t = length(p - ray.origin);
|
||||||
|
|
@ -380,6 +398,54 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||||
// Material + scattering
|
// Material + scattering
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Apply normal map in world space
|
||||||
|
vec3 apply_normal_map(vec3 normal, vec2 texcoord, vec3 tangent, uint normal_handle) {
|
||||||
|
if (normal_handle == 0 || !u_enable_textures) return normal;
|
||||||
|
|
||||||
|
vec3 T = normalize(tangent - normal * dot(tangent, normal));
|
||||||
|
vec3 B = cross(normal, T);
|
||||||
|
mat3 TBN = mat3(T, B, normal);
|
||||||
|
|
||||||
|
vec3 map_n = texture(u_texture_normal, texcoord).xyz * 2.0 - 1.0;
|
||||||
|
return normalize(TBN * map_n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply material textures to get final PBR values
|
||||||
|
void apply_material_textures(inout Material mat, vec2 texcoord, vec3 normal, vec3 tangent) {
|
||||||
|
if (!u_enable_textures) return;
|
||||||
|
|
||||||
|
// Albedo texture
|
||||||
|
if (mat.texture_handles[0] != 0) {
|
||||||
|
mat.albedo *= texture(u_texture_albedo, texcoord).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal map
|
||||||
|
if (mat.texture_handles[1] != 0) {
|
||||||
|
normal = apply_normal_map(normal, texcoord, tangent, mat.texture_handles[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metallic texture
|
||||||
|
if (mat.texture_handles[2] != 0) {
|
||||||
|
mat.metallic *= texture(u_texture_metallic, texcoord).r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roughness texture
|
||||||
|
if (mat.texture_handles[3] != 0) {
|
||||||
|
mat.roughness *= texture(u_texture_roughness, texcoord).r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AO texture (multiply)
|
||||||
|
if (mat.texture_handles[4] != 0) {
|
||||||
|
float ao = texture(u_texture_ao, texcoord).r;
|
||||||
|
mat.albedo *= ao;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emission texture (add)
|
||||||
|
if (mat.texture_handles[5] != 0) {
|
||||||
|
mat.emission += texture(u_texture_emission, texcoord).rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
|
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
|
||||||
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
|
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
|
||||||
}
|
}
|
||||||
|
|
@ -423,18 +489,37 @@ ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout ui
|
||||||
r.attenuation = vec3(1.0);
|
r.attenuation = vec3(1.0);
|
||||||
|
|
||||||
vec3 unit_dir = normalize(ray_in.direction);
|
vec3 unit_dir = normalize(ray_in.direction);
|
||||||
float cos_theta = min(dot(-unit_dir, hit.normal), 1.0);
|
float cos_theta = dot(-unit_dir, hit.normal);
|
||||||
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
|
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
|
||||||
|
|
||||||
float refraction_ratio = dot(unit_dir, hit.normal) < 0.0 ? (1.0 / mat.ior) : mat.ior;
|
// Determine if ray is entering or exiting the material
|
||||||
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
|
// If dot(dir, normal) < 0, ray is entering (from air into material)
|
||||||
float reflect_prob = fresnel_dielectric(cos_theta, refraction_ratio);
|
bool entering = cos_theta > 0.0;
|
||||||
|
|
||||||
|
// eta: ratio of indices (etai/etat)
|
||||||
|
// Entering: eta = 1.0/ior (air to material)
|
||||||
|
// Exiting: eta = ior/1.0 (material to air)
|
||||||
|
float eta = entering ? (1.0 / mat.ior) : mat.ior;
|
||||||
|
|
||||||
|
// Use correct normal for refraction calculation
|
||||||
|
// When exiting, we need to use -normal
|
||||||
|
vec3 normal = entering ? hit.normal : -hit.normal;
|
||||||
|
|
||||||
|
// Check for total internal reflection
|
||||||
|
float sin_theta_t = eta * sin_theta;
|
||||||
|
bool total_internal_reflection = sin_theta_t >= 1.0;
|
||||||
|
|
||||||
|
// Fresnel reflectance (Schlick approximation)
|
||||||
|
float f0 = pow((1.0 - mat.ior) / (1.0 + mat.ior), 2.0);
|
||||||
|
float f = f0 + (1.0 - f0) * pow(1.0 - abs(cos_theta), 5.0);
|
||||||
|
|
||||||
vec3 dir;
|
vec3 dir;
|
||||||
if (cannot_refract || random_float(seed) < reflect_prob) {
|
if (total_internal_reflection || random_float(seed) < f) {
|
||||||
dir = reflect_vector(unit_dir, hit.normal);
|
// Reflect
|
||||||
|
dir = reflect_vector(unit_dir, normal);
|
||||||
} else {
|
} else {
|
||||||
dir = refract_vector(unit_dir, hit.normal, refraction_ratio);
|
// Refract
|
||||||
|
dir = refract_vector(unit_dir, normal, eta);
|
||||||
}
|
}
|
||||||
|
|
||||||
r.scattered_ray.origin = hit.position + dir * EPSILON;
|
r.scattered_ray.origin = hit.position + dir * EPSILON;
|
||||||
|
|
@ -548,6 +633,16 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
||||||
if (hit0.hit) {
|
if (hit0.hit) {
|
||||||
Material mat0 = fetch_material(hit0.material_id);
|
Material mat0 = fetch_material(hit0.material_id);
|
||||||
|
|
||||||
|
// Override material type from G-Buffer if available
|
||||||
|
if (hit0.material_type >= 0) {
|
||||||
|
mat0.type = hit0.material_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply PBR textures
|
||||||
|
vec3 tangent0 = normalize(cross(hit0.normal, vec3(0.0, 1.0, 0.0)));
|
||||||
|
if (length(tangent0) < 0.001) tangent0 = normalize(cross(hit0.normal, vec3(1.0, 0.0, 0.0)));
|
||||||
|
apply_material_textures(mat0, hit0.texcoord, hit0.normal, tangent0);
|
||||||
|
|
||||||
radiance += throughput * mat0.emission;
|
radiance += throughput * mat0.emission;
|
||||||
if (mat0.type == MATERIAL_DIFFUSE) {
|
if (mat0.type == MATERIAL_DIFFUSE) {
|
||||||
radiance += throughput * eval_direct_lighting(hit0, mat0, seed);
|
radiance += throughput * eval_direct_lighting(hit0, mat0, seed);
|
||||||
|
|
@ -570,6 +665,11 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
||||||
|
|
||||||
Material mat = fetch_material(hit.material_id);
|
Material mat = fetch_material(hit.material_id);
|
||||||
|
|
||||||
|
// Apply PBR textures
|
||||||
|
vec3 tangent = normalize(cross(hit.normal, vec3(0.0, 1.0, 0.0)));
|
||||||
|
if (length(tangent) < 0.001) tangent = normalize(cross(hit.normal, vec3(1.0, 0.0, 0.0)));
|
||||||
|
apply_material_textures(mat, hit.texcoord, hit.normal, tangent);
|
||||||
|
|
||||||
radiance += throughput * mat.emission;
|
radiance += throughput * mat.emission;
|
||||||
if (mat.type == MATERIAL_DIFFUSE) {
|
if (mat.type == MATERIAL_DIFFUSE) {
|
||||||
radiance += throughput * eval_direct_lighting(hit, mat, seed);
|
radiance += throughput * eval_direct_lighting(hit, mat, seed);
|
||||||
|
|
@ -595,6 +695,16 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
||||||
return radiance;
|
return radiance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ACES Filmic Tone Mapping
|
||||||
|
vec3 aces_tonemap(vec3 x) {
|
||||||
|
float a = 2.51;
|
||||||
|
float b = 0.03;
|
||||||
|
float c = 2.43;
|
||||||
|
float d = 0.59;
|
||||||
|
float e = 0.14;
|
||||||
|
return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
|
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
|
||||||
ivec2 image_size = imageSize(output_image);
|
ivec2 image_size = imageSize(output_image);
|
||||||
|
|
@ -611,14 +721,20 @@ void main() {
|
||||||
}
|
}
|
||||||
color /= float(spp);
|
color /= float(spp);
|
||||||
|
|
||||||
color = clamp(color, vec3(0.0), vec3(10.0));
|
color = clamp(color, vec3(0.0), vec3(100.0));
|
||||||
|
|
||||||
|
// Store HDR color to accumulation buffer BEFORE tone mapping
|
||||||
|
vec3 accumulation_color = color;
|
||||||
|
|
||||||
if (u_enable_accumulation && u_frame_count > 0u) {
|
if (u_enable_accumulation && u_frame_count > 0u) {
|
||||||
vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
|
vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
|
||||||
float w = 1.0 / float(u_frame_count + 1u);
|
float w = 1.0 / float(u_frame_count + 1u);
|
||||||
color = mix(accumulated, color, w);
|
accumulation_color = mix(accumulated, color, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
|
// Apply ACES tone mapping to output (not accumulation)
|
||||||
imageStore(output_image, pixel_coords, vec4(color, 1.0));
|
vec3 output_color = aces_tonemap(accumulation_color);
|
||||||
|
|
||||||
|
imageStore(accumulation_image, pixel_coords, vec4(accumulation_color, 1.0));
|
||||||
|
imageStore(output_image, pixel_coords, vec4(output_color, 1.0));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,35 @@ void RayTracer::trace(const Scene &scene, const GBuffer &gbuffer, TextureHandle
|
||||||
compute_shader_->set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
|
compute_shader_->set_uint("u_light_count", static_cast<uint>(scene.get_lights().size()));
|
||||||
compute_shader_->set_bool("u_enable_accumulation", config_.enable_accumulation_);
|
compute_shader_->set_bool("u_enable_accumulation", config_.enable_accumulation_);
|
||||||
|
|
||||||
|
// Enable/disable textures based on material usage
|
||||||
|
const auto &materials = scene.get_materials();
|
||||||
|
bool has_textures = false;
|
||||||
|
for (const auto &mat : materials) {
|
||||||
|
if (mat->has_texture(TextureSlot::ALBEDO) || mat->has_texture(TextureSlot::NORMAL) || mat->has_texture(TextureSlot::METALLIC) || mat->has_texture(TextureSlot::ROUGHNESS) || mat->has_texture(TextureSlot::AO) || mat->has_texture(TextureSlot::EMISSION)) {
|
||||||
|
has_textures = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compute_shader_->set_bool("u_enable_textures", has_textures);
|
||||||
|
|
||||||
|
// Bind texture samplers (binding 10-15)
|
||||||
|
if (has_textures) {
|
||||||
|
// Bind default textures (0 = no texture) for now
|
||||||
|
// In full implementation, would bind actual material textures
|
||||||
|
glActiveTexture(GL_TEXTURE10);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE11);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE12);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE13);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE14);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE15);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Set camera data
|
// Set camera data
|
||||||
const Camera &camera = scene.get_camera();
|
const Camera &camera = scene.get_camera();
|
||||||
compute_shader_->set_vec3("u_camera_position", camera.get_position());
|
compute_shader_->set_vec3("u_camera_position", camera.get_position());
|
||||||
|
|
@ -247,14 +276,17 @@ void RayTracer::upload_scene_data_(const Scene &scene) {
|
||||||
// Upload materials (on change only)
|
// Upload materials (on change only)
|
||||||
const auto &materials = scene.get_materials();
|
const auto &materials = scene.get_materials();
|
||||||
if (!materials.empty()) {
|
if (!materials.empty()) {
|
||||||
|
// Aligned to match GLSL std430 layout (vec3 = vec4 = 16 bytes)
|
||||||
struct MaterialData {
|
struct MaterialData {
|
||||||
Vec3 albedo;
|
alignas(16) Vec3 albedo;
|
||||||
|
alignas(16) Vec3 emission;
|
||||||
float metallic;
|
float metallic;
|
||||||
Vec3 emission;
|
|
||||||
float roughness;
|
float roughness;
|
||||||
int type;
|
int type;
|
||||||
float ior;
|
float ior;
|
||||||
Vec2 padding;
|
float padding1;
|
||||||
|
float padding2;
|
||||||
|
uint texture_handles[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<MaterialData> material_data;
|
std::vector<MaterialData> material_data;
|
||||||
|
|
@ -268,6 +300,15 @@ void RayTracer::upload_scene_data_(const Scene &scene) {
|
||||||
data.roughness = mat->get_roughness();
|
data.roughness = mat->get_roughness();
|
||||||
data.type = static_cast<int>(mat->get_type());
|
data.type = static_cast<int>(mat->get_type());
|
||||||
data.ior = mat->get_ior();
|
data.ior = mat->get_ior();
|
||||||
|
|
||||||
|
// Texture handles (0 = no texture)
|
||||||
|
data.texture_handles[0] = mat->get_texture(TextureSlot::ALBEDO) ? mat->get_texture(TextureSlot::ALBEDO)->get_handle() : 0;
|
||||||
|
data.texture_handles[1] = mat->get_texture(TextureSlot::NORMAL) ? mat->get_texture(TextureSlot::NORMAL)->get_handle() : 0;
|
||||||
|
data.texture_handles[2] = mat->get_texture(TextureSlot::METALLIC) ? mat->get_texture(TextureSlot::METALLIC)->get_handle() : 0;
|
||||||
|
data.texture_handles[3] = mat->get_texture(TextureSlot::ROUGHNESS) ? mat->get_texture(TextureSlot::ROUGHNESS)->get_handle() : 0;
|
||||||
|
data.texture_handles[4] = mat->get_texture(TextureSlot::AO) ? mat->get_texture(TextureSlot::AO)->get_handle() : 0;
|
||||||
|
data.texture_handles[5] = mat->get_texture(TextureSlot::EMISSION) ? mat->get_texture(TextureSlot::EMISSION)->get_handle() : 0;
|
||||||
|
|
||||||
material_data.push_back(data);
|
material_data.push_back(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "scene/material.h"
|
#include "scene/material.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
||||||
|
|
@ -9,8 +10,7 @@ Material::Material()
|
||||||
, roughness_(0.5f)
|
, roughness_(0.5f)
|
||||||
, ior_(1.5f)
|
, ior_(1.5f)
|
||||||
, type_(MaterialType::DIFFUSE)
|
, type_(MaterialType::DIFFUSE)
|
||||||
, albedo_texture_(nullptr)
|
, textures_() {
|
||||||
, normal_texture_(nullptr) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Material::~Material() {
|
Material::~Material() {
|
||||||
|
|
@ -41,11 +41,31 @@ void Material::set_type(MaterialType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::set_albedo_texture(std::shared_ptr<Texture> texture) {
|
void Material::set_albedo_texture(std::shared_ptr<Texture> texture) {
|
||||||
albedo_texture_ = texture;
|
textures_[static_cast<int>(TextureSlot::ALBEDO)] = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::set_normal_texture(std::shared_ptr<Texture> texture) {
|
void Material::set_normal_texture(std::shared_ptr<Texture> texture) {
|
||||||
normal_texture_ = texture;
|
textures_[static_cast<int>(TextureSlot::NORMAL)] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::set_metallic_texture(std::shared_ptr<Texture> texture) {
|
||||||
|
textures_[static_cast<int>(TextureSlot::METALLIC)] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::set_roughness_texture(std::shared_ptr<Texture> texture) {
|
||||||
|
textures_[static_cast<int>(TextureSlot::ROUGHNESS)] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::set_ao_texture(std::shared_ptr<Texture> texture) {
|
||||||
|
textures_[static_cast<int>(TextureSlot::AO)] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::set_emission_texture(std::shared_ptr<Texture> texture) {
|
||||||
|
textures_[static_cast<int>(TextureSlot::EMISSION)] = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::set_texture(TextureSlot slot, std::shared_ptr<Texture> texture) {
|
||||||
|
textures_[static_cast<int>(slot)] = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "scene/mesh.h"
|
#include "scene/mesh.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
namespace are {
|
namespace are {
|
||||||
|
|
@ -96,7 +97,8 @@ bool Mesh::upload_to_gpu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::release_gpu_resources() {
|
void Mesh::release_gpu_resources() {
|
||||||
if (!uploaded_) return;
|
if (!uploaded_)
|
||||||
|
return;
|
||||||
|
|
||||||
if (vao_ != 0) {
|
if (vao_ != 0) {
|
||||||
glDeleteVertexArrays(1, &vao_);
|
glDeleteVertexArrays(1, &vao_);
|
||||||
|
|
@ -116,4 +118,53 @@ void Mesh::release_gpu_resources() {
|
||||||
uploaded_ = false;
|
uploaded_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mesh::compute_tangents() {
|
||||||
|
if (vertices_.empty() || indices_.empty()) {
|
||||||
|
ARE_LOG_WARN("Cannot compute tangents: mesh is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fill(vertices_.begin(), vertices_.end(), Vertex {});
|
||||||
|
|
||||||
|
for (size_t i = 0; i < indices_.size(); i += 3) {
|
||||||
|
uint i0 = indices_[i];
|
||||||
|
uint i1 = indices_[i + 1];
|
||||||
|
uint i2 = indices_[i + 2];
|
||||||
|
|
||||||
|
Vertex &v0 = vertices_[i0];
|
||||||
|
Vertex &v1 = vertices_[i1];
|
||||||
|
Vertex &v2 = vertices_[i2];
|
||||||
|
|
||||||
|
Vec3 pos0 = v0.position_;
|
||||||
|
Vec3 pos1 = v1.position_;
|
||||||
|
Vec3 pos2 = v2.position_;
|
||||||
|
|
||||||
|
Vec2 uv0 = v0.texcoord_;
|
||||||
|
Vec2 uv1 = v1.texcoord_;
|
||||||
|
Vec2 uv2 = v2.texcoord_;
|
||||||
|
|
||||||
|
Vec3 edge1 = pos1 - pos0;
|
||||||
|
Vec3 edge2 = pos2 - pos0;
|
||||||
|
Vec2 delta_uv1 = uv1 - uv0;
|
||||||
|
Vec2 delta_uv2 = uv2 - uv0;
|
||||||
|
|
||||||
|
float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y);
|
||||||
|
if (std::abs(r) < 1e-6f)
|
||||||
|
r = 1.0f;
|
||||||
|
|
||||||
|
Vec3 tangent = (edge1 * delta_uv2.y - edge2 * delta_uv1.y) * r;
|
||||||
|
tangent = glm::normalize(tangent - v0.normal_ * glm::dot(tangent, v0.normal_));
|
||||||
|
|
||||||
|
v0.tangent_ += tangent;
|
||||||
|
v1.tangent_ += tangent;
|
||||||
|
v2.tangent_ += tangent;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &v : vertices_) {
|
||||||
|
v.tangent_ = glm::normalize(v.tangent_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARE_LOG_INFO("Computed tangents for mesh");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace are
|
} // namespace are
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue