parent
9b63afd9ae
commit
fab45b52a3
|
|
@ -1,81 +0,0 @@
|
|||
#ifndef ARE_INCLUDE_RESOURCE_TEXTURE_ARRAY_H
|
||||
#define ARE_INCLUDE_RESOURCE_TEXTURE_ARRAY_H
|
||||
|
||||
#include "basic/types.h"
|
||||
#include <string>
|
||||
|
||||
namespace are {
|
||||
|
||||
/**
|
||||
* @brief 2D texture array wrapper for PBR textures
|
||||
*/
|
||||
class TextureArray {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct texture array
|
||||
*/
|
||||
TextureArray();
|
||||
|
||||
/**
|
||||
* @brief Destroy texture array
|
||||
*/
|
||||
~TextureArray();
|
||||
|
||||
TextureArray(const TextureArray&) = delete;
|
||||
TextureArray& operator=(const TextureArray&) = delete;
|
||||
|
||||
TextureArray(TextureArray&& other) noexcept;
|
||||
TextureArray& operator=(TextureArray&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Create empty texture array storage
|
||||
* @param width Layer width
|
||||
* @param height Layer height
|
||||
* @param layers Layer count
|
||||
* @param internal_format OpenGL internal format (e.g. GL_RGBA8, GL_RGBA16F)
|
||||
* @param srgb True if texture should be treated as sRGB (use GL_SRGB8_ALPHA8)
|
||||
* @return True on success
|
||||
*/
|
||||
bool create(uint width, uint height, uint layers, uint internal_format);
|
||||
|
||||
/**
|
||||
* @brief Upload one layer (expects RGBA8 data)
|
||||
* @param layer Layer index
|
||||
* @param data Pixel data pointer
|
||||
* @param width Data width
|
||||
* @param height Data height
|
||||
*/
|
||||
bool upload_rgba8(uint layer, const void* data, uint width, uint height);
|
||||
|
||||
/**
|
||||
* @brief Bind to texture unit
|
||||
*/
|
||||
void bind(uint unit) const;
|
||||
|
||||
/**
|
||||
* @brief Release OpenGL resources
|
||||
*/
|
||||
void release();
|
||||
|
||||
/**
|
||||
* @brief Get OpenGL handle
|
||||
*/
|
||||
TextureHandle get_handle() const { return handle_; }
|
||||
|
||||
uint get_width() const { return width_; }
|
||||
uint get_height() const { return height_; }
|
||||
uint get_layers() const { return layers_; }
|
||||
|
||||
bool is_valid() const { return handle_ != INVALID_HANDLE; }
|
||||
|
||||
private:
|
||||
TextureHandle handle_;
|
||||
uint width_;
|
||||
uint height_;
|
||||
uint layers_;
|
||||
uint internal_format_;
|
||||
};
|
||||
|
||||
} // namespace are
|
||||
|
||||
#endif // ARE_INCLUDE_RESOURCE_TEXTURE_ARRAY_H
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#ifndef ARE_INCLUDE_SCENE_PBR_MATERIAL_GPU_H
|
||||
#define ARE_INCLUDE_SCENE_PBR_MATERIAL_GPU_H
|
||||
|
||||
#include "basic/types.h"
|
||||
|
||||
namespace are {
|
||||
|
||||
/**
|
||||
* @brief PBR material feature flags
|
||||
*/
|
||||
enum class PbrMaterialFlags : uint {
|
||||
NONE = 0u,
|
||||
HAS_BASE_COLOR_TEX = 1u << 0,
|
||||
HAS_NORMAL_TEX = 1u << 1,
|
||||
HAS_METAL_ROUGH_TEX = 1u << 2,
|
||||
HAS_EMISSIVE_TEX = 1u << 3,
|
||||
DOUBLE_SIDED = 1u << 4,
|
||||
ALPHA_MASK = 1u << 5,
|
||||
ALPHA_BLEND = 1u << 6
|
||||
};
|
||||
|
||||
inline PbrMaterialFlags operator|(PbrMaterialFlags a, PbrMaterialFlags b) {
|
||||
return static_cast<PbrMaterialFlags>(static_cast<uint>(a) | static_cast<uint>(b));
|
||||
}
|
||||
|
||||
inline uint to_uint(PbrMaterialFlags f) {
|
||||
return static_cast<uint>(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GPU-friendly PBR material (std430 aligned by vec4/uvec4)
|
||||
* @note All fields are designed to be consumed by GLSL std430 without padding issues.
|
||||
*/
|
||||
struct PbrMaterialGpu {
|
||||
Vec4 base_color_factor_; ///< rgb = baseColor, a = alpha
|
||||
Vec4 emissive_factor_; ///< rgb = emissive, a = unused
|
||||
Vec4 mr_normal_flags_; ///< x=metallic, y=roughness, z=normal_scale, w=flags (uint bits in float)
|
||||
|
||||
// texture layer indices (per texture array). TEX_INVALID if none.
|
||||
glm::uvec4 tex0_; ///< x=baseColor, y=normal, z=metalRough, w=emissive
|
||||
glm::uvec4 tex1_; ///< reserved
|
||||
};
|
||||
|
||||
constexpr uint TEX_INVALID = 0xFFFFFFFFu;
|
||||
|
||||
} // namespace are
|
||||
|
||||
#endif // ARE_INCLUDE_SCENE_PBR_MATERIAL_GPU_H
|
||||
|
|
@ -6,6 +6,11 @@
|
|||
#define MAX_FLOAT 3.402823466e38
|
||||
#define RR_THRESHOLD 0.1
|
||||
|
||||
#define MATERIAL_DIFFUSE 0
|
||||
#define MATERIAL_METAL 1
|
||||
#define MATERIAL_DIELECTRIC 2
|
||||
#define MATERIAL_EMISSIVE 3
|
||||
|
||||
#define LIGHT_DIRECTIONAL 0
|
||||
#define LIGHT_POINT 1
|
||||
#define LIGHT_SPOT 2
|
||||
|
|
@ -25,14 +30,14 @@ layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
|
|||
layout(binding = 3, rgba32f) uniform image2D output_image;
|
||||
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||
|
||||
const uint TEX_INVALID = 4294967295u;
|
||||
|
||||
struct PbrMaterialGpu {
|
||||
vec4 base_color_factor; // rgb + alpha
|
||||
vec4 emissive_factor; // rgb
|
||||
vec4 mr_normal_flags; // x=metallic, y=roughness, z=normal_scale, w=flags (uint bits in float)
|
||||
uvec4 tex0; // baseColor, normal, metalRough, emissive (layer indices)
|
||||
uvec4 tex1;
|
||||
struct Material {
|
||||
vec3 albedo;
|
||||
float metallic;
|
||||
vec3 emission;
|
||||
float roughness;
|
||||
int type;
|
||||
float ior;
|
||||
vec2 padding;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
|
|
@ -82,7 +87,7 @@ struct TriangleGpu {
|
|||
vec4 uv2; // xy uv2
|
||||
};
|
||||
|
||||
layout(std430, binding = 0) readonly buffer MaterialBuffer { PbrMaterialGpu materials[]; };
|
||||
layout(std430, binding = 0) readonly buffer MaterialBuffer { Material materials[]; };
|
||||
layout(std430, binding = 1) readonly buffer LightBuffer { Light lights[]; };
|
||||
layout(std430, binding = 2) readonly buffer BVHNodeBuffer { BVHNodeGpu bvh_nodes[]; };
|
||||
layout(std430, binding = 3) readonly buffer TriangleBuffer { TriangleGpu bvh_tris[]; };
|
||||
|
|
@ -100,15 +105,32 @@ uniform uint u_bvh_node_count;
|
|||
// Utility
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Check if vector is near zero
|
||||
*/
|
||||
bool near_zero(vec3 v) {
|
||||
return (abs(v.x) < EPSILON) && (abs(v.y) < EPSILON) && (abs(v.z) < EPSILON);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reflect vector around normal
|
||||
*/
|
||||
vec3 reflect_vector(vec3 v, vec3 n) {
|
||||
return v - 2.0 * dot(v, n) * n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Refract vector through surface
|
||||
*/
|
||||
vec3 refract_vector(vec3 uv, vec3 n, float etai_over_etat) {
|
||||
float cos_theta = min(dot(-uv, n), 1.0);
|
||||
vec3 r_out_perp = etai_over_etat * (uv + cos_theta * n);
|
||||
vec3 r_out_parallel = -sqrt(abs(1.0 - dot(r_out_perp, r_out_perp))) * n;
|
||||
return r_out_perp + r_out_parallel;
|
||||
}
|
||||
|
||||
uint as_uint(float f) { return floatBitsToUint(f); }
|
||||
float as_float(uint u) { return uintBitsToFloat(u); }
|
||||
|
||||
// ============================================================================
|
||||
// RNG (PCG)
|
||||
|
|
@ -144,8 +166,12 @@ vec3 random_unit_vector(inout uint seed) {
|
|||
// Camera ray
|
||||
// ============================================================================
|
||||
|
||||
Ray generate_camera_ray_center(ivec2 pixel_coords, ivec2 image_size) {
|
||||
vec2 uv = (vec2(pixel_coords) + vec2(0.5)) / vec2(image_size);
|
||||
/**
|
||||
* @brief Generate primary ray in world space
|
||||
*/
|
||||
Ray generate_camera_ray(ivec2 pixel_coords, ivec2 image_size, inout uint seed) {
|
||||
vec2 jitter = vec2(random_float(seed), random_float(seed));
|
||||
vec2 uv = (vec2(pixel_coords) + jitter) / vec2(image_size);
|
||||
vec2 ndc = uv * 2.0 - 1.0;
|
||||
|
||||
vec4 p_near = u_inv_view_projection * vec4(ndc, 0.0, 1.0);
|
||||
|
|
@ -163,6 +189,9 @@ Ray generate_camera_ray_center(ivec2 pixel_coords, ivec2 image_size) {
|
|||
// Intersection
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Ray-AABB intersection
|
||||
*/
|
||||
bool intersect_aabb(Ray ray, vec3 aabb_min, vec3 aabb_max, float t_max) {
|
||||
vec3 inv_d = 1.0 / ray.direction;
|
||||
vec3 t0 = (aabb_min - ray.origin) * inv_d;
|
||||
|
|
@ -177,6 +206,9 @@ bool intersect_aabb(Ray ray, vec3 aabb_min, vec3 aabb_max, float t_max) {
|
|||
return (tmax2 >= max(tmin, 0.0)) && (tmin <= t_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moller-Trumbore triangle intersection
|
||||
*/
|
||||
bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) {
|
||||
vec3 v0 = tri.v0_material.xyz;
|
||||
vec3 v1 = tri.v1.xyz;
|
||||
|
|
@ -219,12 +251,17 @@ bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BVH traversal (closest hit)
|
||||
*/
|
||||
HitInfo trace_ray_bvh(Ray ray) {
|
||||
HitInfo hit;
|
||||
hit.hit = false;
|
||||
hit.t = MAX_FLOAT;
|
||||
|
||||
if (!u_use_bvh || u_bvh_node_count == 0u) return hit;
|
||||
if (!u_use_bvh || u_bvh_node_count == 0u) {
|
||||
return hit;
|
||||
}
|
||||
|
||||
uint stack[64];
|
||||
int sp = 0;
|
||||
|
|
@ -258,6 +295,9 @@ HitInfo trace_ray_bvh(Ray ray) {
|
|||
return hit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Any-hit BVH for shadow ray
|
||||
*/
|
||||
bool trace_any_bvh(Ray ray, float t_max) {
|
||||
if (!u_use_bvh || u_bvh_node_count == 0u) return false;
|
||||
|
||||
|
|
@ -301,6 +341,10 @@ bool trace_any_bvh(Ray ray, float t_max) {
|
|||
// Primary-ray fast path via G-Buffer
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Read primary hit from G-Buffer if current pixel has geometry
|
||||
* @note Uses g_position.w as "valid" marker (your gbuffer writes 1.0 on hits, clear is 0).
|
||||
*/
|
||||
HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||
HitInfo hit;
|
||||
hit.hit = false;
|
||||
|
|
@ -311,47 +355,45 @@ HitInfo trace_primary_gbuffer(Ray ray, ivec2 pixel_coords) {
|
|||
hit.material_id = 0u;
|
||||
|
||||
vec4 pos = imageLoad(g_position, pixel_coords);
|
||||
if (pos.w <= 0.5) return hit;
|
||||
if (pos.w <= 0.5) {
|
||||
return hit;
|
||||
}
|
||||
|
||||
vec3 p = pos.xyz;
|
||||
vec3 n = normalize(imageLoad(g_normal, pixel_coords).xyz);
|
||||
|
||||
// integer material id
|
||||
uint mid = imageLoad(g_material_id, pixel_coords).r;
|
||||
|
||||
hit.hit = true;
|
||||
hit.position = pos.xyz;
|
||||
hit.normal = normalize(imageLoad(g_normal, pixel_coords).xyz);
|
||||
hit.material_id = imageLoad(g_material_id, pixel_coords).r;
|
||||
hit.position = p;
|
||||
hit.normal = n;
|
||||
hit.material_id = mid;
|
||||
|
||||
// For RR/any debug usage; path tracing uses this as starting point only.
|
||||
hit.t = length(p - ray.origin);
|
||||
|
||||
// Only for traversal cutoff, keep consistent
|
||||
hit.t = length(hit.position - ray.origin);
|
||||
return hit;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Material fetch
|
||||
// Material + scattering
|
||||
// ============================================================================
|
||||
|
||||
PbrMaterialGpu fetch_material(uint material_id) {
|
||||
uint cnt = uint(materials.length());
|
||||
if (material_id < cnt) return materials[material_id];
|
||||
|
||||
PbrMaterialGpu m;
|
||||
m.base_color_factor = vec4(0.5, 0.5, 0.5, 1.0);
|
||||
m.emissive_factor = vec4(0.0);
|
||||
m.mr_normal_flags = vec4(0.0, 0.5, 1.0, uintBitsToFloat(0u));
|
||||
m.tex0 = uvec4(TEX_INVALID);
|
||||
m.tex1 = uvec4(TEX_INVALID);
|
||||
return m;
|
||||
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
|
||||
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
|
||||
}
|
||||
|
||||
vec3 environment_color(vec3 dir) {
|
||||
return vec3(0.1, 0.1, 0.15);
|
||||
float fresnel_dielectric(float cos_theta, float ior) {
|
||||
float r0 = (1.0 - ior) / (1.0 + ior);
|
||||
r0 = r0 * r0;
|
||||
return r0 + (1.0 - r0) * pow(1.0 - cos_theta, 5.0);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Scattering (temporary, PBR v1 will be GGX sampling later)
|
||||
// ============================================================================
|
||||
|
||||
ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
||||
ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult r;
|
||||
r.scattered = true;
|
||||
r.attenuation = mat.base_color_factor.rgb;
|
||||
r.attenuation = mat.albedo;
|
||||
|
||||
vec3 dir = hit.normal + random_unit_vector(seed);
|
||||
if (near_zero(dir)) dir = hit.normal;
|
||||
|
|
@ -361,35 +403,61 @@ ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, PbrMaterialGpu mat, inout
|
|||
return r;
|
||||
}
|
||||
|
||||
ScatterResult scatter_metal_like(Ray ray_in, HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
||||
ScatterResult scatter_metal(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult r;
|
||||
|
||||
float roughness = clamp(mat.mr_normal_flags.y, 0.0, 1.0);
|
||||
vec3 reflected = reflect_vector(normalize(ray_in.direction), hit.normal);
|
||||
vec3 fuzz = roughness * random_in_unit_sphere(seed);
|
||||
vec3 fuzz = mat.roughness * random_in_unit_sphere(seed);
|
||||
vec3 dir = reflected + fuzz;
|
||||
|
||||
r.scattered = dot(dir, hit.normal) > 0.0;
|
||||
r.attenuation = mat.base_color_factor.rgb;
|
||||
r.attenuation = mat.albedo;
|
||||
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
r.scattered_ray.direction = normalize(dir);
|
||||
return r;
|
||||
}
|
||||
|
||||
ScatterResult scatter_pbr(Ray ray_in, HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
||||
// Simple blend by metallic (not physically accurate, but compiles & runs)
|
||||
float metallic = clamp(mat.mr_normal_flags.x, 0.0, 1.0);
|
||||
if (random_float(seed) < metallic) {
|
||||
return scatter_metal_like(ray_in, hit, mat, seed);
|
||||
ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult r;
|
||||
r.scattered = true;
|
||||
r.attenuation = vec3(1.0);
|
||||
|
||||
vec3 unit_dir = normalize(ray_in.direction);
|
||||
float cos_theta = min(dot(-unit_dir, hit.normal), 1.0);
|
||||
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);
|
||||
|
||||
vec3 dir;
|
||||
if (cannot_refract || random_float(seed) < reflect_prob) {
|
||||
dir = reflect_vector(unit_dir, hit.normal);
|
||||
} else {
|
||||
dir = refract_vector(unit_dir, hit.normal, refraction_ratio);
|
||||
}
|
||||
return scatter_diffuse(ray_in, hit, mat, seed);
|
||||
|
||||
r.scattered_ray.origin = hit.position + dir * EPSILON;
|
||||
r.scattered_ray.direction = normalize(dir);
|
||||
return r;
|
||||
}
|
||||
|
||||
ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
if (mat.type == MATERIAL_DIFFUSE) return scatter_diffuse(ray_in, hit, mat, seed);
|
||||
if (mat.type == MATERIAL_METAL) return scatter_metal(ray_in, hit, mat, seed);
|
||||
if (mat.type == MATERIAL_DIELECTRIC) return scatter_dielectric(ray_in, hit, mat, seed);
|
||||
|
||||
ScatterResult r;
|
||||
r.scattered = false;
|
||||
r.attenuation = vec3(0.0);
|
||||
return r;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Direct lighting (simple diffuse only, temporary)
|
||||
// Direct lighting (with shadow ray)
|
||||
// ============================================================================
|
||||
|
||||
vec3 eval_direct_lighting(HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
||||
vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) {
|
||||
if (u_light_count == 0u) return vec3(0.0);
|
||||
|
||||
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
|
||||
|
|
@ -425,7 +493,7 @@ vec3 eval_direct_lighting(HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
|||
if (trace_any_bvh(shadow_ray, t_max)) return vec3(0.0);
|
||||
|
||||
float pdf_light = 1.0 / float(u_light_count);
|
||||
vec3 brdf = mat.base_color_factor.rgb * INV_PI;
|
||||
vec3 brdf = mat.albedo * INV_PI;
|
||||
return brdf * radiance * n_dot_l / max(pdf_light, EPSILON);
|
||||
}
|
||||
|
||||
|
|
@ -433,42 +501,81 @@ vec3 eval_direct_lighting(HitInfo hit, PbrMaterialGpu mat, inout uint seed) {
|
|||
// Path tracing
|
||||
// ============================================================================
|
||||
|
||||
Material fetch_material(uint material_id) {
|
||||
uint cnt = uint(materials.length());
|
||||
if (material_id < cnt) return materials[material_id];
|
||||
|
||||
Material m;
|
||||
m.albedo = vec3(0.5);
|
||||
m.metallic = 0.0;
|
||||
m.emission = vec3(0.0);
|
||||
m.roughness = 0.5;
|
||||
m.type = MATERIAL_DIFFUSE;
|
||||
m.ior = 1.5;
|
||||
return m;
|
||||
}
|
||||
|
||||
vec3 environment_color(vec3 dir) {
|
||||
return vec3(0.1, 0.1, 0.15);
|
||||
}
|
||||
|
||||
Ray generate_camera_ray_center(ivec2 pixel_coords, ivec2 image_size) {
|
||||
vec2 uv = (vec2(pixel_coords) + vec2(0.5)) / vec2(image_size);
|
||||
vec2 ndc = uv * 2.0 - 1.0;
|
||||
|
||||
vec4 p_near = u_inv_view_projection * vec4(ndc, 0.0, 1.0);
|
||||
vec4 p_far = u_inv_view_projection * vec4(ndc, 1.0, 1.0);
|
||||
vec3 near_ws = p_near.xyz / p_near.w;
|
||||
vec3 far_ws = p_far.xyz / p_far.w;
|
||||
|
||||
Ray r;
|
||||
r.origin = near_ws;
|
||||
r.direction = normalize(far_ws - near_ws);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Trace path with primary-ray G-Buffer acceleration
|
||||
*/
|
||||
vec3 trace_path_primary_gbuffer(ivec2 pixel_coords, ivec2 image_size, inout uint seed) {
|
||||
Ray ray = generate_camera_ray_center(pixel_coords, image_size);
|
||||
|
||||
vec3 radiance = vec3(0.0);
|
||||
vec3 throughput = vec3(1.0);
|
||||
|
||||
// Depth 0: try G-Buffer hit first
|
||||
HitInfo hit0 = trace_primary_gbuffer(ray, pixel_coords);
|
||||
uint depth_start = 0u;
|
||||
|
||||
if (hit0.hit) {
|
||||
PbrMaterialGpu mat0 = fetch_material(hit0.material_id);
|
||||
Material mat0 = fetch_material(hit0.material_id);
|
||||
|
||||
radiance += throughput * mat0.emissive_factor.rgb;
|
||||
radiance += throughput * mat0.emission;
|
||||
if (mat0.type == MATERIAL_DIFFUSE) {
|
||||
radiance += throughput * eval_direct_lighting(hit0, mat0, seed);
|
||||
}
|
||||
|
||||
ScatterResult sc0 = scatter_pbr(ray, hit0, mat0, seed);
|
||||
ScatterResult sc0 = scatter_ray(ray, hit0, mat0, seed);
|
||||
if (!sc0.scattered) return radiance;
|
||||
|
||||
throughput *= sc0.attenuation;
|
||||
ray = sc0.scattered_ray;
|
||||
depth_start = 1u;
|
||||
}
|
||||
|
||||
for (uint depth = depth_start; depth < u_max_depth; ++depth) {
|
||||
// Subsequent bounces: BVH
|
||||
for (uint depth = (hit0.hit ? 1u : 0u); depth < u_max_depth; ++depth) {
|
||||
HitInfo hit = trace_ray_bvh(ray);
|
||||
if (!hit.hit) {
|
||||
radiance += throughput * environment_color(ray.direction);
|
||||
break;
|
||||
}
|
||||
|
||||
PbrMaterialGpu mat = fetch_material(hit.material_id);
|
||||
Material mat = fetch_material(hit.material_id);
|
||||
|
||||
radiance += throughput * mat.emissive_factor.rgb;
|
||||
radiance += throughput * mat.emission;
|
||||
if (mat.type == MATERIAL_DIFFUSE) {
|
||||
radiance += throughput * eval_direct_lighting(hit, mat, seed);
|
||||
}
|
||||
|
||||
ScatterResult sc = scatter_pbr(ray, hit, mat, seed);
|
||||
ScatterResult sc = scatter_ray(ray, hit, mat, seed);
|
||||
if (!sc.scattered) break;
|
||||
|
||||
throughput *= sc.attenuation;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "core/raytracer.h"
|
||||
#include "scene/pbr_material_gpu.h"
|
||||
#include "basic/constants.h"
|
||||
#include "utils/logger.h"
|
||||
#include <glad/glad.h>
|
||||
|
|
@ -245,32 +244,50 @@ void RayTracer::set_config(const RayTracerConfig &config) {
|
|||
}
|
||||
|
||||
void RayTracer::upload_scene_data_(const Scene &scene) {
|
||||
// Upload PBR materials (temporary: texture indices = TEX_INVALID)
|
||||
// Upload materials (on change only)
|
||||
const auto &materials = scene.get_materials();
|
||||
if (!materials.empty()) {
|
||||
std::vector<PbrMaterialGpu> material_data;
|
||||
struct MaterialData {
|
||||
Vec3 albedo;
|
||||
float metallic;
|
||||
Vec3 emission;
|
||||
float roughness;
|
||||
int type;
|
||||
float ior;
|
||||
Vec2 padding;
|
||||
};
|
||||
|
||||
std::vector<MaterialData> material_data;
|
||||
material_data.reserve(materials.size());
|
||||
|
||||
for (const auto &mat : materials) {
|
||||
PbrMaterialGpu m{};
|
||||
m.base_color_factor_ = Vec4(mat->get_albedo(), 1.0f);
|
||||
m.emissive_factor_ = Vec4(mat->get_emission(), 0.0f);
|
||||
|
||||
// Pack flags into float bits (w)
|
||||
uint flags = 0u;
|
||||
m.mr_normal_flags_ = Vec4(mat->get_metallic(), mat->get_roughness(), 1.0f, glm::uintBitsToFloat(flags));
|
||||
|
||||
m.tex0_ = glm::uvec4(TEX_INVALID, TEX_INVALID, TEX_INVALID, TEX_INVALID);
|
||||
m.tex1_ = glm::uvec4(TEX_INVALID, TEX_INVALID, TEX_INVALID, TEX_INVALID);
|
||||
|
||||
material_data.push_back(m);
|
||||
MaterialData data {};
|
||||
data.albedo = mat->get_albedo();
|
||||
data.metallic = mat->get_metallic();
|
||||
data.emission = mat->get_emission();
|
||||
data.roughness = mat->get_roughness();
|
||||
data.type = static_cast<int>(mat->get_type());
|
||||
data.ior = mat->get_ior();
|
||||
material_data.push_back(data);
|
||||
}
|
||||
|
||||
uint h = fnv1a_hash_bytes(material_data.data(), material_data.size() * sizeof(MaterialData));
|
||||
if (h != materials_hash_) {
|
||||
materials_hash_ = h;
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER,
|
||||
material_data.size() * sizeof(PbrMaterialGpu),
|
||||
material_data.size() * sizeof(MaterialData),
|
||||
material_data.data(), GL_DYNAMIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
|
||||
|
||||
reset_accumulation(); // materials changed => invalidate accumulation
|
||||
} else {
|
||||
// Still ensure bound (in case other code changed bindings)
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_);
|
||||
}
|
||||
} else {
|
||||
materials_hash_ = 0u;
|
||||
}
|
||||
|
||||
// Upload lights (on change only)
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
#include "resource/texture_array.h"
|
||||
#include "utils/logger.h"
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace are {
|
||||
|
||||
TextureArray::TextureArray()
|
||||
: handle_(INVALID_HANDLE)
|
||||
, width_(0)
|
||||
, height_(0)
|
||||
, layers_(0)
|
||||
, internal_format_(0) {
|
||||
}
|
||||
|
||||
TextureArray::~TextureArray() {
|
||||
release();
|
||||
}
|
||||
|
||||
TextureArray::TextureArray(TextureArray&& other) noexcept
|
||||
: handle_(other.handle_)
|
||||
, width_(other.width_)
|
||||
, height_(other.height_)
|
||||
, layers_(other.layers_)
|
||||
, internal_format_(other.internal_format_) {
|
||||
other.handle_ = INVALID_HANDLE;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
other.layers_ = 0;
|
||||
other.internal_format_ = 0;
|
||||
}
|
||||
|
||||
TextureArray& TextureArray::operator=(TextureArray&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
release();
|
||||
handle_ = other.handle_;
|
||||
width_ = other.width_;
|
||||
height_ = other.height_;
|
||||
layers_ = other.layers_;
|
||||
internal_format_ = other.internal_format_;
|
||||
other.handle_ = INVALID_HANDLE;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
other.layers_ = 0;
|
||||
other.internal_format_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool TextureArray::create(uint width, uint height, uint layers, uint internal_format) {
|
||||
release();
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
layers_ = layers;
|
||||
internal_format_ = internal_format;
|
||||
|
||||
glGenTextures(1, &handle_);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handle_);
|
||||
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, internal_format_, width_, height_, layers_);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureArray::upload_rgba8(uint layer, const void* data, uint width, uint height) {
|
||||
if (!is_valid()) {
|
||||
ARE_LOG_ERROR("TextureArray upload on invalid handle");
|
||||
return false;
|
||||
}
|
||||
if (layer >= layers_) {
|
||||
ARE_LOG_ERROR("TextureArray layer out of range");
|
||||
return false;
|
||||
}
|
||||
if (width != width_ || height != height_) {
|
||||
ARE_LOG_WARN("TextureArray upload size mismatch (resizing not implemented yet)");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handle_);
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
|
||||
0, 0, 0, static_cast<int>(layer),
|
||||
width_, height_, 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureArray::bind(uint unit) const {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handle_);
|
||||
}
|
||||
|
||||
void TextureArray::release() {
|
||||
if (handle_ != INVALID_HANDLE) {
|
||||
glDeleteTextures(1, &handle_);
|
||||
handle_ = INVALID_HANDLE;
|
||||
}
|
||||
width_ = height_ = layers_ = 0;
|
||||
internal_format_ = 0;
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
Loading…
Reference in New Issue