Fix: 修改raytracing.comp部分细节

master
ternaryop8479 2026-02-10 15:01:47 +08:00
parent 5826c8c729
commit 03baf12976
2 changed files with 533 additions and 353 deletions

3
.gitignore vendored
View File

@ -1,6 +1,5 @@
# Build directories
build/
cmake-build-*/
# IDE files
.vscode/
@ -26,7 +25,6 @@ cmake_install.cmake
Makefile
# Output files
output/
*.ppm
*.bmp
*.png
@ -38,4 +36,3 @@ Thumbs.db
# Experiments (optional, can be tracked)
experiments/*
!experiments/.gitkeep

View File

@ -7,6 +7,7 @@
#define MAX_FLOAT 3.402823466e38
#define MAX_RAY_DEPTH 8
#define MAX_LIGHTS 16
#define RR_THRESHOLD 0.1
// Material types
#define MATERIAL_DIFFUSE 0
@ -37,7 +38,7 @@ struct Light {
float intensity;
vec3 color;
float range;
vec2 spot_angles; // inner, outer
vec2 spot_angles;
vec2 padding;
};
@ -55,97 +56,13 @@ struct HitInfo {
uint material_id;
};
// Utility functions
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
struct ScatterResult {
bool scattered;
vec3 attenuation;
Ray scattered_ray;
float pdf;
};
vec3 saturate(vec3 x) {
return clamp(x, vec3(0.0), vec3(1.0));
}
// Random number generation (PCG Hash)
uint pcg_hash(uint seed) {
uint state = seed * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
float random_float(inout uint seed) {
seed = pcg_hash(seed);
return float(seed) / 4294967296.0;
}
vec2 random_vec2(inout uint seed) {
return vec2(random_float(seed), random_float(seed));
}
vec3 random_vec3(inout uint seed) {
return vec3(random_float(seed), random_float(seed), random_float(seed));
}
// Random direction in hemisphere
vec3 random_hemisphere_direction(vec3 normal, inout uint seed) {
float z = random_float(seed);
float r = sqrt(max(0.0, 1.0 - z * z));
float phi = 2.0 * PI * random_float(seed);
vec3 dir = vec3(r * cos(phi), r * sin(phi), z);
// Create coordinate system
vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, normal));
vec3 bitangent = cross(normal, tangent);
return normalize(tangent * dir.x + bitangent * dir.y + normal * dir.z);
}
// Cosine-weighted hemisphere sampling
// vec3 cosine_weighted_hemisphere(vec3 normal, inout uint seed) {
// vec2 r = random_vec2(seed);
// float r1 = 2.0 * PI * r.x;
// float r2 = r.y;
// float r2s = sqrt(r2);
//
// vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
// vec3 tangent = normalize(cross(up, normal));
// vec3 bitangent = cross(normal, tangent);
//
// vec3 dir = tangent * cos(r1) * r2s + bitangent * sin(r1) * r2s + normal * sqrt(1.0 - r2);
// return normalize(dir);
// }
// Schlick's approximation for Fresnel
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
}
// GGX distribution
float distribution_ggx(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, EPSILON);
}
// Smith's geometry function
float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float r = roughness + 1.0;
float k = (r * r) / 8.0;
float ggx1 = NdotV / (NdotV * (1.0 - k) + k);
float ggx2 = NdotL / (NdotL * (1.0 - k) + k);
return ggx1 * ggx2;
}
layout(local_size_x = 16, local_size_y = 16) in;
@ -177,11 +94,517 @@ uniform mat4 u_inv_view_projection;
uniform bool u_enable_accumulation;
uniform bool u_use_bvh;
// Evaluate direct lighting
vec3 evaluate_direct_lighting(vec3 position, vec3 normal, vec3 view_dir,
Material material, inout uint seed) {
// ============================================================================
// Utility Functions
// ============================================================================
/**
* @brief Saturate float value to [0, 1]
*/
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
/**
* @brief Saturate vec3 value to [0, 1]
*/
vec3 saturate(vec3 x) {
return clamp(x, vec3(0.0), vec3(1.0));
}
/**
* @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;
}
// ============================================================================
// Random Number Generation
// ============================================================================
/**
* @brief PCG hash function for random number generation
*/
uint pcg_hash(uint seed) {
uint state = seed * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
/**
* @brief Generate random float in [0, 1)
*/
float random_float(inout uint seed) {
seed = pcg_hash(seed);
return float(seed) / 4294967296.0;
}
/**
* @brief Generate random vec2
*/
vec2 random_vec2(inout uint seed) {
return vec2(random_float(seed), random_float(seed));
}
/**
* @brief Generate random vec3
*/
vec3 random_vec3(inout uint seed) {
return vec3(random_float(seed), random_float(seed), random_float(seed));
}
/**
* @brief Generate random vector in unit sphere
*/
vec3 random_in_unit_sphere(inout uint seed) {
while (true) {
vec3 p = 2.0 * random_vec3(seed) - vec3(1.0);
if (dot(p, p) < 1.0) return p;
}
}
/**
* @brief Generate random unit vector
*/
vec3 random_unit_vector(inout uint seed) {
return normalize(random_in_unit_sphere(seed));
}
// ============================================================================
// Sampling Functions
// ============================================================================
/**
* @brief Cosine-weighted hemisphere sampling
* @return Sampled direction in local space (z-up)
*/
vec3 cosine_weighted_hemisphere(inout uint seed) {
vec2 r = random_vec2(seed);
float phi = 2.0 * PI * r.x;
float cos_theta = sqrt(r.y);
float sin_theta = sqrt(1.0 - r.y);
return vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta);
}
/**
* @brief Build orthonormal basis from normal
*/
void build_onb(vec3 normal, out vec3 tangent, out vec3 bitangent) {
vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
tangent = normalize(cross(up, normal));
bitangent = cross(normal, tangent);
}
/**
* @brief Transform direction from local to world space
*/
vec3 local_to_world(vec3 local_dir, vec3 normal) {
vec3 tangent, bitangent;
build_onb(normal, tangent, bitangent);
return tangent * local_dir.x + bitangent * local_dir.y + normal * local_dir.z;
}
/**
* @brief Sample GGX distribution for microfacet normal
*/
vec3 sample_ggx(vec3 normal, float roughness, inout uint seed) {
vec2 r = random_vec2(seed);
float a = roughness * roughness;
float a2 = a * a;
float phi = 2.0 * PI * r.x;
float cos_theta = sqrt((1.0 - r.y) / (1.0 + (a2 - 1.0) * r.y));
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
vec3 local_h = vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta);
return local_to_world(local_h, normal);
}
// ============================================================================
// BRDF Functions
// ============================================================================
/**
* @brief Schlick's approximation for Fresnel reflectance
*/
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
}
/**
* @brief Fresnel reflectance for dielectrics
*/
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);
}
/**
* @brief GGX normal distribution function
*/
float distribution_ggx(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, EPSILON);
}
/**
* @brief Smith's geometry function for GGX
*/
float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float r = roughness + 1.0;
float k = (r * r) / 8.0;
float ggx1 = NdotV / (NdotV * (1.0 - k) + k);
float ggx2 = NdotL / (NdotL * (1.0 - k) + k);
return ggx1 * ggx2;
}
// ============================================================================
// Material Scattering
// ============================================================================
/**
* @brief Scatter ray for diffuse material
*/
ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
ScatterResult result;
result.scattered = true;
result.attenuation = mat.albedo;
// Cosine-weighted hemisphere sampling
vec3 local_dir = cosine_weighted_hemisphere(seed);
vec3 scatter_direction = local_to_world(local_dir, hit.normal);
// Prevent degenerate scatter direction
if (near_zero(scatter_direction)) {
scatter_direction = hit.normal;
}
result.scattered_ray.origin = hit.position + hit.normal * EPSILON;
result.scattered_ray.direction = normalize(scatter_direction);
result.pdf = max(dot(hit.normal, result.scattered_ray.direction), 0.0) * INV_PI;
return result;
}
/**
* @brief Scatter ray for metal material
*/
ScatterResult scatter_metal(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
ScatterResult result;
vec3 reflected = reflect_vector(normalize(ray_in.direction), hit.normal);
// Add roughness perturbation
vec3 fuzz = mat.roughness * random_in_unit_sphere(seed);
vec3 scatter_direction = reflected + fuzz;
result.scattered = dot(scatter_direction, hit.normal) > 0.0;
if (result.scattered) {
result.scattered_ray.origin = hit.position + hit.normal * EPSILON;
result.scattered_ray.direction = normalize(scatter_direction);
vec3 V = -normalize(ray_in.direction);
vec3 H = normalize(V + result.scattered_ray.direction);
vec3 F0 = mat.albedo;
result.attenuation = fresnel_schlick(max(dot(H, V), 0.0), F0);
result.pdf = 1.0; // Delta distribution approximation
} else {
result.attenuation = vec3(0.0);
result.pdf = 0.0;
}
return result;
}
/**
* @brief Scatter ray for dielectric material (glass)
*/
ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
ScatterResult result;
result.scattered = true;
result.attenuation = vec3(1.0);
float refraction_ratio = dot(ray_in.direction, hit.normal) < 0.0 ?
(1.0 / mat.ior) : mat.ior;
vec3 unit_direction = normalize(ray_in.direction);
float cos_theta = min(dot(-unit_direction, hit.normal), 1.0);
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
float reflectance = fresnel_dielectric(cos_theta, refraction_ratio);
vec3 direction;
if (cannot_refract || reflectance > random_float(seed)) {
direction = reflect_vector(unit_direction, hit.normal);
} else {
direction = refract_vector(unit_direction, hit.normal, refraction_ratio);
}
result.scattered_ray.origin = hit.position + direction * EPSILON;
result.scattered_ray.direction = normalize(direction);
result.pdf = 1.0; // Delta distribution
return result;
}
/**
* @brief Scatter ray based on material type
*/
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);
} else if (mat.type == MATERIAL_METAL) {
return scatter_metal(ray_in, hit, mat, seed);
} else if (mat.type == MATERIAL_DIELECTRIC) {
return scatter_dielectric(ray_in, hit, mat, seed);
} else {
// Emissive material doesn't scatter
ScatterResult result;
result.scattered = false;
result.attenuation = vec3(0.0);
result.pdf = 0.0;
return result;
}
}
// ============================================================================
// Scene Intersection (G-Buffer based)
// ============================================================================
/**
* @brief Trace ray against G-Buffer (single bounce only)
* @note This is a simplified version - full path tracing needs scene geometry
*/
HitInfo trace_ray_gbuffer(Ray ray, ivec2 pixel_coords) {
HitInfo hit;
// Read from G-Buffer at current pixel
vec4 position_data = imageLoad(g_position, pixel_coords);
vec4 normal_data = imageLoad(g_normal, pixel_coords);
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
if (position_data.w > 0.5) {
hit.hit = true;
hit.position = position_data.xyz;
hit.normal = normalize(normal_data.xyz);
hit.material_id = uint(albedo_data.a * 255.0 + 0.5);
hit.t = length(hit.position - ray.origin);
} else {
hit.hit = false;
hit.t = MAX_FLOAT;
}
return hit;
}
// ============================================================================
// Path Tracing Core
// ============================================================================
/**
* @brief Sample direct lighting from light sources
*/
vec3 sample_direct_lighting(vec3 position, vec3 normal, Material mat, inout uint seed) {
if (u_light_count == 0u) return vec3(0.0);
vec3 direct_light = vec3(0.0);
// Sample one random light (could be improved with MIS)
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
Light light = lights[light_idx];
vec3 light_dir;
float light_distance;
float pdf_light = 1.0 / float(u_light_count);
if (light.type == LIGHT_POINT) {
vec3 to_light = light.position - position;
light_distance = length(to_light);
light_dir = to_light / light_distance;
if (light_distance > light.range) return vec3(0.0);
float NdotL = max(dot(normal, light_dir), 0.0);
if (NdotL > 0.0) {
float attenuation = 1.0 / max(light_distance * light_distance, 0.01);
vec3 radiance = light.color * light.intensity * attenuation;
// Simple BRDF evaluation (diffuse)
direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light;
}
} else if (light.type == LIGHT_DIRECTIONAL) {
light_dir = normalize(-light.direction);
float NdotL = max(dot(normal, light_dir), 0.0);
if (NdotL > 0.0) {
vec3 radiance = light.color * light.intensity;
direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light;
}
}
return direct_light;
}
/**
* @brief Trace path and accumulate radiance
*/
vec3 trace_path(Ray initial_ray, ivec2 pixel_coords, inout uint seed) {
vec3 radiance = vec3(0.0);
vec3 throughput = vec3(1.0);
Ray current_ray = initial_ray;
uint mat_count = uint(materials.length());
for (uint depth = 0u; depth < u_max_depth; depth++) {
// Trace ray (only first bounce uses G-Buffer)
HitInfo hit;
if (depth == 0u) {
hit = trace_ray_gbuffer(current_ray, pixel_coords);
} else {
// For subsequent bounces, we can't trace without full scene geometry
// This is a limitation of G-Buffer based approach
// In a full path tracer, you'd trace against the actual scene here
hit.hit = false;
}
if (!hit.hit) {
// Hit sky/background
vec3 sky_color = vec3(0.1, 0.1, 0.15);
radiance += throughput * sky_color;
break;
}
// Get material
Material mat;
if (hit.material_id < mat_count) {
mat = materials[hit.material_id];
} else {
// Fallback material
mat.albedo = vec3(0.5);
mat.metallic = 0.0;
mat.roughness = 0.5;
mat.emission = vec3(0.0);
mat.type = MATERIAL_DIFFUSE;
mat.ior = 1.5;
}
// Add emission
radiance += throughput * mat.emission;
// Sample direct lighting (only for diffuse surfaces)
if (mat.type == MATERIAL_DIFFUSE && depth == 0u) {
radiance += throughput * sample_direct_lighting(hit.position, hit.normal, mat, seed);
}
// Scatter ray
ScatterResult scatter = scatter_ray(current_ray, hit, mat, seed);
if (!scatter.scattered || scatter.pdf < EPSILON) {
break;
}
// Update throughput
throughput *= scatter.attenuation;
// Russian roulette path termination
if (depth > 3u) {
float rr_probability = max(throughput.r, max(throughput.g, throughput.b));
if (rr_probability < RR_THRESHOLD || random_float(seed) > rr_probability) {
break;
}
throughput /= rr_probability;
}
// Continue with scattered ray
current_ray = scatter.scattered_ray;
// Safety check for throughput
if (all(lessThan(throughput, vec3(EPSILON)))) {
break;
}
}
return radiance;
}
/**
* @brief Enhanced direct lighting with G-Buffer
*/
vec3 render_direct_lighting(ivec2 pixel_coords, inout uint seed) {
// Read G-Buffer
vec4 position_data = imageLoad(g_position, pixel_coords);
vec4 normal_data = imageLoad(g_normal, pixel_coords);
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
if (position_data.w < 0.5) {
return vec3(0.1, 0.1, 0.15); // Sky
}
vec3 position = position_data.xyz;
vec3 normal = normalize(normal_data.xyz);
uint material_id = uint(albedo_data.a * 255.0 + 0.5);
// Get material
Material mat;
uint mat_count = uint(materials.length());
if (material_id < mat_count) {
mat = materials[material_id];
} else {
// Fallback: use G-Buffer albedo
mat.albedo = albedo_data.rgb;
mat.metallic = 0.0;
mat.roughness = 0.5;
mat.emission = vec3(0.0);
mat.type = MATERIAL_DIFFUSE;
mat.ior = 1.5;
}
vec3 color = vec3(0.0);
// Emission
color += mat.emission;
// View direction
vec3 view_dir = normalize(u_camera_position - position);
// Direct lighting from all lights
for (uint i = 0u; i < u_light_count; i++) {
Light light = lights[i];
vec3 light_dir;
@ -207,26 +630,31 @@ vec3 evaluate_direct_lighting(vec3 position, vec3 normal, vec3 view_dir,
if (NdotL > 0.0) {
vec3 H = normalize(view_dir + light_dir);
float NdotV = max(dot(normal, view_dir), 0.0);
float NdotH = max(dot(normal, H), 0.0);
float HdotV = max(dot(H, view_dir), 0.0);
// PBR lighting
vec3 F0 = mix(vec3(0.04), material.albedo, material.metallic);
vec3 F = fresnel_schlick(max(dot(H, view_dir), 0.0), F0);
float D = distribution_ggx(normal, H, max(material.roughness, 0.04));
float G = geometry_smith(normal, view_dir, light_dir, material.roughness);
// Cook-Torrance BRDF
vec3 F0 = mix(vec3(0.04), mat.albedo, mat.metallic);
vec3 F = fresnel_schlick(HdotV, F0);
float D = distribution_ggx(normal, H, max(mat.roughness, 0.04));
float G = geometry_smith(normal, view_dir, light_dir, mat.roughness);
vec3 numerator = D * G * F;
float denominator = 4.0 * NdotV * NdotL + EPSILON;
vec3 specular = numerator / denominator;
vec3 kS = F;
vec3 kD = (vec3(1.0) - kS) * (1.0 - material.metallic);
vec3 kD = (vec3(1.0) - kS) * (1.0 - mat.metallic);
vec3 radiance = light.color * light.intensity * attenuation;
direct_light += (kD * material.albedo * INV_PI + specular) * radiance * NdotL;
color += (kD * mat.albedo * INV_PI + specular) * radiance * NdotL;
}
}
return direct_light;
// Ambient occlusion approximation
color += mat.albedo * 0.03;
return color;
}
void main() {
@ -237,62 +665,11 @@ void main() {
return;
}
// Read G-Buffer
vec4 position_data = imageLoad(g_position, pixel_coords);
vec4 normal_data = imageLoad(g_normal, pixel_coords);
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
// Background
if (position_data.w < 0.5) {
vec3 background = vec3(0.1, 0.1, 0.15);
imageStore(output_image, pixel_coords, vec4(background, 1.0));
imageStore(accumulation_image, pixel_coords, vec4(background, 1.0));
return;
}
vec3 position = position_data.xyz;
vec3 normal = normalize(normal_data.xyz);
vec3 albedo = albedo_data.rgb;
// 关键修复从G-Buffer的alpha通道读取material_id
// 注意albedo_data是从RGBA8纹理读取的alpha值范围是[0,1]
// 我们需要将其转换回整数ID
uint material_id = uint(albedo_data.a * 255.0 + 0.5);
// Initialize random seed
uint seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x) + u_frame_count * 719393u;
uint base_seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x);
uint seed = base_seed + u_frame_count * 719393u;
vec3 color = vec3(0.0);
// Get material from buffer
Material material;
uint mat_count = uint(materials.length());
if (material_id < mat_count) {
// 从SSBO读取材质
material = materials[material_id];
} else {
// 使用G-Buffer中的albedo作为fallback
material.albedo = albedo;
material.metallic = 0.0;
material.roughness = 0.5;
material.emission = vec3(0.0);
material.type = MATERIAL_DIFFUSE;
material.ior = 1.5;
}
// Add emission
color += material.emission;
// Direct lighting
vec3 view_dir = normalize(u_camera_position - position);
if (u_light_count > 0u) {
color += evaluate_direct_lighting(position, normal, view_dir, material, seed);
}
// Ambient lighting
color += material.albedo * 0.05;
vec3 color = render_direct_lighting(pixel_coords, seed);
// Clamp
color = clamp(color, vec3(0.0), vec3(10.0));
@ -307,197 +684,3 @@ void main() {
imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
imageStore(output_image, pixel_coords, vec4(color, 1.0));
}
// layout(local_size_x = 16, local_size_y = 16) in;
//
// // G-Buffer inputs
// layout(binding = 0, rgba32f) uniform readonly image2D g_position;
// layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
// layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
//
// // Output
// layout(binding = 3, rgba32f) uniform image2D output_image;
// layout(binding = 4, rgba32f) uniform image2D accumulation_image;
//
// // Scene data
// layout(std430, binding = 0) readonly buffer MaterialBuffer {
// Material materials[];
// };
//
// layout(std430, binding = 1) readonly buffer LightBuffer {
// Light lights[];
// };
//
// // Uniforms
// uniform uint u_frame_count;
// uniform uint u_samples_per_pixel;
// uniform uint u_max_depth;
// uniform uint u_light_count;
// uniform vec3 u_camera_position;
// uniform mat4 u_inv_view_projection;
// uniform bool u_enable_accumulation;
// uniform bool u_use_bvh;
//
// // Cosine-weighted hemisphere sampling
// vec3 cosine_weighted_hemisphere(vec3 normal, inout uint seed) {
// vec2 r = random_vec2(seed);
// float r1 = 2.0 * PI * r.x;
// float r2 = r.y;
// float r2s = sqrt(r2);
//
// vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
// vec3 tangent = normalize(cross(up, normal));
// vec3 bitangent = cross(normal, tangent);
//
// vec3 dir = tangent * cos(r1) * r2s + bitangent * sin(r1) * r2s + normal * sqrt(1.0 - r2);
// return normalize(dir);
// }
//
// // Evaluate direct lighting
// vec3 evaluate_direct_lighting(vec3 position, vec3 normal, vec3 view_dir,
// Material material, inout uint seed) {
// vec3 direct_light = vec3(0.0);
//
// for (uint i = 0u; i < u_light_count; i++) {
// Light light = lights[i];
// vec3 light_dir;
// float light_distance;
// float attenuation = 1.0;
//
// if (light.type == LIGHT_POINT) {
// vec3 to_light = light.position - position;
// light_distance = length(to_light);
// light_dir = to_light / light_distance;
// attenuation = 1.0 / max(light_distance * light_distance, 0.01);
//
// if (light_distance > light.range) continue;
// } else if (light.type == LIGHT_DIRECTIONAL) {
// light_dir = normalize(-light.direction);
// light_distance = MAX_FLOAT;
// } else {
// continue;
// }
//
// float NdotL = max(dot(normal, light_dir), 0.0);
//
// if (NdotL > 0.0) {
// vec3 H = normalize(view_dir + light_dir);
// float NdotV = max(dot(normal, view_dir), 0.0);
//
// // PBR lighting
// vec3 F0 = mix(vec3(0.04), material.albedo, material.metallic);
// vec3 F = fresnel_schlick(max(dot(H, view_dir), 0.0), F0);
// float D = distribution_ggx(normal, H, max(material.roughness, 0.04));
// float G = geometry_smith(normal, view_dir, light_dir, material.roughness);
//
// vec3 numerator = D * G * F;
// float denominator = 4.0 * NdotV * NdotL + EPSILON;
// vec3 specular = numerator / denominator;
//
// vec3 kS = F;
// vec3 kD = (vec3(1.0) - kS) * (1.0 - material.metallic);
//
// vec3 radiance = light.color * light.intensity * attenuation;
// direct_light += (kD * material.albedo * INV_PI + specular) * radiance * NdotL;
// }
// }
//
// return direct_light;
// }
//
// // Trace indirect lighting (simple path tracing)
// vec3 trace_indirect(vec3 position, vec3 normal, Material material, inout uint seed) {
// vec3 color = vec3(0.0);
//
// // Sample random direction
// vec3 ray_dir = cosine_weighted_hemisphere(normal, seed);
//
// // Sky color based on direction
// float t = 0.5 * (ray_dir.y + 1.0);
// vec3 sky_color = mix(vec3(1.0), vec3(0.5, 0.7, 1.0), t) * 0.2;
//
// color = sky_color;
//
// return color;
// }
//
// void main() {
// ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
// ivec2 image_size = imageSize(output_image);
//
// if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) {
// return;
// }
//
// // Read G-Buffer
// vec4 position_data = imageLoad(g_position, pixel_coords);
// vec4 normal_data = imageLoad(g_normal, pixel_coords);
// vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
//
// // Check if this pixel has valid geometry
// if (position_data.w < 0.5) {
// vec3 background = vec3(0.1, 0.1, 0.15);
// imageStore(output_image, pixel_coords, vec4(background, 1.0));
// imageStore(accumulation_image, pixel_coords, vec4(background, 1.0));
// return;
// }
//
// vec3 position = position_data.xyz;
// vec3 normal = normalize(normal_data.xyz);
// vec3 albedo = albedo_data.rgb;
// uint material_id = floatBitsToUint(albedo_data.a);
//
// if (material_id >= 1000u) {
// material_id = 0u;
// }
//
// // Initialize random seed
// uint seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x) + u_frame_count * 719393u;
//
// vec3 color = vec3(0.0);
//
// // Get material
// Material material;
// if (material_id < uint(materials.length())) {
// material = materials[material_id];
// } else {
// material.albedo = albedo;
// material.metallic = 0.0;
// material.roughness = 0.5;
// material.emission = vec3(0.0);
// material.type = MATERIAL_DIFFUSE;
// material.ior = 1.5;
// }
//
// // Add emission
// color += material.emission;
//
// // Direct lighting
// vec3 view_dir = normalize(u_camera_position - position);
//
// if (u_light_count > 0u) {
// color += evaluate_direct_lighting(position, normal, view_dir, material, seed);
// }
//
// // Indirect lighting (path tracing) - THIS ADDS NOISE
// for (uint samp_idx = 0u; samp_idx < u_samples_per_pixel; samp_idx++) { // 修复: sample -> samp_idx
// vec3 indirect = trace_indirect(position, normal, material, seed);
// color += indirect * material.albedo * INV_PI;
// }
//
// // Ambient
// color += material.albedo * 0.02;
//
// // Clamp
// color = clamp(color, vec3(0.0), vec3(100.0));
//
// // Accumulation for denoising
// if (u_enable_accumulation && u_frame_count > 0u) {
// vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
// float weight = 1.0 / float(u_frame_count + 1u);
// color = mix(accumulated, color, weight);
// }
//
// imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
// imageStore(output_image, pixel_coords, vec4(color, 1.0));
// }