aurora-rendering-engine/shaders/raytracing/raytracing.comp

285 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#version 430 core
#include "../include/common.glsl"
#include "../include/structs.glsl"
#include "../include/math.glsl"
#include "../include/rng.glsl"
#include "../include/sobol.glsl"
#include "../include/sampling.glsl"
#include "../include/tonemap.glsl"
layout(local_size_x = 16, local_size_y = 16) in;
layout(binding = 0, rgba32f) uniform readonly image2D g_position;
layout(binding = 1, rg32f) uniform readonly image2D g_normal;
layout(binding = 5, rgba32f) uniform readonly image2D g_material;
layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
layout(binding = 2, rgba32f) uniform readonly image2D g_texcoord;
layout(binding = 7, rgba32f) uniform readonly image2D g_tangent;
layout(binding = 3, rgba32f) uniform image2D output_image;
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
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 {
TriangleCompactGpu bvh_tris[];
};
layout(std430, binding = 4) readonly buffer AttrBuffer {
TriangleAttrGpu bvh_attrs[];
};
uniform uint u_frame_count;
uniform uint u_samples_per_pixel;
uniform uint u_max_depth;
uniform uint u_light_count;
uniform mat4 u_inv_view_projection;
uniform bool u_enable_accumulation;
uniform bool u_use_bvh;
uniform uint u_bvh_node_count;
uniform bool u_enable_textures;
uniform uint u_sr_enabled;
uniform uint u_sr_scaling; // pixel ratio, e.g. 4 → 4× fewer pixels
uniform uint u_sr_block; // sqrt(scaling), block side length in pixels
uniform uint u_sr_jitter; // frame index within one jitter cycle (0 .. scaling-1)
uniform uint u_sr_full_width;
uniform uint u_sr_full_height;
layout(binding = 10) uniform sampler2DArray u_texture_albedo_array;
layout(binding = 11) uniform sampler2DArray u_texture_normal_array;
layout(binding = 12) uniform sampler2DArray u_texture_metallic_array;
layout(binding = 13) uniform sampler2DArray u_texture_roughness_array;
layout(binding = 14) uniform sampler2DArray u_texture_ao_array;
layout(binding = 15) uniform sampler2DArray u_texture_emission_array;
#include "../include/material.glsl"
#include "../include/bvh.glsl"
#include "../include/lighting.glsl"
struct SobolState {
uint sample_index;
uint dimension;
uint scramble;
};
SobolState init_sobol(uint pixel_index, uint frame, uint sample_idx) {
SobolState state;
state.sample_index = sample_idx + frame * 1024u + pixel_index + 1u;
state.dimension = 0u;
state.scramble = pcg_hash(pixel_index + frame * 668265263u);
return state;
}
float sobol_next(inout SobolState state) {
float value;
if (state.dimension < 16u) {
value = sobol_get(state.sample_index, state.dimension, state.scramble);
} else {
uint rng_state = pcg_hash(state.scramble + state.dimension * 2654435761u);
value = float(rng_state) / 4294967296.0;
}
state.dimension++;
return value;
}
vec3 sobol_ggx_half_vector(float roughness, vec3 N, inout SobolState state) {
float a = roughness * roughness;
float a2 = a * a;
float u1 = clamp(sobol_next(state), 0.001, 0.999);
float u2 = sobol_next(state);
float ct = sqrt((1.0 - u1) / ((a2 - 1.0) * u1 + 1.0));
ct = clamp(ct, 0.0, 1.0);
float st = sqrt(max(0.0, 1.0 - ct * ct));
float ph = 2.0 * PI * u2;
vec3 Ht = vec3(st * cos(ph), st * sin(ph), ct);
vec3 up = (abs(N.y) < 0.999) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 T = normalize(cross(up, N));
vec3 B = cross(N, T);
return mat3(T, B, N) * Ht;
}
ScatterResult scatter_diffuse_sobol(Ray ray_in, HitInfo hit, Material mat, inout SobolState state) {
ScatterResult r;
r.scattered = true;
r.attenuation = mat.albedo;
float rs = sqrt(sobol_next(state));
float ph = 2.0 * PI * sobol_next(state);
float x = rs * cos(ph), y = rs * sin(ph);
float z = sqrt(max(0.0, 1.0 - x * x - y * y));
vec3 up = (abs(hit.normal.y) < 0.999) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 T = normalize(cross(up, hit.normal));
vec3 B = cross(hit.normal, T);
vec3 dir = mat3(T, B, hit.normal) * vec3(x, y, z);
if (near_zero(dir)) dir = hit.normal;
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
r.scattered_ray.direction = dir;
return r;
}
ScatterResult scatter_metal_sobol(Ray ray_in, HitInfo hit, Material mat, inout SobolState state) {
ScatterResult r;
vec3 V = normalize(-ray_in.direction);
float roughness = clamp(mat.roughness, 0.04, 1.0);
vec3 H = sobol_ggx_half_vector(roughness, hit.normal, state);
if (dot(H, hit.normal) < 0.0) H = -H;
vec3 L = reflect(-V, H);
if (dot(hit.normal, L) <= 0.0) {
r.scattered = false;
r.attenuation = vec3(0.0);
return r;
}
float HdotV = max(dot(H, V), 0.001);
vec3 F = fresnel_schlick(HdotV, mat.albedo);
r.attenuation = clamp(F, vec3(0.0), vec3(1.0));
r.scattered = true;
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
r.scattered_ray.direction = L;
return r;
}
ScatterResult scatter_dielectric_sobol(Ray ray_in, HitInfo hit, Material mat, inout SobolState state) {
ScatterResult r;
r.scattered = true;
r.attenuation = vec3(1.0);
vec3 ud = normalize(ray_in.direction);
float ct = dot(-ud, hit.normal);
float st = sqrt(max(0.0, 1.0 - ct * ct));
bool ent = ct > 0.0;
float eta = ent ? (1.0 / mat.ior) : mat.ior;
vec3 N = ent ? hit.normal : -hit.normal;
float st_t = eta * st;
float f = fresnel_dielectric(ct, mat.ior);
vec3 dir;
if (st_t >= 1.0 || sobol_next(state) < f)
dir = reflect_vector(ud, N);
else
dir = refract_vector(ud, N, eta);
r.scattered_ray.origin = hit.position + dir * EPSILON;
r.scattered_ray.direction = dir;
return r;
}
ScatterResult scatter_ray_sobol(Ray ray_in, HitInfo hit, Material mat, inout SobolState state) {
if (mat.type == MATERIAL_DIFFUSE) return scatter_diffuse_sobol(ray_in, hit, mat, state);
if (mat.type == MATERIAL_METAL) return scatter_metal_sobol(ray_in, hit, mat, state);
if (mat.type == MATERIAL_DIELECTRIC) return scatter_dielectric_sobol(ray_in, hit, mat, state);
ScatterResult r;
r.scattered = false;
r.attenuation = vec3(0.0);
return r;
}
Ray generate_camera_ray(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 pn = u_inv_view_projection * vec4(ndc, 0.0, 1.0);
vec4 pf = u_inv_view_projection * vec4(ndc, 1.0, 1.0);
Ray r;
r.origin = pn.xyz / pn.w;
r.direction = normalize(pf.xyz / pf.w - r.origin);
return r;
}
vec3 trace_path_sobol(ivec2 pixel_coords, ivec2 image_size, inout SobolState sobol) {
Ray ray = generate_camera_ray(pixel_coords, image_size);
vec3 radiance = vec3(0.0);
vec3 throughput = vec3(1.0);
HitInfo hit0 = trace_primary_gbuffer(ray, pixel_coords);
if (hit0.hit) {
Material mat0 = fetch_material(hit0.material_id);
if (hit0.material_type >= 0) mat0.type = hit0.material_type;
apply_material_textures(mat0, hit0.normal, hit0.texcoord, hit0.tangent);
radiance += throughput * mat0.emission;
ScatterResult sc0 = scatter_ray_sobol(ray, hit0, mat0, sobol);
if (!sc0.scattered) return radiance;
throughput *= sc0.attenuation;
ray = sc0.scattered_ray;
}
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;
}
Material mat = fetch_material(hit.material_id);
apply_material_textures(mat, hit.normal, hit.texcoord, hit.tangent);
radiance += throughput * mat.emission;
ScatterResult sc = scatter_ray_sobol(ray, hit, mat, sobol);
if (!sc.scattered) break;
throughput *= sc.attenuation;
if (depth > 3u) {
float p = max(max(throughput.r, throughput.g), throughput.b);
p = clamp(p, 0.0, 0.95);
if (p < RR_THRESHOLD || sobol_next(sobol) > p) break;
throughput /= p;
}
ray = sc.scattered_ray;
if (all(lessThan(throughput, vec3(EPSILON)))) break;
}
return radiance;
}
void main() {
ivec2 rt_coord = ivec2(gl_GlobalInvocationID.xy);
ivec2 output_size = imageSize(output_image);
if (rt_coord.x >= output_size.x || rt_coord.y >= output_size.y) return;
ivec2 pixel_coords;
ivec2 image_size;
if (u_sr_enabled != 0u) {
uint jx = u_sr_jitter % u_sr_block;
uint jy = u_sr_jitter / u_sr_block;
pixel_coords = rt_coord * int(u_sr_block) + ivec2(int(jx), int(jy));
image_size = ivec2(int(u_sr_full_width), int(u_sr_full_height));
} else {
pixel_coords = rt_coord;
image_size = output_size;
}
if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) return;
uint pixel_index = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x);
vec3 color = vec3(0.0);
uint spp = max(u_samples_per_pixel, 1u);
for (uint s = 0u; s < spp; ++s) {
SobolState sobol = init_sobol(pixel_index, u_frame_count, s);
color += trace_path_sobol(pixel_coords, image_size, sobol);
}
color /= float(spp);
color = clamp(color, vec3(0.0), vec3(100.0));
// ── Super resolution: percycle running average inside the RT shader ──
if (u_sr_enabled != 0u) {
vec4 old = imageLoad(accumulation_image, pixel_coords);
uint cyc = u_frame_count / u_sr_scaling;
float w = 1.0 / float(cyc + 1u);
vec3 acc = mix(old.rgb, color, w);
imageStore(accumulation_image, pixel_coords, vec4(acc, 1.0));
return;
}
// ── Standard nonSR accumulation ─────────────────────────────────────
vec3 acc_color = color;
if (u_enable_accumulation && u_frame_count > 0u) {
vec3 old = imageLoad(accumulation_image, rt_coord).rgb;
float w = 1.0 / float(u_frame_count + 1u);
acc_color = mix(old, color, w);
}
vec3 out_color = aces_tonemap(acc_color);
imageStore(accumulation_image, rt_coord, vec4(acc_color, 1.0));
imageStore(output_image, rt_coord, vec4(out_color, 1.0));
}