Fix: 修改raytracing.comp部分细节
parent
5826c8c729
commit
03baf12976
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
// }
|
||||
|
|
|
|||
Loading…
Reference in New Issue