504 lines
16 KiB
Plaintext
504 lines
16 KiB
Plaintext
#version 430 core
|
||
|
||
// Constants
|
||
#define PI 3.14159265359
|
||
#define INV_PI 0.31830988618
|
||
#define EPSILON 1e-4
|
||
#define MAX_FLOAT 3.402823466e38
|
||
#define MAX_RAY_DEPTH 8
|
||
#define MAX_LIGHTS 16
|
||
|
||
// Material types
|
||
#define MATERIAL_DIFFUSE 0
|
||
#define MATERIAL_METAL 1
|
||
#define MATERIAL_DIELECTRIC 2
|
||
#define MATERIAL_EMISSIVE 3
|
||
|
||
// Light types
|
||
#define LIGHT_DIRECTIONAL 0
|
||
#define LIGHT_POINT 1
|
||
#define LIGHT_SPOT 2
|
||
|
||
// Structures
|
||
struct Material {
|
||
vec3 albedo;
|
||
float metallic;
|
||
vec3 emission;
|
||
float roughness;
|
||
int type;
|
||
float ior;
|
||
vec2 padding;
|
||
};
|
||
|
||
struct Light {
|
||
vec3 position;
|
||
int type;
|
||
vec3 direction;
|
||
float intensity;
|
||
vec3 color;
|
||
float range;
|
||
vec2 spot_angles; // inner, outer
|
||
vec2 padding;
|
||
};
|
||
|
||
struct Ray {
|
||
vec3 origin;
|
||
vec3 direction;
|
||
};
|
||
|
||
struct HitInfo {
|
||
bool hit;
|
||
float t;
|
||
vec3 position;
|
||
vec3 normal;
|
||
vec2 texcoord;
|
||
uint material_id;
|
||
};
|
||
|
||
// Utility functions
|
||
float saturate(float x) {
|
||
return clamp(x, 0.0, 1.0);
|
||
}
|
||
|
||
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;
|
||
|
||
// 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;
|
||
|
||
// 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;
|
||
}
|
||
|
||
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);
|
||
|
||
// 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;
|
||
|
||
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;
|
||
|
||
// Clamp
|
||
color = clamp(color, vec3(0.0), vec3(10.0));
|
||
|
||
// Accumulation
|
||
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));
|
||
}
|
||
|
||
// 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));
|
||
// }
|