diff --git a/baseq2/q2rtx.menu b/baseq2/q2rtx.menu index 50c12dc59..5f72a0629 100644 --- a/baseq2/q2rtx.menu +++ b/baseq2/q2rtx.menu @@ -147,6 +147,7 @@ begin video toggle "AMD FSR 1.0" flt_fsr_enable range --status "lower is sharper" "AMD FSR 1.0 sharpness" flt_fsr_sharpness 0 2 0.01 pairs "global illumination" pt_num_bounce_rays low 0.5 medium 1 high 2 + pairs "direct light sampling" pt_restir "RIS light" 0 "ReSTIR, high quality" 1 "ReSTIR, half" 2 "ReSTIR, quarter" 3 pairs "reflection/refraction depth" pt_reflect_refract off 0 1 1 2 2 4 4 8 8 toggle --status "turn some monitors in the game into security camera views" \ "security cameras" pt_cameras diff --git a/cmake/compileShaders.cmake b/cmake/compileShaders.cmake index 544bcb650..dd10070d6 100644 --- a/cmake/compileShaders.cmake +++ b/cmake/compileShaders.cmake @@ -16,6 +16,7 @@ set(SHADER_SOURCE_DEPENDENCIES ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/precomputed_sky.glsl ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/precomputed_sky_params.h ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/projection.glsl + ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/restir.h ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/sky.h ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/tiny_encryption_algorithm.h ${CMAKE_SOURCE_DIR}/src/refresh/vkpt/shader/tone_mapping_utils.glsl diff --git a/doc/client.md b/doc/client.md index 0e2cd6e81..b4dc09eb8 100644 --- a/doc/client.md +++ b/doc/client.md @@ -937,6 +937,13 @@ Selects the projection to use for rendering. Default value is 0. #### `pt_reflect_refract` Number of reflection or refraction bounces to trace. Default value is 2. +#### `pt_restir` +Switch for experimental direct light sampling algorithms. Default value is 1. +- 0 — RIS light sampling. +- 1 — ReSTIR, high quality. +- 2 — ReSTIR El-Cheapo, uses half of the shadow rays. +- 3 — ReSTIR El-Very-Cheapo, uses one quarter of the shadow rays. + #### `pt_roughness_override` Global override for roughness of all materials. Negative values mean there is no override. Default value is -1. diff --git a/inc/refresh/models.h b/inc/refresh/models.h index d9becfc57..0a19c56ff 100644 --- a/inc/refresh/models.h +++ b/inc/refresh/models.h @@ -118,6 +118,7 @@ typedef struct light_poly_s { int cluster; int style; float emissive_factor; + int type; } light_poly_t; typedef struct maliasmesh_s maliasmesh_t; diff --git a/src/refresh/vkpt/bsp_mesh.c b/src/refresh/vkpt/bsp_mesh.c index 7117a6c43..0bf0347da 100644 --- a/src/refresh/vkpt/bsp_mesh.c +++ b/src/refresh/vkpt/bsp_mesh.c @@ -959,6 +959,7 @@ collect_one_light_poly_entire_texture(bsp_t *bsp, mface_t *surf, mtexinfo_t *tex light.material = texinfo->material; light.style = light_style; + light.type = LIGHT_POLYGON; if(!get_triangle_off_center(light.positions, light.off_center, NULL, 1.f)) continue; @@ -1113,6 +1114,7 @@ collect_one_light_poly(bsp_t *bsp, mface_t *surf, mtexinfo_t *texinfo, int model light_poly_t* light = append_light_poly(num_lights, allocated_lights, lights); light->material = texinfo->material; light->style = light_style; + light->type = LIGHT_POLYGON; light->emissive_factor = emissive_factor; VectorCopy(instance_positions[0], light->positions + 0); VectorCopy(instance_positions[i1], light->positions + 3); @@ -1328,6 +1330,7 @@ collect_sky_and_lava_light_polys(bsp_mesh_t *wm, bsp_t* bsp) } light.style = 0; + light.type = LIGHT_POLYGON; if (!get_triangle_off_center(light.positions, light.off_center, NULL, 1.f)) continue; @@ -1821,6 +1824,7 @@ bsp_mesh_create_custom_sky_prims(uint32_t* prim_ctr, bsp_mesh_t* wm, const bsp_t light->material = 0; light->style = 0; light->cluster = cluster; + light->type = LIGHT_POLYGON; ++*prim_ctr; diff --git a/src/refresh/vkpt/conversion.h b/src/refresh/vkpt/conversion.h index 97e3bfb77..cb35497bf 100644 --- a/src/refresh/vkpt/conversion.h +++ b/src/refresh/vkpt/conversion.h @@ -90,6 +90,16 @@ static inline float halfToFloat(uint16_t value) return v.f; } +static inline float uintBitsToFloat(uint32_t ui) +{ + union { + float f; + uint32_t ui; + } bits; + bits.ui = ui; + return bits.f; +} + void packHalf4x16(uint32_t* half, float* vec4); #endif // CONVERSION_H_ diff --git a/src/refresh/vkpt/main.c b/src/refresh/vkpt/main.c index 5cc1eaf64..bf8a05ab8 100644 --- a/src/refresh/vkpt/main.c +++ b/src/refresh/vkpt/main.c @@ -1666,20 +1666,14 @@ destroy_vulkan(void) return 0; } -typedef struct entity_hash_s { - unsigned int mesh : 8; - unsigned int model : 9; - unsigned int entity : 14; - unsigned int bsp : 1; -} entity_hash_t; - static int entity_frame_num = 0; static uint32_t model_entity_ids[2][MAX_MODEL_INSTANCES]; static int model_entity_id_count[2]; +static int light_entity_ids[2][MAX_MODEL_LIGHTS]; +static int light_entity_id_count[2]; static int iqm_matrix_count[2]; static ModelInstance model_instances_prev[MAX_MODEL_INSTANCES]; -#define MAX_MODEL_LIGHTS 16384 static int num_model_lights = 0; static light_poly_t model_lights[MAX_MODEL_LIGHTS]; @@ -1808,47 +1802,70 @@ static void fill_model_instance(ModelInstance* instance, const entity_t* entity, instance->material |= MATERIAL_FLAG_LIGHT; } -static void add_dlight_spot(const dlight_t* light, DynLightData* dynlight_data) +static void add_dlight_spot(const dlight_t* dlight, light_poly_t* light) { // Copy spot data - VectorCopy(light->spot.direction, dynlight_data->spot_direction); - switch(light->spot.emission_profile) + VectorCopy(dlight->spot.direction, light->positions + 6); + uint spot_type = 0, spot_data = 0; + switch(dlight->spot.emission_profile) { case DLIGHT_SPOT_EMISSION_PROFILE_FALLOFF: - dynlight_data->type |= DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF << 16; - dynlight_data->spot_data = floatToHalf(light->spot.cos_total_width) | (floatToHalf(light->spot.cos_falloff_start) << 16); + spot_type = DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF; + spot_data = floatToHalf(dlight->spot.cos_total_width) | (floatToHalf(dlight->spot.cos_falloff_start) << 16); break; case DLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE: - dynlight_data->type |= DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE << 16; - dynlight_data->spot_data = floatToHalf(light->spot.total_width) | (light->spot.texture << 16); + spot_type = DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE; + spot_data = floatToHalf(dlight->spot.total_width) | (dlight->spot.texture << 16); break; } + light->positions[4] = uintBitsToFloat(spot_type); + light->positions[5] = uintBitsToFloat(spot_data); } static void -add_dlights(const dlight_t* lights, int num_lights, QVKUniformBuffer_t* ubo) +add_dlights(const dlight_t* dlights, int num_dlights, light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, int* light_entity_ids) { - ubo->num_dyn_lights = 0; - - for (int i = 0; i < num_lights; i++) + for (int i = 0; i < num_dlights; i++) { - const dlight_t* light = lights + i; - - DynLightData* dynlight_data = ubo->dyn_light_data + ubo->num_dyn_lights; - VectorCopy(light->origin, dynlight_data->center); - VectorScale(light->color, light->intensity / 25.f, dynlight_data->color); - dynlight_data->radius = light->radius; - switch(light->light_type) { - case DLIGHT_SPHERE: - dynlight_data->type = DYNLIGHT_SPHERE; - break; - case DLIGHT_SPOT: - dynlight_data->type = DYNLIGHT_SPOT; - add_dlight_spot(light, dynlight_data); - break; - } + if (*num_lights >= max_lights) + return; + + const dlight_t* dlight = dlights + i; + light_poly_t* light = light_list + *num_lights; - ubo->num_dyn_lights++; + light->cluster = BSP_PointLeaf(bsp->nodes, dlight->origin)->cluster; + + entity_hash_t hash; + hash.entity = i + 1; //entity ID + hash.mesh = 0xAA; + + if(light->cluster >= 0) + { + //Super wasteful but we want to have all lights in the same list. + + VectorCopy(dlight->origin, light->positions + 0); + VectorScale(dlight->color, dlight->intensity / 25.f, light->color); + light->positions[3] = dlight->radius; + light->material = NULL; + light->style = 0; + + switch(dlight->light_type) { + case DLIGHT_SPHERE: + light->type = LIGHT_SPHERE; + hash.model = 0xFE; + break; + case DLIGHT_SPOT: + light->type = LIGHT_SPOT; + // Copy spot data + add_dlight_spot(dlight, light); + hash.model = 0xFD; + break; + } + + light_entity_ids[(*num_lights)] = *(uint32_t*)&hash; + (*num_lights)++; + + } } } @@ -1860,7 +1877,7 @@ static inline void transform_point(const float* p, const float* matrix, float* r VectorCopy(transformed, result); // vec4 -> vec3 } -static void instance_model_lights(int num_light_polys, const light_poly_t* light_polys, const float* transform) +static void instance_model_lights(int num_light_polys, const light_poly_t* light_polys, const float* transform, entity_hash_t hash) { for (int nlight = 0; nlight < num_light_polys; nlight++) { @@ -1890,10 +1907,15 @@ static void instance_model_lights(int num_light_polys, const light_poly_t* light VectorCopy(src_light->color, dst_light->color); dst_light->material = src_light->material; dst_light->style = src_light->style; + dst_light->type = LIGHT_POLYGON; + + hash.mesh = nlight; //More a light index than a mesh + light_entity_ids[entity_frame_num][num_model_lights] = *(uint32_t*)&hash; num_model_lights++; } } + static const mat4 g_identity_transform = { { 1.f, 0.f, 0.f, 0.f }, { 0.f, 1.f, 0.f, 0.f }, @@ -1973,7 +1995,7 @@ static void process_bsp_entity(const entity_t* entity, int* instance_count) mi->render_buffer_idx = VERTEX_BUFFER_WORLD; mi->render_prim_offset = model->geometry.prim_offsets[0]; - instance_model_lights(model->num_light_polys, model->light_polys, transform); + instance_model_lights(model->num_light_polys, model->light_polys, transform, hash); if (model->geometry.accel) { @@ -2166,7 +2188,13 @@ static void process_regular_entity( mult_matrix_vector(end, transform, offset2); VectorSet(color, 0.25f, 0.5f, 0.07f); - vkpt_build_cylinder_light(model_lights, &num_model_lights, MAX_MODEL_LIGHTS, bsp_world_model, begin, end, color, 1.5f); + entity_hash_t hash; + hash.entity = entity->id; + hash.model = entity->model; + hash.mesh = 0; + hash.bsp = 0; + + vkpt_build_cylinder_light(model_lights, &num_model_lights, MAX_MODEL_LIGHTS, bsp_world_model, begin, end, color, 1.5f, hash, light_entity_ids[entity_frame_num]); } *instance_count = current_instance_index; @@ -2241,7 +2269,13 @@ prepare_entities(EntityUploadInfo* upload_info) else create_entity_matrix(transform, (entity_t*)entity); - instance_model_lights(model->num_light_polys, model->light_polys, transform); + entity_hash_t hash; + hash.entity = i + 1; + hash.model = ~entity->model; + hash.mesh = 0; + hash.bsp = 0; + + instance_model_lights(model->num_light_polys, model->light_polys, transform, hash); } } } @@ -2318,6 +2352,7 @@ prepare_entities(EntityUploadInfo* upload_info) memset(instance_buffer->model_current_to_prev, -1, sizeof(instance_buffer->model_current_to_prev)); memset(instance_buffer->model_prev_to_current, -1, sizeof(instance_buffer->model_prev_to_current)); + memset(instance_buffer->mlight_prev_to_current, ~0u, sizeof(instance_buffer->mlight_prev_to_current)); model_entity_id_count[entity_frame_num] = model_instance_idx; for(int i = 0; i < model_entity_id_count[entity_frame_num]; i++) { @@ -2937,8 +2972,6 @@ prepare_ubo(refdef_t *fd, mleaf_t* viewleaf, const reference_mode_t* ref_mode, c VectorCopy(sky_matrix[0], ubo->environment_rotation_matrix[0]); VectorCopy(sky_matrix[1], ubo->environment_rotation_matrix[1]); VectorCopy(sky_matrix[2], ubo->environment_rotation_matrix[2]); - - add_dlights(vkpt_refdef.fd->dlights, vkpt_refdef.fd->num_dlights, ubo); if (wm->num_cameras > 0) { @@ -2955,6 +2988,21 @@ prepare_ubo(refdef_t *fd, mleaf_t* viewleaf, const reference_mode_t* ref_mode, c ubo->num_cameras = wm->num_cameras; } +static void +update_mlight_prev_to_current(void) +{ + light_entity_id_count[entity_frame_num] = num_model_lights; + for(int i = 0; i < light_entity_id_count[entity_frame_num]; i++) { + entity_hash_t hash = *(entity_hash_t*)&light_entity_ids[entity_frame_num][i]; + if(hash.entity == 0u) continue; + for(int j = 0; j < light_entity_id_count[!entity_frame_num]; j++) { + if(light_entity_ids[entity_frame_num][i] == light_entity_ids[!entity_frame_num][j]) { + vkpt_refdef.uniform_instance_buffer.mlight_prev_to_current[j] = i; + break; + } + } + } +} /* renders the map ingame */ void @@ -3043,9 +3091,12 @@ R_RenderFrame_RTX(refdef_t *fd) vkpt_pt_instance_model_blas(&vkpt_refdef.bsp_mesh_world.geom_sky, g_identity_transform, VERTEX_BUFFER_WORLD, -1, 0); vkpt_pt_instance_model_blas(&vkpt_refdef.bsp_mesh_world.geom_custom_sky, g_identity_transform, VERTEX_BUFFER_WORLD, -1, 0); - vkpt_build_beam_lights(model_lights, &num_model_lights, MAX_MODEL_LIGHTS, bsp_world_model, fd->entities, fd->num_entities, prev_adapted_luminance); + vkpt_build_beam_lights(model_lights, &num_model_lights, MAX_MODEL_LIGHTS, bsp_world_model, fd->entities, fd->num_entities, prev_adapted_luminance, light_entity_ids[entity_frame_num], &num_model_lights); + add_dlights(vkpt_refdef.fd->dlights, vkpt_refdef.fd->num_dlights, model_lights, &num_model_lights, MAX_MODEL_LIGHTS, bsp_world_model, light_entity_ids[entity_frame_num]); } + update_mlight_prev_to_current(); + vkpt_vertex_buffer_ensure_primbuf_size(upload_info.num_prims); QVKUniformBuffer_t *ubo = &vkpt_refdef.uniform_buffer; diff --git a/src/refresh/vkpt/path_tracer.c b/src/refresh/vkpt/path_tracer.c index 5b9d33328..37746db55 100644 --- a/src/refresh/vkpt/path_tracer.c +++ b/src/refresh/vkpt/path_tracer.c @@ -104,6 +104,7 @@ cvar_t* cvar_pt_enable_sprites = NULL; extern cvar_t *cvar_pt_caustics; extern cvar_t *cvar_pt_reflect_refract; +extern cvar_t *cvar_pt_restir; typedef struct QvkGeometryInstance_s { @@ -1139,6 +1140,11 @@ vkpt_pt_trace_lighting(VkCommandBuffer cmd_buf, float num_bounce_rays) BARRIER_COMPUTE(cmd_buf, qvk.images[VKPT_IMG_PT_COLOR_HF]); BARRIER_COMPUTE(cmd_buf, qvk.images[VKPT_IMG_PT_COLOR_SPEC]); + if(cvar_pt_restir->value != 0) { + int frame_idx = qvk.frame_counter & 1; + BARRIER_COMPUTE(cmd_buf, qvk.images[VKPT_IMG_PT_RESTIR_A + frame_idx]); + } + BUFFER_BARRIER(cmd_buf, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, diff --git a/src/refresh/vkpt/shader/constants.h b/src/refresh/vkpt/shader/constants.h index e8c13b6c7..c4fbb32b4 100644 --- a/src/refresh/vkpt/shader/constants.h +++ b/src/refresh/vkpt/shader/constants.h @@ -114,6 +114,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAX_TLAS_INSTANCES (MAX_MODEL_INSTANCES + MAX_RESERVED_INSTANCES) #define MAX_LIGHT_SOURCES 32 #define MAX_LIGHT_STYLES 64 +#define MAX_MODEL_LIGHTS 16384 #define TLAS_INDEX_GEOMETRY 0 #define TLAS_INDEX_EFFECTS 1 @@ -161,9 +162,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #define M_PI 3.1415926535897932384626433832795 #endif -// Dynamic light types -#define DYNLIGHT_SPHERE 0 -#define DYNLIGHT_SPOT 1 +// Light types +#define LIGHT_POLYGON 0 +#define LIGHT_SPHERE 1 +#define LIGHT_SPOT 2 // // Spotlight styles (emission profiles) diff --git a/src/refresh/vkpt/shader/direct_lighting.rgen b/src/refresh/vkpt/shader/direct_lighting.rgen index 6261ecaaa..098969427 100644 --- a/src/refresh/vkpt/shader/direct_lighting.rgen +++ b/src/refresh/vkpt/shader/direct_lighting.rgen @@ -1,6 +1,7 @@ /* Copyright (C) 2018 Christoph Schied Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved. +Copyright (C) 2022 Jorge Gustavo Martinez This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,11 +31,293 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma optionNV(unroll all) #define ENABLE_SHADOW_CAUSTICS -#include "path_tracer_rgen.h" +#include "restir.h" #include "projection.glsl" layout(constant_id = 0) const uint spec_enable_caustics = 0; + +void +direct_lighting_restir(ivec2 ipos, bool is_odd_checkerboard, out vec3 high_freq, out vec3 o_specular) +{ + high_freq = vec3(0); + o_specular = vec3(0); + + rng_seed = texelFetch(TEX_ASVGF_RNG_SEED_A, ipos, 0).r; + + vec3 direct_diffuse, direct_specular; + float vis; + + Reservoir r, prev_r, spatial_r; + prev_r.y = RESTIR_INVALID_ID; + init_reservoir(r); + + vec4 position_material = texelFetch(TEX_PT_SHADING_POSITION, ipos, 0); + + vec3 position = position_material.xyz; + uint material_id = floatBitsToUint(position_material.w); + + // don't compute lighting for invalid surfaces + if(material_id == 0) + { + imageStore(IMG_PT_RESTIR_A, ipos, uvec4(pack_reservoir(r))); + return; + } + + bool is_gradient = get_is_gradient(ipos); + + vec4 view_direction = texelFetch(TEX_PT_VIEW_DIRECTION, ipos, 0); + vec3 normal = decode_normal(texelFetch(TEX_PT_NORMAL_A, ipos, 0).x); + vec3 geo_normal = decode_normal(texelFetch(TEX_PT_GEO_NORMAL_A, ipos, 0).x); + vec4 primary_base_color = texelFetch(TEX_PT_BASE_COLOR_A, ipos, 0); + float primary_specular_factor = primary_base_color.a; + vec2 metal_rough = texelFetch(TEX_PT_METALLIC_A, ipos, 0).xy; + float primary_metallic = metal_rough.x; + float primary_roughness = metal_rough.y; + uint cluster_idx = texelFetch(TEX_PT_CLUSTER_A, ipos, 0).x; + if(cluster_idx == 0xffff) cluster_idx = ~0u; // because the image is uint16 + float view_depth = texelFetch(TEX_PT_VIEW_DEPTH_A, ipos, 0).x; + vec4 motion = texelFetch(TEX_PT_MOTION, ipos, 0); + + bool primary_is_weapon = (material_id & MATERIAL_FLAG_WEAPON) != 0; + int primary_medium = int((material_id & MATERIAL_LIGHT_STYLE_MASK) >> MATERIAL_LIGHT_STYLE_SHIFT); + + bool use_shadow_rays = true; + + int shadow_cull_mask = SHADOW_RAY_CULL_MASK; + + if(global_ubo.first_person_model != 0 && !primary_is_weapon) + shadow_cull_mask |= AS_FLAG_VIEWER_MODELS; + else + shadow_cull_mask |= AS_FLAG_VIEWER_WEAPON; + + float direct_specular_weight = smoothstep( + global_ubo.pt_direct_roughness_threshold - 0.02, + global_ubo.pt_direct_roughness_threshold + 0.02, + primary_roughness); + + vec3 primary_albedo, primary_base_reflectivity; + get_reflectivity(primary_base_color.rgb, primary_metallic, primary_albedo, primary_base_reflectivity); + + float alpha = square(primary_roughness); + float phong_exp = RoughnessSquareToSpecPower(alpha); + float phong_scale = min(100, 1 / (M_PI * square(alpha))); + float phong_weight = clamp(primary_specular_factor * luminance(primary_base_reflectivity) / (luminance(primary_base_reflectivity) + luminance(primary_albedo)), 0, 0.9); + + int field_left = 0; + int field_right = global_ubo.prev_width / 2; + if(ipos.x >= global_ubo.width / 2) + { + field_left = field_right; + field_right = global_ubo.prev_width; + } + + //Do temporal + ivec2 pos_prev = ivec2(floor(((vec2(ipos) + vec2(0.5)) * vec2(global_ubo.inv_width * 2, global_ubo.inv_height) + motion.xy) * vec2(global_ubo.prev_width * 0.5, global_ubo.prev_height))); + ivec2 pos_prev_or = pos_prev; + if(!is_gradient) + { + if(global_ubo.pt_restir == 2) + { + use_shadow_rays = (ipos.x & 1) == (ipos.y & 1); + pos_prev.x = (pos_prev.x & ~1) + (pos_prev.y & 1); + } + else if(global_ubo.pt_restir == 3) + { + use_shadow_rays = (ipos.x & 1) == 0 && (ipos.y & 1) == 0; + pos_prev &= ~1; + } + } + + if (!(pos_prev.x < field_left || pos_prev.x >= field_right || pos_prev.y < 0 || pos_prev.y >= global_ubo.height)) + { + float depth_prev = texelFetch(TEX_PT_VIEW_DEPTH_B, pos_prev, 0).x; + float dist_depth = abs(depth_prev - view_depth) / abs(view_depth); + + if(dist_depth < 0.1) + { + vec3 normal_prev; + float dot_normals; + normal_prev = decode_normal(texelFetch(TEX_PT_NORMAL_B, pos_prev, 0).x); + dot_normals = dot(normal_prev, normal); + + if(dot_normals > 0.5) + { + uvec4 packed_reservoir = texelFetch(TEX_PT_RESTIR_B, pos_prev, 0); + unpack_reservoir(packed_reservoir, prev_r); + prev_r.y = get_light_current_idx(prev_r.y); + if(prev_r.y != RESTIR_INVALID_ID && use_shadow_rays) + { + prev_r.p_hat = get_unshadowed_path_contrib(prev_r.y, position, normal, view_direction.xyz, phong_exp, phong_scale, phong_weight, prev_r.y_pos); + } + } + } + } + + //Sample lights + if(use_shadow_rays && !is_gradient) + { + get_direct_illumination_restir( + position, + normal, + cluster_idx, + view_direction.xyz, + phong_exp, + phong_scale, + phong_weight, + 0, + prev_r, + r); + } + else if(is_gradient) + { + r = prev_r; + } + else if(prev_r.y != RESTIR_INVALID_ID) + { + float rng = 0.0; + update_reservoir(prev_r.y, prev_r.p_hat * prev_r.W * prev_r.M, prev_r.y_pos, prev_r.M, prev_r.p_hat, rng, r); + } + + //Spatio-temporal + if(global_ubo.pt_restir_spatial != 0 && !is_gradient) + { + float rng = get_rng(RNG_RESTIR_SP_LIGHT_SELECTION(0)); + float min_p_hat = 0.0;//rng * 0.5; + uint processed = 0; + ivec2 sample_pos; + Reservoir st_r; + init_reservoir(spatial_r); + + vec2 rng_pos = vec2(get_rng(RNG_RESTIR_SPATIAL_X(0)), get_rng(RNG_RESTIR_SPATIAL_Y(0))); + int distance = use_shadow_rays ? RESTIR_SPACIAL_DISTANCE : 2; + uint sp_samples = uint(global_ubo.pt_restir_spatial); + + //Extra sampling when temporal failed + if(prev_r.y == RESTIR_INVALID_ID) sp_samples += 2; + + int samples = min(RESTIR_SPACIAL_SAMPLES, int(sp_samples) * 2); + ivec2 pos; + #pragma unroll + for(int i=0; i < samples ; i++) + { + pos = ivec2(ceil(sample_disk(rng_pos) * distance)); + + if(i == 4) distance *= 2; + + rng_pos = rng_pos * 10 - floor(rng_pos * 10); + + sample_pos.x = clamp(pos_prev_or.x + pos.x, field_left, field_right - 1); + sample_pos.y = clamp(pos_prev_or.y + pos.y, 0, int(global_ubo.height - 1)); + ivec2 sample_pos_r = sample_pos; + + if(global_ubo.pt_restir == 2) + { + sample_pos.x = min((sample_pos.x & ~1) + (sample_pos.y & 1), field_right - 1); + sample_pos_r = sample_pos; + } + else if(global_ubo.pt_restir == 3) + { + sample_pos &= ~1; + } + + uvec4 packed_reservoir = texelFetch(TEX_PT_RESTIR_B, sample_pos, 0); + unpack_reservoir(packed_reservoir, st_r); + st_r.y = get_light_current_idx(st_r.y); + + if(st_r.y == RESTIR_INVALID_ID || st_r.M == 0) continue; + + float depth_prev = texelFetch(TEX_PT_VIEW_DEPTH_B, sample_pos, 0).x; + float dist_depth = abs(depth_prev - view_depth) / abs(view_depth); + + if(dist_depth < 0.1) + { + + vec3 normal_prev; + float dot_normals; + + if(use_shadow_rays) + { + normal_prev = decode_normal(texelFetch(TEX_PT_NORMAL_B, sample_pos, 0).x); + dot_normals = dot(normal_prev, normal); + } + else + { + dot_normals = 1.0f; + } + + if(dot_normals > 0.5) + { + + float p_hat = get_unshadowed_path_contrib(st_r.y, position, normal, view_direction.xyz, phong_exp, phong_scale, phong_weight, st_r.y_pos); + if(p_hat < min_p_hat) { + continue; + } + + //Combine reservoirs + update_reservoir(st_r.y, p_hat * st_r.W * st_r.M, st_r.y_pos, st_r.M, p_hat, rng, spatial_r); + processed ++; + if(sp_samples == processed) break; + } + } + + } + + if(processed > 0) + { + //Combine with the other reservoir + update_reservoir(spatial_r.y, spatial_r.w_sum, spatial_r.y_pos, spatial_r.M, spatial_r.p_hat, rng, r); + r.W = r.w_sum / (r.p_hat * r.M); + if(isnan(r.W) || isinf(r.W)) r.W = 0.0; + } + } + + if(r.y != RESTIR_INVALID_ID) + { + process_selected_light_restir( + r.y, + r.y_pos, + min(r.W, global_ubo.pt_restir_max_w), + position, + normal, + geo_normal, + shadow_cull_mask, + view_direction.xyz, + primary_base_reflectivity, + primary_specular_factor, + primary_roughness, + primary_medium, + spec_enable_caustics != 0, + direct_specular_weight, + phong_exp, + phong_scale, + phong_weight, + use_shadow_rays && r.W > 0.0, + cluster_idx, + direct_diffuse, + direct_specular, + vis); + + high_freq += direct_diffuse; + o_specular += direct_specular; + + if(vis == 0.0f) r.W = 0.0f; + } + + //Normalize reservoir + float m_clamp = global_ubo.pt_restir != 3 ? float(RESTIR_M_CLAMP) : float(RESTIR_M_VC_CLAMP); + r.w_sum = (m_clamp * r.w_sum) / r.M; + + o_specular = demodulate_specular(primary_base_reflectivity, o_specular); + + high_freq = clamp_output(high_freq); + o_specular = clamp_output(o_specular); + + imageStore(IMG_PT_RESTIR_A, ipos, uvec4(pack_reservoir(r))); + +} + + void direct_lighting(ivec2 ipos, bool is_odd_checkerboard, out vec3 high_freq, out vec3 o_specular) { @@ -45,7 +328,7 @@ direct_lighting(ivec2 ipos, bool is_odd_checkerboard, out vec3 high_freq, out ve vec4 position_material = texelFetch(TEX_PT_SHADING_POSITION, ipos, 0); - + vec3 position = position_material.xyz; uint material_id = floatBitsToUint(position_material.w); @@ -55,11 +338,11 @@ direct_lighting(ivec2 ipos, bool is_odd_checkerboard, out vec3 high_freq, out ve vec4 view_direction = texelFetch(TEX_PT_VIEW_DIRECTION, ipos, 0); vec3 normal = decode_normal(texelFetch(TEX_PT_NORMAL_A, ipos, 0).x); - vec3 geo_normal = decode_normal(texelFetch(TEX_PT_GEO_NORMAL_A, ipos, 0).x); - vec4 primary_base_color = texelFetch(TEX_PT_BASE_COLOR_A, ipos, 0); - float primary_specular_factor = primary_base_color.a; - vec2 metal_rough = texelFetch(TEX_PT_METALLIC_A, ipos, 0).xy; - float primary_metallic = metal_rough.x; + vec3 geo_normal = decode_normal(texelFetch(TEX_PT_GEO_NORMAL_A, ipos, 0).x); + vec4 primary_base_color = texelFetch(TEX_PT_BASE_COLOR_A, ipos, 0); + float primary_specular_factor = primary_base_color.a; + vec2 metal_rough = texelFetch(TEX_PT_METALLIC_A, ipos, 0).xy; + float primary_metallic = metal_rough.x; float primary_roughness = metal_rough.y; uint cluster_idx = texelFetch(TEX_PT_CLUSTER_A, ipos, 0).x; if(cluster_idx == 0xffff) cluster_idx = ~0u; // because the image is uint16 @@ -81,35 +364,35 @@ direct_lighting(ivec2 ipos, bool is_odd_checkerboard, out vec3 high_freq, out ve bool is_gradient = get_is_gradient(ipos); - vec3 primary_albedo, primary_base_reflectivity; - get_reflectivity(primary_base_color.rgb, primary_metallic, primary_albedo, primary_base_reflectivity); + vec3 primary_albedo, primary_base_reflectivity; + get_reflectivity(primary_base_color.rgb, primary_metallic, primary_albedo, primary_base_reflectivity); vec3 direct_diffuse, direct_specular; - get_direct_illumination( - position, - normal, - geo_normal, - cluster_idx, - material_id, - shadow_cull_mask, - view_direction.xyz, - primary_albedo, - primary_base_reflectivity, - primary_specular_factor, - primary_roughness, - primary_medium, - spec_enable_caustics != 0, - direct_specular_weight, - global_ubo.pt_direct_polygon_lights > 0, - global_ubo.pt_direct_dyn_lights > 0, - is_gradient, - 0, - direct_diffuse, - direct_specular); - - high_freq += direct_diffuse; - o_specular += direct_specular; - + get_direct_illumination( + position, + normal, + geo_normal, + cluster_idx, + material_id, + shadow_cull_mask, + view_direction.xyz, + primary_albedo, + primary_base_reflectivity, + primary_specular_factor, + primary_roughness, + primary_medium, + spec_enable_caustics != 0, + direct_specular_weight, + global_ubo.pt_direct_polygon_lights > 0, + global_ubo.pt_direct_dyn_lights > 0, + is_gradient, + 0, + direct_diffuse, + direct_specular); + + high_freq += direct_diffuse; + o_specular += direct_specular; + if(global_ubo.pt_direct_sun_light != 0) { vec3 direct_sun_diffuse, direct_sun_specular; @@ -149,7 +432,10 @@ main() bool is_odd_checkerboard = (rt_LaunchID.z != 0) || (push_constants.gpu_index == 1); vec3 high_freq, specular; - direct_lighting(ipos, is_odd_checkerboard, high_freq, specular); + if(global_ubo.pt_restir > 0) + direct_lighting_restir(ipos, is_odd_checkerboard, high_freq, specular); + else + direct_lighting(ipos, is_odd_checkerboard, high_freq, specular); high_freq *= STORAGE_SCALE_HF; specular *= STORAGE_SCALE_SPEC; diff --git a/src/refresh/vkpt/shader/global_textures.h b/src/refresh/vkpt/shader/global_textures.h index 2452b7f90..291e60daf 100644 --- a/src/refresh/vkpt/shader/global_textures.h +++ b/src/refresh/vkpt/shader/global_textures.h @@ -109,6 +109,8 @@ with this program; if not, write to the Free Software Foundation, Inc., IMG_DO(ASVGF_HIST_COLOR_LF_COCG_B,NUM_IMAGES_BASE + 27, R16G16_SFLOAT, rg16f, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ IMG_DO(ASVGF_GRAD_SMPL_POS_A, NUM_IMAGES_BASE + 28, R32_UINT, r32ui, IMG_WIDTH_GRAD_MGPU, IMG_HEIGHT_GRAD) \ IMG_DO(ASVGF_GRAD_SMPL_POS_B, NUM_IMAGES_BASE + 29, R32_UINT, r32ui, IMG_WIDTH_GRAD_MGPU, IMG_HEIGHT_GRAD) \ + IMG_DO(PT_RESTIR_A, NUM_IMAGES_BASE + 30, R32G32_UINT, rg32ui, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ + IMG_DO(PT_RESTIR_B, NUM_IMAGES_BASE + 31, R32G32_UINT, rg32ui, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ #define LIST_IMAGES_B_A \ IMG_DO(PT_VISBUF_PRIM_B, NUM_IMAGES_BASE + 0, R32G32_UINT, rg32ui, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ @@ -141,8 +143,10 @@ with this program; if not, write to the Free Software Foundation, Inc., IMG_DO(ASVGF_HIST_COLOR_LF_COCG_A,NUM_IMAGES_BASE + 27, R16G16_SFLOAT, rg16f, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ IMG_DO(ASVGF_GRAD_SMPL_POS_B, NUM_IMAGES_BASE + 28, R32_UINT, r32ui, IMG_WIDTH_GRAD_MGPU, IMG_HEIGHT_GRAD) \ IMG_DO(ASVGF_GRAD_SMPL_POS_A, NUM_IMAGES_BASE + 29, R32_UINT, r32ui, IMG_WIDTH_GRAD_MGPU, IMG_HEIGHT_GRAD) \ + IMG_DO(PT_RESTIR_B, NUM_IMAGES_BASE + 30, R32G32_UINT, rg32ui, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ + IMG_DO(PT_RESTIR_A, NUM_IMAGES_BASE + 31, R32G32_UINT, rg32ui, IMG_WIDTH_MGPU, IMG_HEIGHT ) \ -#define NUM_IMAGES (NUM_IMAGES_BASE + 30) /* this really sucks but I don't know how to fix it +#define NUM_IMAGES (NUM_IMAGES_BASE + 32) /* this really sucks but I don't know how to fix it counting with enum does not work in GLSL */ // todo: make naming consistent! diff --git a/src/refresh/vkpt/shader/global_ubo.h b/src/refresh/vkpt/shader/global_ubo.h index 1b4f14058..92598b3d1 100644 --- a/src/refresh/vkpt/shader/global_ubo.h +++ b/src/refresh/vkpt/shader/global_ubo.h @@ -92,6 +92,9 @@ with this program; if not, write to the Free Software Foundation, Inc., UBO_CVAR_DO(pt_particle_softness, 0.7) /* particle softness */ \ UBO_CVAR_DO(pt_particle_brightness, 100) /* particle brightness */ \ UBO_CVAR_DO(pt_reflect_refract, 2) /* number of reflection or refraction bounces: 0, 1 or 2 */ \ + UBO_CVAR_DO(pt_restir, 1) /* switch for using RIS or ReSTIR, 0 or 1 */ \ + UBO_CVAR_DO(pt_restir_spatial, 1) /* ReSTIR spatial samples */ \ + UBO_CVAR_DO(pt_restir_max_w, 12.0) /* ReSTIR max weight clamp */ \ UBO_CVAR_DO(pt_roughness_override, -1) /* overrides roughness of all materials if non-negative, [0..1] */ \ UBO_CVAR_DO(pt_specular_anti_flicker, 2) /* fade factor for rough reflections of surfaces far away, [0..inf) */ \ UBO_CVAR_DO(pt_specular_mis, 1) /* enables the use of MIS between specular direct lighting and BRDF specular rays */ \ @@ -162,7 +165,7 @@ with this program; if not, write to the Free Software Foundation, Inc., GLOBAL_UBO_VAR_LIST_DO(int, planet_albedo_map) \ GLOBAL_UBO_VAR_LIST_DO(int, planet_normal_map) \ \ - GLOBAL_UBO_VAR_LIST_DO(int, num_dyn_lights) \ + GLOBAL_UBO_VAR_LIST_DO(int, padding1) \ GLOBAL_UBO_VAR_LIST_DO(int , num_static_lights) \ GLOBAL_UBO_VAR_LIST_DO(uint, num_static_primitives) \ GLOBAL_UBO_VAR_LIST_DO(int, cluster_debug_index) \ @@ -219,7 +222,6 @@ with this program; if not, write to the Free Software Foundation, Inc., GLOBAL_UBO_VAR_LIST_DO(vec4, world_size) \ GLOBAL_UBO_VAR_LIST_DO(vec4, world_half_size_inv) \ \ - GLOBAL_UBO_VAR_LIST_DO(DynLightData, dyn_light_data[MAX_LIGHT_SOURCES]) \ GLOBAL_UBO_VAR_LIST_DO(vec4, cam_pos) \ GLOBAL_UBO_VAR_LIST_DO(mat4, V) \ GLOBAL_UBO_VAR_LIST_DO(mat4, invV) \ @@ -238,21 +240,6 @@ with this program; if not, write to the Free Software Foundation, Inc., \ UBO_CVAR_LIST // WARNING: Do not put any other members into global_ubo after this: the CVAR list is not vec4-aligned -BEGIN_SHADER_STRUCT( DynLightData ) -{ - vec3 center; - float radius; - vec3 color; - uint type; // Combines type (sphere vs spot) and "style" of light (eg spotlight emission profile) - vec3 spot_direction; - /* spot_data depends on spotlight emssion profile: - * DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF -> contains packed2x16 with cosTotalWidth, cosFalloffStart - * DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE -> contains a half with cosTotalWidth and the texture index - */ - uint spot_data; -} -END_SHADER_STRUCT( DynLightData ) - BEGIN_SHADER_STRUCT( ModelInstance ) { mat4 transform; @@ -300,6 +287,7 @@ BEGIN_SHADER_STRUCT( InstanceBuffer ) uint model_current_to_prev [MAX_MODEL_INSTANCES]; uint model_prev_to_current [MAX_MODEL_INSTANCES]; ModelInstance model_instances [MAX_MODEL_INSTANCES]; + uint mlight_prev_to_current [MAX_MODEL_LIGHTS]; uint tlas_instance_prim_offsets[MAX_TLAS_INSTANCES]; int tlas_instance_model_indices[MAX_TLAS_INSTANCES]; } diff --git a/src/refresh/vkpt/shader/light_lists.h b/src/refresh/vkpt/shader/light_lists.h index c555f96e8..75bc6b2a8 100644 --- a/src/refresh/vkpt/shader/light_lists.h +++ b/src/refresh/vkpt/shader/light_lists.h @@ -37,7 +37,7 @@ project_triangle(mat3 positions, vec3 p) } float -spherical_tri_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight) +projected_tri_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight) { positions[0] = positions[0] - p; positions[1] = positions[1] - p; @@ -64,6 +64,88 @@ spherical_tri_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, floa return pa * brdf; } +float +projected_sphere_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight) +{ + vec3 position = positions[0] - p; + float sphere_radius = positions[1].x; + float dist = length(position); + float rdist = 1.0 / dist; + vec3 L = position * rdist; + + if (dot(n, L) <= 0) + return 0.0; + + float specular = phong(n, L, V, phong_exp) * phong_scale; + float brdf = mix(1.0, specular, phong_weight); + + float irradiance = 2 * (1 - sqrt(max(0, 1 - square(sphere_radius * rdist)))); + irradiance = min(irradiance, 2 * M_PI); //max solid angle + + return irradiance * brdf; +} + +float +spot_falloff(in mat3 positions, in float cosTheta) +{ + float falloff; + const uint spot_style = floatBitsToUint(positions[1].y); + const uint spot_data = floatBitsToUint(positions[1].z); + + if(spot_style == DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF) { + const vec2 spot_falloff = unpackHalf2x16(spot_data); + const float cosTotalWidth = spot_falloff.x; + const float cosFalloffStart = spot_falloff.y; + + if(cosTheta < cosTotalWidth) + falloff = 0; + else if (cosTheta > cosFalloffStart) + falloff = 1; + else { + float delta = (cosTheta - cosTotalWidth) / (cosFalloffStart - cosTotalWidth); + falloff = (delta * delta) * (delta * delta); + } + } else if(spot_style == DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE) { + const float theta = acos(cosTheta); + const float totalWidth = unpackHalf2x16(spot_data).x; + const uint texture_num = spot_data >> 16; + + if (cosTheta >= 0) { + // Use the angle directly as texture coordinate for better angular resolution next to the center of the beam + float tc = clamp(theta / totalWidth, 0, 1); + falloff = global_texture(texture_num, vec2(tc, 0)).r; + } else + falloff = 0; + } + + return falloff; +} + +float +projected_spotlight_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight) +{ + vec3 position = positions[0] - p; + + float dist = length(position); + float rdist = 1.0 / dist; + vec3 L = position * rdist; + + if (dot(n, L) <= 0) + return 0.0; + + float specular = phong(n, L, V, phong_exp) * phong_scale; + float brdf = mix(1.0, specular, phong_weight); + + float cosTheta = dot(-L, positions[2]); // cosine of angle to spot direction + float falloff = spot_falloff(positions, cosTheta); + + float irradiance = 2 * falloff * square(rdist); + + irradiance = min(irradiance, 2 * M_PI); //max solid angle + + return irradiance * brdf; +} + float get_spherical_triangle_pdfw(mat3 positions) { // Project triangle to unit sphere @@ -153,6 +235,62 @@ sample_projected_triangle(vec3 pt, mat3 positions, vec2 rnd, out vec3 light_norm return pt + lo; } +vec3 +sample_projected_sphere(vec3 p, mat3 positions, vec2 rnd, out vec3 light_normal, out float pdfw) +{ + vec3 light_center = positions[0]; + vec3 position = light_center - p; + float sphere_radius = positions[1].x; + float dist = length(position); + float rdist = 1.0 / dist; + vec3 L = position * rdist; + + float projected_area = 2 * (1 - sqrt(max(0, 1 - square(sphere_radius * rdist)))); + projected_area = min(projected_area, 2 * M_PI); //max solid angle + pdfw = 1.0 / projected_area; + + mat3 onb = construct_ONB_frisvad(L); + vec3 diskpt; + diskpt.xy = sample_disk(rnd); + diskpt.z = sqrt(max(0, 1 - diskpt.x * diskpt.x - diskpt.y * diskpt.y)); + + vec3 position_light = light_center + (onb[0] * diskpt.x + onb[2] * diskpt.y - L * diskpt.z) * sphere_radius; + + light_normal = normalize(position_light - light_center); + + return position_light; +} + +vec3 +sample_projected_spotlight(vec3 p, mat3 positions, vec2 rnd, out vec3 light_normal, out float pdfw) +{ + vec3 light_center = positions[0]; + float emitter_radius = positions[1].x; + + mat3 onb = construct_ONB_frisvad(positions[2]); + // Emit light from a small disk around the origin + vec2 diskpt = sample_disk(rnd); + vec3 position_light = light_center + (onb[0] * diskpt.x + onb[2] * diskpt.y) * emitter_radius; + + vec3 c = position_light - p; + float dist = length(c); + float rdist = 1.0 / dist; + vec3 L = c * rdist; + + // Direction from emission point to surface, in a basis where +Y is the spot direction + vec3 L_l = -L * onb; + float cosTheta = L_l.y; // cosine of angle to spot direction + float falloff = spot_falloff(positions, cosTheta); + + float projected_area = 2 * falloff * square(rdist); + projected_area = min(projected_area, 2 * M_PI); //max solid angle + pdfw = 1.0 / projected_area; + + light_normal = normalize(positions[2]); + + return position_light; +} + uint get_light_stats_addr(uint cluster, uint light, uint side) { uint addr = cluster; @@ -163,7 +301,7 @@ uint get_light_stats_addr(uint cluster, uint light, uint side) } void -sample_polygonal_lights( +sample_lights( uint list_idx, vec3 p, vec3 n, @@ -232,7 +370,18 @@ sample_polygonal_lights( LightPolygon light = get_light_polygon(current_idx); - float m = spherical_tri_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight); + float m = 0.0f; + switch(uint(light.type)){ + case LIGHT_POLYGON: + m = projected_tri_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight); + break; + case LIGHT_SPHERE: + m = projected_sphere_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight); + break; + case LIGHT_SPOT: + m = projected_spotlight_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight); + break; + } float light_lum = luminance(light.color); @@ -315,7 +464,18 @@ sample_polygonal_lights( LightPolygon light = get_light_polygon(current_idx); vec3 light_normal; - position_light = sample_projected_triangle(p, light.positions, rng.yz, light_normal, pdfw); + + switch(uint(light.type)){ + case LIGHT_POLYGON: + position_light = sample_projected_triangle(p, light.positions, rng.yz, light_normal, pdfw); + break; + case LIGHT_SPHERE: + position_light = sample_projected_sphere(p, light.positions, rng.yz, light_normal, pdfw); + break; + case LIGHT_SPOT: + position_light = sample_projected_spotlight(p, light.positions, rng.yz, light_normal, pdfw); + break; + } vec3 L = normalize(position_light - p); @@ -345,119 +505,6 @@ sample_polygonal_lights( light_color /= pdf; } -float -compute_dynlight_sphere(uint light_idx, vec3 light_center, vec3 p, out vec3 position_light, vec3 rng) -{ - vec3 c = light_center - p; - float dist = length(c); - float rdist = 1.0 / dist; - vec3 L = c * rdist; - - float sphere_radius = global_ubo.dyn_light_data[light_idx].radius; - float irradiance = 2 * (1 - sqrt(max(0, 1 - square(sphere_radius * rdist)))); - - mat3 onb = construct_ONB_frisvad(L); - vec3 diskpt; - diskpt.xy = sample_disk(rng.yz); - diskpt.z = sqrt(max(0, 1 - diskpt.x * diskpt.x - diskpt.y * diskpt.y)); - - position_light = light_center + (onb[0] * diskpt.x + onb[2] * diskpt.y - L * diskpt.z) * sphere_radius; - - return irradiance; -} - -float -compute_dynlight_spot(uint light_idx, uint spot_style, vec3 light_center, vec3 p, out vec3 position_light, vec3 rng) -{ - mat3 onb = construct_ONB_frisvad(global_ubo.dyn_light_data[light_idx].spot_direction); - // Emit light from a small disk around the origin - float emitter_radius = global_ubo.dyn_light_data[light_idx].radius; - vec2 diskpt = sample_disk(rng.yz); - position_light = light_center + (onb[0] * diskpt.x + onb[2] * diskpt.y) * emitter_radius; - - vec3 c = position_light - p; - float dist = length(c); - float rdist = 1.0 / dist; - vec3 L = c * rdist; - - // Direction from emission point to surface, in a basis where +Y is the spot direction - vec3 L_l = -L * onb; - float cosTheta = L_l.y; // cosine of angle to spot direction - float falloff; - - if(spot_style == DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF) { - const vec2 spot_falloff = unpackHalf2x16(global_ubo.dyn_light_data[light_idx].spot_data); - const float cosTotalWidth = spot_falloff.x; - const float cosFalloffStart = spot_falloff.y; - - if(cosTheta < cosTotalWidth) - falloff = 0; - else if (cosTheta > cosFalloffStart) - falloff = 1; - else { - float delta = (cosTheta - cosTotalWidth) / (cosFalloffStart - cosTotalWidth); - falloff = (delta * delta) * (delta * delta); - } - } else if(spot_style == DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE) { - const uint spot_data = global_ubo.dyn_light_data[light_idx].spot_data; - const float theta = acos(cosTheta); - const float totalWidth = unpackHalf2x16(spot_data).x; - const uint texture_num = spot_data >> 16; - - if (cosTheta >= 0) { - // Use the angle directly as texture coordinate for better angular resolution next to the center of the beam - float tc = clamp(theta / totalWidth, 0, 1); - falloff = global_texture(texture_num, vec2(tc, 0)).r; - } else - falloff = 0; - } - - float irradiance = 2 * falloff * square(rdist); - - return irradiance; -} - -void -sample_dynamic_lights( - vec3 p, - vec3 n, - vec3 gn, - float max_solid_angle, - out vec3 position_light, - out vec3 light_color, - vec3 rng) -{ - position_light = vec3(0); - light_color = vec3(0); - - if(global_ubo.num_dyn_lights == 0) - return; - - float random_light = rng.x * global_ubo.num_dyn_lights; - uint light_idx = min(global_ubo.num_dyn_lights - 1, uint(random_light)); - - vec3 light_center = global_ubo.dyn_light_data[light_idx].center; - - light_color = global_ubo.dyn_light_data[light_idx].color; - - uint light_type = global_ubo.dyn_light_data[light_idx].type & 0xffff; - uint light_style = global_ubo.dyn_light_data[light_idx].type >> 16; - - float irradiance; - if(light_type == DYNLIGHT_SPHERE) { - irradiance = compute_dynlight_sphere(light_idx, light_center, p, position_light, rng); - } else { - irradiance = compute_dynlight_spot(light_idx, light_style, light_center, p, position_light, rng); - } - irradiance = min(irradiance, max_solid_angle); - irradiance *= float(global_ubo.num_dyn_lights); // 1 / pdf - - light_color *= irradiance; - - if(dot(position_light - p, gn) <= 0) - light_color = vec3(0); -} - #endif /*_LIGHT_LISTS_*/ // vim: shiftwidth=4 noexpandtab tabstop=4 cindent diff --git a/src/refresh/vkpt/shader/path_tracer_rgen.h b/src/refresh/vkpt/shader/path_tracer_rgen.h index 21cdac6a8..3f42ea7ec 100644 --- a/src/refresh/vkpt/shader/path_tracer_rgen.h +++ b/src/refresh/vkpt/shader/path_tracer_rgen.h @@ -52,15 +52,18 @@ uniform accelerationStructureEXT topLevelAS[TLAS_COUNT]; #define RNG_PRIMARY_APERTURE_X 2 #define RNG_PRIMARY_APERTURE_Y 3 -#define RNG_NEE_LIGHT_SELECTION(bounce) (4 + 0 + 9 * bounce) -#define RNG_NEE_TRI_X(bounce) (4 + 1 + 9 * bounce) -#define RNG_NEE_TRI_Y(bounce) (4 + 2 + 9 * bounce) -#define RNG_NEE_LIGHT_TYPE(bounce) (4 + 3 + 9 * bounce) -#define RNG_BRDF_X(bounce) (4 + 4 + 9 * bounce) -#define RNG_BRDF_Y(bounce) (4 + 5 + 9 * bounce) -#define RNG_BRDF_FRESNEL(bounce) (4 + 6 + 9 * bounce) -#define RNG_SUNLIGHT_X(bounce) (4 + 7 + 9 * bounce) -#define RNG_SUNLIGHT_Y(bounce) (4 + 8 + 9 * bounce) +#define RNG_NEE_LIGHT_SELECTION(bounce) (4 + 0 + 12 * bounce) +#define RNG_NEE_TRI_X(bounce) (4 + 1 + 12 * bounce) +#define RNG_NEE_TRI_Y(bounce) (4 + 2 + 12 * bounce) +#define RNG_NEE_LIGHT_TYPE(bounce) (4 + 3 + 12 * bounce) +#define RNG_BRDF_X(bounce) (4 + 4 + 12 * bounce) +#define RNG_BRDF_Y(bounce) (4 + 5 + 12 * bounce) +#define RNG_BRDF_FRESNEL(bounce) (4 + 6 + 12 * bounce) +#define RNG_SUNLIGHT_X(bounce) (4 + 7 + 12 * bounce) +#define RNG_SUNLIGHT_Y(bounce) (4 + 8 + 12 * bounce) +#define RNG_RESTIR_SP_LIGHT_SELECTION(bounce) (4 + 9 + 12 * bounce) +#define RNG_RESTIR_SPATIAL_X(bounce) (4 + 10 + 12 * bounce) +#define RNG_RESTIR_SPATIAL_Y(bounce) (4 + 11 + 12 * bounce) #define PRIMARY_RAY_CULL_MASK (AS_FLAG_OPAQUE | AS_FLAG_TRANSPARENT | AS_FLAG_VIEWER_WEAPON | AS_FLAG_SKY) #define REFLECTION_RAY_CULL_MASK (AS_FLAG_OPAQUE | AS_FLAG_SKY) @@ -656,6 +659,28 @@ get_specular_sampled_lighting_weight(float roughness, vec3 N, vec3 V, vec3 L, fl return clamp(pdfw / (pdfw + ggxVndfPdf), 0, 1); } +float get_unshadowed_env_path_contrib( + vec3 normal, + vec3 view_direction, + float phong_exp, + float phong_scale, + float phong_weight, + vec2 rng) +{ + vec3 direction = global_ubo.sun_direction; + float NoL = dot(direction , normal); + if(NoL <= 0.0001) return 0.0; + + float specular = phong(normal, direction, view_direction, phong_exp) * phong_scale; + float m = mix(1.0, specular, phong_weight); + + float light_lum = sun_color_ubo.sun_luminance;// / global_ubo.sun_solid_angle; + + m *= abs(light_lum); // abs because sky lights have negative color + + return m; +} + void get_direct_illumination( vec3 position, @@ -682,19 +707,17 @@ get_direct_illumination( diffuse = vec3(0); specular = vec3(0); - vec3 pos_on_light_polygonal; - vec3 pos_on_light_dynamic; + vec3 pos_on_light; - vec3 contrib_polygonal = vec3(0); - vec3 contrib_dynamic = vec3(0); + vec3 contrib = vec3(0); float alpha = square(roughness); float phong_exp = RoughnessSquareToSpecPower(alpha); float phong_scale = min(100, 1 / (M_PI * square(alpha))); float phong_weight = clamp(specular_factor * luminance(base_reflectivity) / (luminance(base_reflectivity) + luminance(albedo)), 0, 0.9); - int polygonal_light_index = -1; - float polygonal_light_pdfw = 0; + int light_index = -1; + float light_pdfw = 0; bool polygonal_light_is_sky = false; vec3 rng = vec3( @@ -703,9 +726,9 @@ get_direct_illumination( get_rng(RNG_NEE_TRI_Y(bounce))); /* polygonal light illumination */ - if(enable_polygonal) + if(enable_polygonal || enable_dynamic) { - sample_polygonal_lights( + sample_lights( cluster_idx, position, normal, @@ -715,51 +738,24 @@ get_direct_illumination( phong_scale, phong_weight, is_gradient, - pos_on_light_polygonal, - contrib_polygonal, - polygonal_light_index, - polygonal_light_pdfw, + pos_on_light, + contrib, + light_index, + light_pdfw, polygonal_light_is_sky, rng); } bool is_polygonal = true; - float vis = 1; + float vis = 1.0; - /* dynamic light illumination */ - if(enable_dynamic) - { - // Limit the solid angle of sphere lights for indirect lighting - // in order to kill some fireflies in locations with many sphere lights. - // Example: green wall-lamp corridor in the "train" map. - float max_solid_angle = (bounce == 0) ? 2 * M_PI : 0.02; - - sample_dynamic_lights( - position, - normal, - geo_normal, - max_solid_angle, - pos_on_light_dynamic, - contrib_dynamic, - rng); - } + float spec_polygonal = phong(normal, normalize(pos_on_light - position), view_direction, phong_exp) * phong_scale; - float spec_polygonal = phong(normal, normalize(pos_on_light_polygonal - position), view_direction, phong_exp) * phong_scale; - float spec_dynamic = phong(normal, normalize(pos_on_light_dynamic - position), view_direction, phong_exp) * phong_scale; + float l_polygonal = luminance(abs(contrib)) * mix(1, spec_polygonal, phong_weight); - float l_polygonal = luminance(abs(contrib_polygonal)) * mix(1, spec_polygonal, phong_weight); - float l_dynamic = luminance(abs(contrib_dynamic)) * mix(1, spec_dynamic, phong_weight); - float l_sum = l_polygonal + l_dynamic; + bool null_light = (l_polygonal == 0); - bool null_light = (l_sum == 0); - - float w = null_light ? 0.5 : l_polygonal / (l_polygonal + l_dynamic); - - float rng2 = get_rng(RNG_NEE_LIGHT_TYPE(bounce)); - is_polygonal = (rng2 < w); - vis = is_polygonal ? (1 / w) : (1 / (1 - w)); - vec3 pos_on_light = null_light ? position : (is_polygonal ? pos_on_light_polygonal : pos_on_light_dynamic); - vec3 contrib = is_polygonal ? contrib_polygonal : contrib_dynamic; + pos_on_light = null_light ? position : pos_on_light; Ray shadow_ray = get_shadow_ray(position - view_direction * 0.01, pos_on_light, 0); @@ -788,12 +784,11 @@ get_direct_illumination( between frames. */ if(global_ubo.pt_light_stats != 0 - && is_polygonal && !null_light - && polygonal_light_index >= 0 - && polygonal_light_index < global_ubo.num_static_lights) + && light_index >= 0 + && light_index < global_ubo.num_static_lights) { - uint addr = get_light_stats_addr(cluster_idx, polygonal_light_index, get_primary_direction(normal)); + uint addr = get_light_stats_addr(cluster_idx, light_index, get_primary_direction(normal)); // Offset 0 is unshadowed rays, // Offset 1 is shadowed rays @@ -811,7 +806,7 @@ get_direct_illumination( vec3 L = pos_on_light - position; L = normalize(L); - if(is_polygonal && direct_specular_weight > 0 && polygonal_light_is_sky && global_ubo.pt_specular_mis != 0) + if(direct_specular_weight > 0 && polygonal_light_is_sky && global_ubo.pt_specular_mis != 0) { // MIS with direct specular and indirect specular. // Only applied to sky lights, for two reasons: @@ -819,7 +814,7 @@ get_direct_illumination( // 2) Non-sky lights are usually away from walls, so the direct sampling issue is not as pronounced. direct_specular_weight *= get_specular_sampled_lighting_weight(roughness, - normal, -view_direction, L, polygonal_light_pdfw); + normal, -view_direction, L, light_pdfw); } vec3 F = vec3(0); @@ -837,6 +832,7 @@ get_direct_illumination( diffuse = radiance * diffuse_brdf * (vec3(1.0) - F); } + void get_sunlight( uint cluster_idx, diff --git a/src/refresh/vkpt/shader/restir.h b/src/refresh/vkpt/shader/restir.h new file mode 100644 index 000000000..f7642ebf4 --- /dev/null +++ b/src/refresh/vkpt/shader/restir.h @@ -0,0 +1,393 @@ +/* +Copyright (C) 2018 Christoph Schied +Copyright (C) 2018 Tobias Zirr +Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved. +Copyright (C) 2022 Jorge Gustavo Martinez + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +// ========================================================================== // +// This rgen shader computes direct lighting for the first opaque surface. +// The parameters of that surface are loaded from the G-buffer, stored there +// previously by the `primary_rays.rgen` and `reflect_refract.rgen` shaders. +// +// See `path_tracer.h` for an overview of the path tracer. +// ========================================================================== // + +#ifndef _RESTIR_H_ +#define _RESTIR_H_ + +#include "path_tracer_rgen.h" + +#define RESTIR_INVALID_ID 0xFFFF +#define RESTIR_ENV_ID 0xFFFE + +#define RESTIR_SPACIAL_DISTANCE 32 +#define RESTIR_SPACIAL_SAMPLES 8 + +#define RESTIR_SAMPLING_M 4 +#define RESTIR_M_CLAMP 32 +#define RESTIR_M_VC_CLAMP 16 + +struct Reservoir +{ + uint y; + uint M; + float w_sum; + float W; + float p_hat; + vec2 y_pos; +}; + +void +init_reservoir(inout Reservoir r) +{ + r.y = RESTIR_INVALID_ID; + r.M = 0; + r.w_sum = 0.0; + r.W = 0.0; + r.p_hat = 0.0; + r.y_pos = vec2(0.0); +} + +bool +update_reservoir(uint xi, float wi, vec2 xi_pos, uint M, float p_hat, inout float rng, inout Reservoir r) +{ + r.w_sum += wi; + r.M += M; + float p_s = r.w_sum > 0.0 ? (wi/r.w_sum) : 0.0; + if(rng < p_s) + { + r.y = xi; + r.y_pos = xi_pos; + r.p_hat = p_hat; + rng /= p_s; + return true; + } + else + { + rng = (rng - p_s) / (1.0f - p_s); + return false; + } +} + +uvec4 +pack_reservoir(Reservoir r) +{ + uvec4 vec; + r.W = r.y == RESTIR_INVALID_ID ? 0.0 : r.W; + vec.x = packHalf2x16(vec2(r.W, r.W)); + vec.x = (vec.x & 0xFFFF0000) | r.y; + vec.y = packHalf2x16(r.y_pos); + return vec; +} + +void +unpack_reservoir(uvec4 packed, out Reservoir r) +{ + vec2 val = unpackHalf2x16(packed.x); + r.y = packed.x & 0xFFFF; + r.W = val.y; + if(isnan(r.W) || isinf(r.W)) r.W = 0.0; + r.y_pos = unpackHalf2x16(packed.y); + r.p_hat = 0.0; + r.M = r.y == RESTIR_INVALID_ID ? 0 : (global_ubo.pt_restir != 3 ? RESTIR_M_CLAMP : RESTIR_M_VC_CLAMP); + r.w_sum = 0; +} + +// Functions + +uint +get_light_current_idx(uint index) +{ + if (index < global_ubo.num_static_lights || index == RESTIR_INVALID_ID || index == RESTIR_ENV_ID) + { + return index; + } + else + { + uint light_id_curr = instance_buffer.mlight_prev_to_current[index - global_ubo.num_static_lights]; + if(light_id_curr != ~0u) return light_id_curr + global_ubo.num_static_lights; + else + { + return RESTIR_INVALID_ID; + } + } +} + + +float +get_unshadowed_path_contrib( + uint light_idx, + vec3 position, + vec3 normal, + vec3 view_direction, + float phong_exp, + float phong_scale, + float phong_weight, + vec2 rng) +{ + if(light_idx == RESTIR_ENV_ID) return get_unshadowed_env_path_contrib(normal,view_direction, phong_exp, phong_scale, phong_weight, rng); + LightPolygon light = get_light_polygon(light_idx); + + float m = 0.0f; + switch(uint(light.type)) + { + case LIGHT_POLYGON: + m = projected_tri_area(light.positions, position, normal, view_direction, phong_exp, phong_scale, phong_weight); + break; + case LIGHT_SPHERE: + m = projected_sphere_area(light.positions, position, normal, view_direction, phong_exp, phong_scale, phong_weight); + break; + case LIGHT_SPOT: + m = projected_spotlight_area(light.positions, position, normal, view_direction, phong_exp, phong_scale, phong_weight); + break; + } + + float light_lum = luminance(light.color); + + // Apply light style scaling. + light_lum *= light.light_style_scale; + + if(light_lum < 0 && global_ubo.environment_type == ENVIRONMENT_DYNAMIC) + { + // Set limits on sky luminance to avoid oversampling the sky in shadowed areas, or undersampling at dusk and dawn. + // Note: the log -> linear conversion of the cvars happens on the CPU, in main.c + m *= clamp(sun_color_ubo.sky_luminance, global_ubo.pt_min_log_sky_luminance, global_ubo.pt_max_log_sky_luminance); + } + else + m *= abs(light_lum); // abs because sky lights have negative color + + return m; +} + + +void +process_selected_light_restir( + uint light_idx, + vec2 light_position, + float weight, + vec3 position, + vec3 normal, + vec3 geo_normal, + int shadow_cull_mask, + vec3 view_direction, + vec3 base_reflectivity, + float specular_factor, + float roughness, + int surface_medium, + bool enable_caustics, + float direct_specular_weight, + float phong_exp, + float phong_scale, + float phong_weight, + bool check_vis, + uint cluster_idx, + out vec3 diffuse, + out vec3 specular, + out float vis) +{ + float polygonal_light_pdfw = 0; + vec3 contrib_polygonal = vec3(0); + vec3 L, pos_on_light_polygonal; + bool polygonal_light_is_sky = false; + diffuse = vec3(0); + specular = vec3(0); + vis = 1.0; + + if(light_idx != RESTIR_ENV_ID) + { + LightPolygon light = get_light_polygon(light_idx); + + vec3 light_normal; + + switch(uint(light.type)) + { + case LIGHT_POLYGON: + pos_on_light_polygonal = sample_projected_triangle(position, light.positions, light_position , light_normal, polygonal_light_pdfw); + break; + case LIGHT_SPHERE: + pos_on_light_polygonal = sample_projected_sphere(position, light.positions, light_position , light_normal, polygonal_light_pdfw); + break; + case LIGHT_SPOT: + pos_on_light_polygonal = sample_projected_spotlight(position, light.positions, light_position , light_normal, polygonal_light_pdfw); + break; + } + + L = normalize(pos_on_light_polygonal - position); + + if(dot(L, geo_normal) <= 0) + polygonal_light_pdfw = 0; + + if(polygonal_light_pdfw > 0){ + float LdotNL = max(0, -dot(light_normal, L)); + float spotlight = sqrt(LdotNL); + float inv_pdfw = 1.0 / polygonal_light_pdfw; + + if(light.color.r >= 0) + { + contrib_polygonal = light.color * (inv_pdfw * spotlight * light.light_style_scale); + } + else + { + contrib_polygonal = env_map(L, true) * inv_pdfw * global_ubo.pt_env_scale; + polygonal_light_is_sky = true; + } + } + + } + else + { + vec2 disk = sample_disk(light_position); + disk.xy *= global_ubo.sun_tan_half_angle; + L = normalize(global_ubo.sun_direction + global_ubo.sun_tangent * disk.x + global_ubo.sun_bitangent * disk.y); + polygonal_light_pdfw = global_ubo.sun_solid_angle; + pos_on_light_polygonal = position + L * 10000; + contrib_polygonal = env_map(L, false) * polygonal_light_pdfw * global_ubo.pt_env_scale; + } + + contrib_polygonal *= weight; + + float spec_polygonal = phong(normal, L, view_direction, phong_exp) * phong_scale; + + float l_polygonal = luminance(abs(contrib_polygonal)) * mix(1, spec_polygonal, phong_weight); + + bool null_light = (l_polygonal == 0); + + Ray shadow_ray = get_shadow_ray(position - view_direction * 0.01, pos_on_light_polygonal, 0); + + if(check_vis) vis *= trace_shadow_ray(shadow_ray, null_light ? 0 : shadow_cull_mask); + + #ifdef ENABLE_SHADOW_CAUSTICS + if(enable_caustics) + { + contrib_polygonal *= trace_caustic_ray(shadow_ray, surface_medium); + } + #endif + + if(null_light) + { + vis = 0.0f; + return; + } + + vec3 radiance = vis * contrib_polygonal; + + if(direct_specular_weight > 0 && polygonal_light_is_sky && global_ubo.pt_specular_mis != 0) + { + // MIS with direct specular and indirect specular. + // Only applied to sky lights, for two reasons: + // 1) Non-sky lights are trimmed to match the light texture, and indirect rays don't see that; + // 2) Non-sky lights are usually away from walls, so the direct sampling issue is not as pronounced. + + direct_specular_weight *= get_specular_sampled_lighting_weight(roughness, + normal, -view_direction, L, polygonal_light_pdfw); + } + + vec3 F = vec3(0); + + if(vis > 0 && direct_specular_weight > 0) + { + vec3 specular_brdf = GGX_times_NdotL(view_direction, L, + normal, roughness, base_reflectivity, 0.0, specular_factor, F); + specular = radiance * specular_brdf * direct_specular_weight; + } + + float NdotL = max(0, dot(normal, L)); + + float diffuse_brdf = NdotL / M_PI; + diffuse = radiance * diffuse_brdf * (vec3(1.0) - F); +} + + +void +get_direct_illumination_restir( + vec3 position, + vec3 normal, + uint cluster_idx, + vec3 view_direction, + float phong_exp, + float phong_scale, + float phong_weight, + int bounce, + Reservoir prev_r, + out Reservoir reservoir) +{ + init_reservoir(reservoir); + + if(cluster_idx == ~0u) + return; + + vec3 contrib_polygonal = vec3(0); + + float rng, p_hat; + + uint list_start = light_buffer.light_list_offsets[cluster_idx]; + uint list_end = light_buffer.light_list_offsets[cluster_idx + 1]; + + rng = get_rng(RNG_NEE_LIGHT_SELECTION(bounce)); + + uint add_sun = (global_ubo.sun_visible != 0) && ((cluster_idx == ~0u) || (light_buffer.sky_visibility[cluster_idx >> 5] & (1 << (cluster_idx & 31))) != 0) ? 1 : 0; + + uint sun_idx = add_sun > 0 ? list_end : -1; + list_end += add_sun; + float list_size = float(list_end - list_start); + float partitions = ceil(list_size / float(RESTIR_SAMPLING_M)); + float inv_pdf = list_size; + float rng_part = rng * partitions; + float fpart = min(floor(rng_part), partitions-1); + + list_start += int(fpart); + int stride = int(partitions); + rng = rng_part - floor(rng_part); + + uint current_idx, current_light_idx; + + vec2 rng2 = vec2( + get_rng(RNG_NEE_TRI_X(bounce)), + get_rng(RNG_NEE_TRI_Y(bounce))); + + float samples = 1.; + + #pragma unroll + for(uint i = 0, n_idx = list_start; i < RESTIR_SAMPLING_M; i++, n_idx += stride) + { + if (n_idx >= list_end) + break; + + current_light_idx = n_idx != sun_idx ? light_buffer.light_list_lights[n_idx] : RESTIR_ENV_ID; + + if(current_light_idx == ~0u) continue; + + p_hat = get_unshadowed_path_contrib(current_light_idx, position, normal, view_direction, phong_exp, phong_scale, phong_weight, rng2); + if(p_hat > 0) + update_reservoir(current_light_idx, p_hat * inv_pdf, rng2, 1, p_hat, rng, reservoir); + } + + reservoir.M = RESTIR_SAMPLING_M; + + //Combine with temporal + if(prev_r.y != RESTIR_INVALID_ID) + { + update_reservoir(prev_r.y, prev_r.p_hat * prev_r.W * prev_r.M ,prev_r.y_pos, prev_r.M, prev_r.p_hat, rng, reservoir); + } + + reservoir.W = reservoir.w_sum / (reservoir.p_hat * reservoir.M); + if(isnan(reservoir.W) || isinf(reservoir.W)) reservoir.W = 0.0; +} + + +#endif /*_RESTIR_H_*/ diff --git a/src/refresh/vkpt/shader/vertex_buffer.h b/src/refresh/vkpt/shader/vertex_buffer.h index 07cf1aaa1..5142f20d3 100644 --- a/src/refresh/vkpt/shader/vertex_buffer.h +++ b/src/refresh/vkpt/shader/vertex_buffer.h @@ -179,10 +179,24 @@ struct MaterialInfo struct LightPolygon { + /* Meaning of positions depends on light type: + * - static/poly/triangle light: actual positions of vertices + * - dynamic light: + * - all types: + * positions[0]: light origin + * positions[1].x: radius + * - spot light: + * positions[1].y: emission profile (uint reinterepreted as float) + * positions[1].z: spot light data, meaning depending on emission profile: + * DYNLIGHT_SPOT_EMISSION_PROFILE_FALLOFF -> contains packed2x16 with cosTotalWidth, cosFalloffStart + * DYNLIGHT_SPOT_EMISSION_PROFILE_AXIS_ANGLE_TEXTURE -> contains a half with cosTotalWidth and the texture index + * positions[2]: direction + */ mat3 positions; vec3 color; float light_style_scale; float prev_style_scale; + float type; }; // The buffers with primitive data, currently two of them: world and instanced. @@ -478,6 +492,7 @@ get_light_polygon(uint index) light.color = vec3(p0.w, p1.w, p2.w); light.light_style_scale = p3.x; light.prev_style_scale = p3.y; + light.type = p3.z; return light; } diff --git a/src/refresh/vkpt/transparency.c b/src/refresh/vkpt/transparency.c index 16b81113a..21e48d1a5 100644 --- a/src/refresh/vkpt/transparency.c +++ b/src/refresh/vkpt/transparency.c @@ -468,7 +468,7 @@ static int compare_beams(const void* _a, const void* _b) return 0; } -bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, vec3_t begin, vec3_t end, vec3_t color, float radius) +bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, vec3_t begin, vec3_t end, vec3_t color, float radius, entity_hash_t hash, int* light_entity_ids) { vec3_t dir, norm_dir; VectorSubtract(end, begin, dir); @@ -540,11 +540,14 @@ bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int ma light->cluster = BSP_PointLeaf(bsp->nodes, light->off_center)->cluster; light->material = NULL; light->style = 0; + light->type = LIGHT_POLYGON; VectorCopy(color, light->color); if (light->cluster >= 0) { + hash.mesh = tri; + light_entity_ids[(*num_lights)] = *(uint32_t*)&hash; (*num_lights)++; } } @@ -552,7 +555,7 @@ bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int ma return true; } -void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, entity_t* entities, int num_entites, float adapted_luminance) +void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, entity_t* entities, int num_entites, float adapted_luminance, int* light_entity_ids, int* num_light_entities) { const float hdr_factor = cvar_pt_beam_lights->value * adapted_luminance * 20.f; @@ -584,6 +587,10 @@ void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_l const entity_t* beam = beams[i]; + entity_hash_t hash; + hash.entity = (beams[i] - entities) + 1; //entity ID + hash.model = RF_BEAM; + // Adjust beam width. Default "narrow" beams have a width of 4, "fat" beams have 16. if (beam->frame == 0) continue; @@ -606,7 +613,7 @@ void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_l vec3_t color; cast_u32_to_f32_color(beam->skinnum, &beam->rgba, color, hdr_factor); - vkpt_build_cylinder_light(light_list, num_lights, max_lights, bsp, begin, end, color, beam_radius); + vkpt_build_cylinder_light(light_list, num_lights, max_lights, bsp, begin, end, color, beam_radius, hash, light_entity_ids); } } diff --git a/src/refresh/vkpt/vertex_buffer.c b/src/refresh/vkpt/vertex_buffer.c index d91a2cf48..a7db66b57 100644 --- a/src/refresh/vkpt/vertex_buffer.c +++ b/src/refresh/vkpt/vertex_buffer.c @@ -635,7 +635,7 @@ copy_light(const light_poly_t* light, float* vblight, const float* sky_radiance) vblight[12] = style_scale; vblight[13] = prev_style; - vblight[14] = 0.f; + vblight[14] = light->type; vblight[15] = 0.f; } diff --git a/src/refresh/vkpt/vkpt.h b/src/refresh/vkpt/vkpt.h index f43508c8f..ff7ea61d4 100644 --- a/src/refresh/vkpt/vkpt.h +++ b/src/refresh/vkpt/vkpt.h @@ -460,6 +460,13 @@ typedef struct sun_light_s { bool visible; } sun_light_t; +typedef struct entity_hash_s { + unsigned int mesh : 8; + unsigned int model : 9; + unsigned int entity : 14; + unsigned int bsp : 1; +} entity_hash_t; + void mult_matrix_matrix(mat4_t p, const mat4_t a, const mat4_t b); void mult_matrix_vector(vec4_t v, const mat4_t a, const vec4_t b); void create_entity_matrix(mat4_t matrix, entity_t *e); @@ -765,8 +772,8 @@ VkBufferView get_transparency_beam_color_buffer_view(void); VkBufferView get_transparency_sprite_info_buffer_view(void); VkBufferView get_transparency_beam_intersect_buffer_view(void); void get_transparency_counts(int* particle_num, int* beam_num, int* sprite_num); -void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, entity_t* entities, int num_entites, float adapted_luminance); -bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, vec3_t begin, vec3_t end, vec3_t color, float radius); +void vkpt_build_beam_lights(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, entity_t* entities, int num_entites, float adapted_luminance, int* light_entity_ids, int* num_light_entities); +bool vkpt_build_cylinder_light(light_poly_t* light_list, int* num_lights, int max_lights, bsp_t *bsp, vec3_t begin, vec3_t end, vec3_t color, float radius, entity_hash_t hash, int* light_entity_ids); bool get_triangle_off_center(const float* positions, float* center, float* anti_center, float offset); VkResult vkpt_initialize_god_rays(void);