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 <scene/scene.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/mesh.h>
|
||||
#include <scene/material.h>
|
||||
#include <scene/light.h>
|
||||
#include <utils/logger.h>
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.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;
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ const uint WINDOW_WIDTH = 800;
|
|||
const uint WINDOW_HEIGHT = 800;
|
||||
|
||||
// Global state
|
||||
GLFWwindow* g_window = nullptr;
|
||||
GLFWwindow *g_window = nullptr;
|
||||
std::unique_ptr<Renderer> g_renderer = nullptr;
|
||||
std::unique_ptr<Scene> g_scene = nullptr;
|
||||
std::shared_ptr<Camera> g_camera = nullptr; // Keep a direct reference to camera
|
||||
|
|
@ -47,23 +47,23 @@ float g_deltaTime = 0.0f;
|
|||
float g_lastFrame = 0.0f;
|
||||
|
||||
// GLFW error callback
|
||||
void glfw_error_callback(int error, const char* description) {
|
||||
void glfw_error_callback(int error, const char *description) {
|
||||
ARE_LOG_ERROR("GLFW Error " + std::to_string(error) + ": " + std::string(description));
|
||||
}
|
||||
|
||||
/// @brief Create a quad mesh
|
||||
std::shared_ptr<Mesh> create_quad(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3,
|
||||
const Vec3& normal, uint material_id) {
|
||||
std::shared_ptr<Mesh> create_quad(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3,
|
||||
const Vec3 &normal, uint material_id) {
|
||||
auto mesh = std::make_shared<Mesh>();
|
||||
|
||||
std::vector<Vertex> vertices = {
|
||||
{v0, normal, Vec2(0.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f)},
|
||||
{v1, normal, Vec2(1.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f)},
|
||||
{v2, normal, Vec2(1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f)},
|
||||
{v3, normal, Vec2(0.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f)}
|
||||
{ v0, normal, Vec2(0.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f) },
|
||||
{ v1, normal, Vec2(1.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f) },
|
||||
{ v2, normal, Vec2(1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f) },
|
||||
{ v3, normal, Vec2(0.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f) }
|
||||
};
|
||||
|
||||
std::vector<uint> indices = {0, 1, 2, 0, 2, 3};
|
||||
std::vector<uint> indices = { 0, 1, 2, 0, 2, 3 };
|
||||
|
||||
mesh->set_vertices(vertices);
|
||||
mesh->set_indices(indices);
|
||||
|
|
@ -73,45 +73,45 @@ std::shared_ptr<Mesh> create_quad(const Vec3& v0, const Vec3& v1, const Vec3& v2
|
|||
}
|
||||
|
||||
/// @brief Create a box mesh
|
||||
std::shared_ptr<Mesh> create_box(const Vec3& min, const Vec3& max, uint material_id) {
|
||||
std::shared_ptr<Mesh> create_box(const Vec3 &min, const Vec3 &max, uint material_id) {
|
||||
auto mesh = std::make_shared<Mesh>();
|
||||
|
||||
std::vector<Vertex> vertices = {
|
||||
// Front face
|
||||
{{min.x, min.y, max.z}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, min.y, max.z}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, max.y, max.z}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{min.x, max.y, max.z}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{ { min.x, min.y, max.z }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, min.y, max.z }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, max.y, max.z }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { min.x, max.y, max.z }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
|
||||
// Back face
|
||||
{{max.x, min.y, min.z}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}},
|
||||
{{min.x, min.y, min.z}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}},
|
||||
{{min.x, max.y, min.z}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, max.y, min.z}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}},
|
||||
{ { max.x, min.y, min.z }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } },
|
||||
{ { min.x, min.y, min.z }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } },
|
||||
{ { min.x, max.y, min.z }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f }, { -1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, max.y, min.z }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f }, { -1.0f, 0.0f, 0.0f } },
|
||||
|
||||
// Top face
|
||||
{{min.x, max.y, max.z}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, max.y, max.z}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, max.y, min.z}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{min.x, max.y, min.z}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{ { min.x, max.y, max.z }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, max.y, max.z }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, max.y, min.z }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { min.x, max.y, min.z }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
|
||||
// Bottom face
|
||||
{{min.x, min.y, min.z}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, min.y, min.z}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{max.x, min.y, max.z}, {0.0f, -1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{min.x, min.y, max.z}, {0.0f, -1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{ { min.x, min.y, min.z }, { 0.0f, -1.0f, 0.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, min.y, min.z }, { 0.0f, -1.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { max.x, min.y, max.z }, { 0.0f, -1.0f, 0.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { min.x, min.y, max.z }, { 0.0f, -1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
|
||||
// Right face
|
||||
{{max.x, min.y, max.z}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
|
||||
{{max.x, min.y, min.z}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
|
||||
{{max.x, max.y, min.z}, {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}},
|
||||
{{max.x, max.y, max.z}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}},
|
||||
{ { max.x, min.y, max.z }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f } },
|
||||
{ { max.x, min.y, min.z }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f, -1.0f } },
|
||||
{ { max.x, max.y, min.z }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, -1.0f } },
|
||||
{ { max.x, max.y, max.z }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f }, { 0.0f, 0.0f, -1.0f } },
|
||||
|
||||
// Left face
|
||||
{{min.x, min.y, min.z}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{min.x, min.y, max.z}, {-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{min.x, max.y, max.z}, {-1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{min.x, max.y, min.z}, {-1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}
|
||||
{ { min.x, min.y, min.z }, { -1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||
{ { min.x, min.y, max.z }, { -1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||
{ { min.x, max.y, max.z }, { -1.0f, 0.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||
{ { min.x, max.y, min.z }, { -1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f } }
|
||||
};
|
||||
|
||||
std::vector<uint> indices = {
|
||||
|
|
@ -130,6 +130,53 @@ std::shared_ptr<Mesh> create_box(const Vec3& min, const Vec3& max, uint material
|
|||
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
|
||||
void setup_cornell_box() {
|
||||
g_scene = std::make_unique<Scene>();
|
||||
|
|
@ -164,10 +211,25 @@ void setup_cornell_box() {
|
|||
auto metal_material = std::make_shared<Material>();
|
||||
metal_material->set_albedo(Vec3(0.95f, 0.93f, 0.88f));
|
||||
metal_material->set_metallic(1.0f);
|
||||
metal_material->set_roughness(0.1f);
|
||||
metal_material->set_roughness(0.0f);
|
||||
metal_material->set_type(MaterialType::METAL);
|
||||
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)
|
||||
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(0.0f, 1.0f, 0.0f),
|
||||
white_id
|
||||
);
|
||||
white_id);
|
||||
floor->upload_to_gpu();
|
||||
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(0.0f, -1.0f, 0.0f),
|
||||
white_id
|
||||
);
|
||||
white_id);
|
||||
ceiling->upload_to_gpu();
|
||||
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(0.0f, 0.0f, 1.0f),
|
||||
white_id
|
||||
);
|
||||
white_id);
|
||||
back_wall->upload_to_gpu();
|
||||
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(1.0f, 0.0f, 0.0f),
|
||||
red_id
|
||||
);
|
||||
red_id);
|
||||
left_wall->upload_to_gpu();
|
||||
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(-1.0f, 0.0f, 0.0f),
|
||||
green_id
|
||||
);
|
||||
green_id);
|
||||
right_wall->upload_to_gpu();
|
||||
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(0.0f, -1.0f, 0.0f),
|
||||
light_id
|
||||
);
|
||||
light_id);
|
||||
area_light->upload_to_gpu();
|
||||
g_scene->add_mesh(area_light);
|
||||
|
||||
|
|
@ -250,10 +306,22 @@ void setup_cornell_box() {
|
|||
g_scene->add_mesh(tall_box);
|
||||
|
||||
// Short box (metal, right side)
|
||||
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), 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();
|
||||
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
|
||||
g_camera = std::make_shared<Camera>();
|
||||
g_camera->set_position(g_cameraPos);
|
||||
|
|
@ -346,13 +414,14 @@ void process_input() {
|
|||
g_pitch += yoffset;
|
||||
|
||||
// Constrain pitch
|
||||
if (g_pitch > 89.0f) g_pitch = 89.0f;
|
||||
if (g_pitch < -89.0f) g_pitch = -89.0f;
|
||||
if (g_pitch > 89.0f)
|
||||
g_pitch = 89.0f;
|
||||
if (g_pitch < -89.0f)
|
||||
g_pitch = -89.0f;
|
||||
|
||||
camera_changed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
g_firstMouse = true; // Reset when released
|
||||
}
|
||||
|
||||
|
|
@ -426,8 +495,7 @@ void render_loop() {
|
|||
|
||||
if (delta >= 1.0) {
|
||||
double fps = frame_count / delta;
|
||||
std::string title = "Aurora - Cornell Box | FPS: " + std::to_string((int)fps) +
|
||||
" | Frame: " + std::to_string((int)stats.frame_time_ms_) + "ms";
|
||||
std::string title = "Aurora - Cornell Box | FPS: " + std::to_string((int)fps) + " | Frame: " + std::to_string((int)stats.frame_time_ms_) + "ms";
|
||||
glfwSetWindowTitle(g_window, title.c_str());
|
||||
|
||||
frame_count = 0;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,22 @@
|
|||
|
||||
#include "basic/types.h"
|
||||
#include "resource/texture.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
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
|
||||
enum class MaterialType {
|
||||
DIFFUSE = 0,
|
||||
|
|
@ -72,6 +84,37 @@ public:
|
|||
*/
|
||||
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
|
||||
* @return Albedo color
|
||||
|
|
@ -125,7 +168,7 @@ public:
|
|||
* @return Albedo texture (nullptr if none)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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:
|
||||
|
|
@ -144,8 +237,7 @@ private:
|
|||
float ior_;
|
||||
MaterialType type_;
|
||||
|
||||
std::shared_ptr<Texture> albedo_texture_;
|
||||
std::shared_ptr<Texture> normal_texture_;
|
||||
std::array<std::shared_ptr<Texture>, static_cast<int>(TextureSlot::COUNT)> textures_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define ARE_INCLUDE_SCENE_MESH_H
|
||||
|
||||
#include "basic/types.h"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace are {
|
||||
|
|
@ -39,6 +40,14 @@ public:
|
|||
*/
|
||||
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
|
||||
* @return Vertex array
|
||||
|
|
@ -77,6 +86,12 @@ public:
|
|||
*/
|
||||
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
|
||||
void release_gpu_resources();
|
||||
|
||||
|
|
|
|||
|
|
@ -32,12 +32,14 @@ layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
|||
|
||||
struct Material {
|
||||
vec3 albedo;
|
||||
float metallic;
|
||||
vec3 emission;
|
||||
float metallic;
|
||||
float roughness;
|
||||
int type;
|
||||
float ior;
|
||||
vec2 padding;
|
||||
float padding1;
|
||||
float padding2;
|
||||
uint texture_handles[6];
|
||||
};
|
||||
|
||||
struct Light {
|
||||
|
|
@ -63,6 +65,7 @@ struct HitInfo {
|
|||
vec3 normal;
|
||||
vec2 texcoord;
|
||||
uint material_id;
|
||||
int material_type; // material type from G-Buffer
|
||||
};
|
||||
|
||||
struct ScatterResult {
|
||||
|
|
@ -100,6 +103,15 @@ uniform mat4 u_inv_view_projection;
|
|||
uniform bool u_enable_accumulation;
|
||||
uniform bool u_use_bvh;
|
||||
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
|
||||
|
|
@ -353,6 +365,7 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
hit.normal = vec3(0.0, 1.0, 0.0);
|
||||
hit.texcoord = vec2(0.0);
|
||||
hit.material_id = 0u;
|
||||
hit.material_type = 0;
|
||||
|
||||
vec4 pos = imageLoad(g_position, pixel_coords);
|
||||
if (pos.w <= 0.5) {
|
||||
|
|
@ -365,10 +378,15 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
// integer material id
|
||||
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.position = p;
|
||||
hit.normal = n;
|
||||
hit.material_id = mid;
|
||||
hit.material_type = mtype;
|
||||
|
||||
// For RR/any debug usage; path tracing uses this as starting point only.
|
||||
hit.t = length(p - ray.origin);
|
||||
|
|
@ -380,6 +398,54 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
// 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) {
|
||||
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);
|
||||
|
||||
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 refraction_ratio = dot(unit_dir, hit.normal) < 0.0 ? (1.0 / mat.ior) : mat.ior;
|
||||
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
|
||||
float reflect_prob = fresnel_dielectric(cos_theta, refraction_ratio);
|
||||
// Determine if ray is entering or exiting the material
|
||||
// If dot(dir, normal) < 0, ray is entering (from air into material)
|
||||
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;
|
||||
if (cannot_refract || random_float(seed) < reflect_prob) {
|
||||
dir = reflect_vector(unit_dir, hit.normal);
|
||||
if (total_internal_reflection || random_float(seed) < f) {
|
||||
// Reflect
|
||||
dir = reflect_vector(unit_dir, normal);
|
||||
} 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;
|
||||
|
|
@ -548,6 +633,16 @@ vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint
|
|||
if (hit0.hit) {
|
||||
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;
|
||||
if (mat0.type == MATERIAL_DIFFUSE) {
|
||||
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);
|
||||
|
||||
// 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;
|
||||
if (mat.type == MATERIAL_DIFFUSE) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 image_size = imageSize(output_image);
|
||||
|
|
@ -611,14 +721,20 @@ void main() {
|
|||
}
|
||||
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) {
|
||||
vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
|
||||
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));
|
||||
imageStore(output_image, pixel_coords, vec4(color, 1.0));
|
||||
// Apply ACES tone mapping to output (not accumulation)
|
||||
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_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
|
||||
const Camera &camera = scene.get_camera();
|
||||
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)
|
||||
const auto &materials = scene.get_materials();
|
||||
if (!materials.empty()) {
|
||||
// Aligned to match GLSL std430 layout (vec3 = vec4 = 16 bytes)
|
||||
struct MaterialData {
|
||||
Vec3 albedo;
|
||||
alignas(16) Vec3 albedo;
|
||||
alignas(16) Vec3 emission;
|
||||
float metallic;
|
||||
Vec3 emission;
|
||||
float roughness;
|
||||
int type;
|
||||
float ior;
|
||||
Vec2 padding;
|
||||
float padding1;
|
||||
float padding2;
|
||||
uint texture_handles[6];
|
||||
};
|
||||
|
||||
std::vector<MaterialData> material_data;
|
||||
|
|
@ -268,6 +300,15 @@ void RayTracer::upload_scene_data_(const Scene &scene) {
|
|||
data.roughness = mat->get_roughness();
|
||||
data.type = static_cast<int>(mat->get_type());
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "scene/material.h"
|
||||
#include <array>
|
||||
|
||||
namespace are {
|
||||
|
||||
|
|
@ -9,18 +10,17 @@ Material::Material()
|
|||
, roughness_(0.5f)
|
||||
, ior_(1.5f)
|
||||
, type_(MaterialType::DIFFUSE)
|
||||
, albedo_texture_(nullptr)
|
||||
, normal_texture_(nullptr) {
|
||||
, textures_() {
|
||||
}
|
||||
|
||||
Material::~Material() {
|
||||
}
|
||||
|
||||
void Material::set_albedo(const Vec3& albedo) {
|
||||
void Material::set_albedo(const Vec3 &albedo) {
|
||||
albedo_ = albedo;
|
||||
}
|
||||
|
||||
void Material::set_emission(const Vec3& emission) {
|
||||
void Material::set_emission(const Vec3 &emission) {
|
||||
emission_ = emission;
|
||||
}
|
||||
|
||||
|
|
@ -41,11 +41,31 @@ void Material::set_type(MaterialType type) {
|
|||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "scene/mesh.h"
|
||||
#include "utils/logger.h"
|
||||
#include <algorithm>
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace are {
|
||||
|
|
@ -17,12 +18,12 @@ Mesh::~Mesh() {
|
|||
release_gpu_resources();
|
||||
}
|
||||
|
||||
void Mesh::set_vertices(const std::vector<Vertex>& vertices) {
|
||||
void Mesh::set_vertices(const std::vector<Vertex> &vertices) {
|
||||
vertices_ = vertices;
|
||||
uploaded_ = false;
|
||||
}
|
||||
|
||||
void Mesh::set_indices(const std::vector<uint>& indices) {
|
||||
void Mesh::set_indices(const std::vector<uint> &indices) {
|
||||
indices_ = indices;
|
||||
uploaded_ = false;
|
||||
}
|
||||
|
|
@ -31,7 +32,7 @@ void Mesh::set_material(uint material_id) {
|
|||
material_id_ = material_id;
|
||||
}
|
||||
|
||||
void Mesh::set_transform(const Mat4& transform) {
|
||||
void Mesh::set_transform(const Mat4 &transform) {
|
||||
transform_ = transform;
|
||||
}
|
||||
|
||||
|
|
@ -71,22 +72,22 @@ bool Mesh::upload_to_gpu() {
|
|||
// Location 0: Position
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)offsetof(Vertex, position_));
|
||||
(void *)offsetof(Vertex, position_));
|
||||
|
||||
// Location 1: Normal
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)offsetof(Vertex, normal_));
|
||||
(void *)offsetof(Vertex, normal_));
|
||||
|
||||
// Location 2: TexCoord
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)offsetof(Vertex, texcoord_));
|
||||
(void *)offsetof(Vertex, texcoord_));
|
||||
|
||||
// Location 3: Tangent
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
(void*)offsetof(Vertex, tangent_));
|
||||
(void *)offsetof(Vertex, tangent_));
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
|
|
@ -96,7 +97,8 @@ bool Mesh::upload_to_gpu() {
|
|||
}
|
||||
|
||||
void Mesh::release_gpu_resources() {
|
||||
if (!uploaded_) return;
|
||||
if (!uploaded_)
|
||||
return;
|
||||
|
||||
if (vao_ != 0) {
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
|
|
@ -116,4 +118,53 @@ void Mesh::release_gpu_resources() {
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue