Q2RTX codepath

02 Jul 2026

Reading time ~12 minutes

…

Q2RTX codepath

#define MATERIAL_KIND_MASK           0xf0000000
#define MATERIAL_KIND_INVALID        0x00000000
#define MATERIAL_KIND_REGULAR        0x10000000
#define MATERIAL_KIND_CHROME         0x20000000
#define MATERIAL_KIND_WATER          0x30000000
#define MATERIAL_KIND_LAVA           0x40000000
#define MATERIAL_KIND_SLIME          0x50000000
#define MATERIAL_KIND_GLASS          0x60000000
#define MATERIAL_KIND_SKY            0x70000000
#define MATERIAL_KIND_INVISIBLE      0x80000000
#define MATERIAL_KIND_EXPLOSION      0x90000000
#define MATERIAL_KIND_TRANSPARENT    0xa0000000 // Transparent walls. Have a distortion effect applied.
#define MATERIAL_KIND_SCREEN         0xb0000000
#define MATERIAL_KIND_CAMERA         0xc0000000
#define MATERIAL_KIND_CHROME_MODEL   0xd0000000
#define MATERIAL_KIND_TRANSP_MODEL   0xe0000000 // Transparent models. No distortion, just "see through".


struct RayPayloadGeometry {
   vec2 barycentric;
   /* two packed 16 bit integers, buffer index in low 16 bits and
    * instance index in high 16 bits */
   int buffer_and_instance_idx;
   uint primitive_id;
   float hit_distance;
};

struct RayPayloadEffects {
   uvec2 transparency; // half4x16
   uint distances; // half2x16 - min and max
   uvec4 fog1; // half8x16: .xy = color.rgba; .z = t_min, t_max; .w = density: a and b for (a*t + b)
   uvec4 fog2; // same as fog1 but for a fog volume further away
#ifndef KHR_RAY_QUERY
   // Store TMax in the payload because gl_RayTmaxEXT changes while the ray is being traced.
   // See the GLSL_EXT_ray_tracing spec near "description for gl_RayTminEXT and gl_RayTmaxEXT"
   // https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GLSL_EXT_ray_tracing.txt 
   float rayTmax; 
#endif
};

layout(location = RT_PAYLOAD_GEOMETRY) rayPayloadEXT RayPayloadGeometry ray_payload_geometry;
layout(location = RT_PAYLOAD_EFFECTS) rayPayloadEXT RayPayloadEffects ray_payload_effects;
// primary ray
vkpt_pt_trace_primary_rays(trace_cmd_buf);
// qvkCmdTraceRaysKHR(cmd_buf,
//  &raygen,
//  &miss_and_hit,
//  &miss_and_hit,
//  &callable,
//  width/2, height, depth); // 2 depth for checkerboard rendering, split
vkpt_submit_command_buffer();

// indirect reflection/refraction
if (ref_mode.reflect_refract > 0)
    vkpt_pt_trace_reflections(trace_cmd_buf, 0);
    
// multiple reflection/refraction
if (ref_mode.reflect_refract > 1)
    for (int pass = 0; pass < ref_mode.reflect_refract - 1; pass++)
        vkpt_pt_trace_reflections(trace_cmd_buf, pass + 1);

// seperated svgf reprojected pass
vkpt_asvgf_gradient_reproject(trace_cmd_buf);

vkpt_pt_trace_lighting(trace_cmd_buf, ref_mode.num_bounce_rays);

vkpt_submit_command_buffer();

vkpt_asvgf_filter(post_cmd_buf, cvar_pt_num_bounce_rays->value >= 0.5f);

vkpt_interleave(post_cmd_buf);

vkpt_taa(post_cmd_buf);

vkpt_bloom_record_cmd_buffer(post_cmd_buf);
vkpt_tone_mapping_record_cmd_buffer();
vkpt_fsr_do(post_cmd_buf);
vkpt_submit_command_buffer_simple();

primary_rays.rgen(path tracer overview的1)

 // generate primary ray information, including bias offset to get depth of field
Ray ray = get_primary_ray(inUV);


// primary ray -> path_tracer.rchit/path_tracer.rmiss
traceRayEXT( topLevelAS[TLAS_INDEX_GEOMETRY], rayFlags, instance_mask,
        SBT_RCHIT_GEOMETRY /*sbtRecordOffset*/, 0 /*sbtRecordStride*/, SBT_RMISS_EMPTY /*missIndex*/,
        ray.origin, ray.t_min, ray.direction, ray.t_max, RT_PAYLOAD_GEOMETRY);

Triangle triangle;

if (found_intersection(ray_payload_geometry)) // hit surface
{
    ray.t_max = ray_payload_geometry.hit_distance;
    triangle = get_hit_triangle(ray_payload_geometry);
}
// particle/explosion/sprite/beam
traceRayEXT( topLevelAS[TLAS_INDEX_EFFECTS], rayFlags, instance_mask,
        SBT_RCHIT_EFFECTS /*sbtRecordOffset*/, 0 /*sbtRecordStride*/, SBT_RMISS_EMPTY /*missIndex*/,
        ray.origin, ray.t_min, ray.direction, ray.t_max, RT_PAYLOAD_EFFECTS);

vec4 effects = get_payload_transparency_with_fog(ray_payload_effects, ray.t_max);

vec3 bary = get_hit_barycentric(ray_payload_geometry);

// store 2 visibility buffer
{
    uvec2 vis_buf;
    vis_buf.x = triangle.instance_index;
    vis_buf.y = triangle.instance_prim;
    imageStore(IMG_PT_VISBUF_PRIM_A, ipos, uvec4(vis_buf, 0, 0)); // rg32ui / R32G32_UINT
    imageStore(IMG_PT_VISBUF_BARY_A, ipos, vec4(bary.yz, 0, 0)); // rg16f / R16G16_SFLOAT
}

// get hit point information
vec3 position = triangle.positions * bary;
vec2 texcoord = triangle.texcoords * bary;
vec3 geo_normal = triangle.normals* bary;
vec3 flat_normal = normalize(cross(
        triangle.positions[1] - triangle.positions[0],
        triangle.positions[2] - triangle.positions[1]));

/* compute view-space derivatives of depth and motion vectors */
Ray ray_0 = get_primary_ray(inUV);
Ray ray_x = get_primary_ray(inUV + vec2(1.0 / float(global_ubo.width), 0));
Ray ray_y = get_primary_ray(inUV + vec2(0, 1.0 / float(global_ubo.height)));

// larger coneangle means high gradient, means higher mips
float half_cone_angle = sqrt(1.0 - square(min(
dot(ray_0.direction, ray_x.direction), 
dot(ray_0.direction, ray_y.direction))));

vec2 tex_coord_x, tex_coord_y;
float fwidth_depth;
// get texure lod
compute_anisotropic_texture_gradients(position, flat_normal, ray.direction, 
    ray_payload_geometry.hit_distance * half_cone_angle, triangle.positions, 
    triangle.tex_coords, tex_coord, tex_coord_x, tex_coord_y, fwidth_depth);

vec3 pos_ws_curr = position;
vec3 pos_ws_prev = triangle.positions_prev * bary;
vec2 screen_pos_curr, screen_pos_prev;
float distance_curr, distance_prev;
projection_view_to_screen((global_ubo.V * vec4(pos_ws_curr, 1)).xyz, screen_pos_curr, distance_curr, false);
projection_view_to_screen((global_ubo.V_prev * vec4(pos_ws_prev, 1)).xyz, screen_pos_prev, distance_prev, true);

// motion vector
vec3 motion;
motion.xy = screen_pos_prev - screen_pos_curr;
motion.z = distance_prev - distance_curr;

imageStore(IMG_PT_VIEW_DEPTH_A, ipos, vec4(distance_curr));
imageStore(IMG_PT_MOTION, ipos, vec4(motion, fwidth_depth));

// Get the primary surface material parameters
get_material(
    triangle,
    bary,
    tex_coord,
    tex_coord_x,
    tex_coord_y,
    -1,
    geo_normal,
    primary_base_color,
    normal,
    primary_metallic,
    primary_roughness,
    primary_emissive,
    primary_specular_factor);

// get mat id
uint material_id = triangle.material_id;
...

// handle various material one by one

// write to gbuffer
// shading normal
imageStore(IMG_PT_NORMAL_A, ipos, uvec4(encode_normal(normal)));
// geo normal
imageStore(IMG_PT_GEO_NORMAL_A, ipos, uvec4(encode_normal(geo_normal)));
//...
imageStore(IMG_PT_SHADING_POSITION, ipos, vec4(position.xyz, uintBitsToFloat(material_id)));
//...
imageStore(IMG_PT_VIEW_DIRECTION, ipos, vec4(direction, float(checkerboard_flags)));
imageStore(IMG_PT_THROUGHPUT, ipos, vec4(throughput, distance_curr));
imageStore(IMG_PT_BOUNCE_THROUGHPUT, ipos, vec4(1, 1, 1, half_cone_angle));
imageStore(IMG_PT_CLUSTER_A, ipos, ivec4(triangle.cluster));
imageStore(IMG_PT_BASE_COLOR_A, ipos, vec4(primary_base_color, primary_specular_factor));
imageStore(IMG_PT_METALLIC_A, ipos, vec4(primary_metallic, primary_roughness, 0, 0));
imageStore(IMG_PT_GODRAYS_THROUGHPUT_DIST, ipos, vec4(1, 1, 1, distance_curr));
transparent = alpha_blend_premultiplied(effects, transparent);

imageStore(IMG_PT_TRANSPARENT, ipos, transparent);

reflect_refract.rgen

reflect_refract.rgen 会在 G-buffer 当前 surface 是 water / slime / glass / chrome / screen / camera / transparent 等特殊材质时,每像素沿 reflection 或 refraction 方向继续追一条 ray,并用命中的 secondary surface 更新 G-buffer / throughput / medium / motion。下面的 IOR + checkerboard 代码是抽象伪码;真实代码会按 material kind 分支处理 thin/thick glass、water/slime medium、total internal reflection、chrome/screen 只反射、transparent see-through 等情况。

// get shading pos
vec4 position_material = imageLoad(IMG_PT_SHADING_POSITION, ipos);

if(!( primary_is_water || 
      primary_is_slime || 
      primary_is_glass || 
      primary_is_chrome || 
      primary_is_screen || 
      primary_is_camera || 
      primary_is_transparent ))
    return;
    
index_of_refraction = get_mat_ior();
vec3 refracted_direction = refract(direction, normal, index_of_refraction);
float F = pow(1.0 - n_dot_v, 5.0);
do_refraction = is_odd_checkerboard && F < 1;
direction = do_refraction ? refract() : reflect();
correction_factor *= 2; // 补能量
throughput *= correction_factor;
primary_medium = new_medium;//更新介质

int reflection_cull_mask = REFLECTION_RAY_CULL_MASK|xxx;

Ray reflection_ray;
reflection_ray.origin = position;
reflection_ray.direction = direction;

trace_geometry_ray(reflection_ray, backface_culling, reflection_cull_mask);
Triangle triangle;

if (found_intersection(ray_payload_geometry))
{
    reflection_ray.t_max = ray_payload_geometry.hit_distance;
    triangle = get_hit_triangle(ray_payload_geometry);
}

vec4 effects = trace_effects_ray(reflection_ray, /* skip_procedural = */ false);

// almost same with primary ray
...
// update gbuffer

asvgf_gradient_reproject.comp

direct_lighting.rgen// direct diffuse + direct sun shadow

vec3 high_freq, specular;
direct_lighting(ipos, is_odd_checkerboard, high_freq, specular);

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;

vec3 primary_albedo, primary_base_reflectivity;
get_reflectivity(primary_base_color.rgb, primary_metallic, primary_albedo, primary_base_reflectivity);

// precompute phong coeff
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);

/*
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);
*/

//  sample a static light
// 1. 重要性采样:从很多 polygon lights 里选一个 light
// float m = spherical_tri_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight);
// float light_lum = luminance(light.color);
// m *= abs(light_lum);
// 2. 几何采样:在这个 light 的三角形上选一个点
sample_polygonal_lights(
    cluster_idx,
    position, 
    normal, 
    geo_normal, 
    view_direction, 
    phong_exp, 
    phong_scale,
    phong_weight, 
    is_gradient, 
    pos_on_light_polygonal, 
    contrib_polygonal,
    polygonal_light_index,
    polygonal_light_pdfw,
    polygonal_light_is_sky,
    rng);
    // 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 a dynamic light
// dynamic lights 内部是在 num_dyn_lights 中 uniform 选一个 light,pdf = 1 / num_dyn_lights,
// sample_dynamic_lights 里会乘 num_dyn_lights 做 1/pdf 补偿;sphere/spot 的能量用 analytic irradiance 近似。
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_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;

// compute luminance of two light
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_sum == 0);

float w = null_light ? 0.5 : l_polygonal / (l_polygonal + l_dynamic);

// random chose one, polygon or 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;

Ray shadow_ray = get_shadow_ray(position - view_direction * 0.01, pos_on_light, 0);

vis *= trace_shadow_ray(shadow_ray, null_light ? 0 : shadow_cull_mask);

// TODO, Adaptive Shadow Testing for Ray Tracing, Ward 1994 optimization

vec3 radiance = vis * contrib;

vec3 L = pos_on_light - position;
L = normalize(L);

// specular lighting

if(is_polygonal && 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, normalize(pos_on_light - position),
        normal, roughness, base_reflectivity, 0.0, specular_factor, F);
    specular = radiance * specular_brdf * direct_specular_weight;
}

float NdotL = max(0, dot(normal, L));

// diffuse lighting

float diffuse_brdf = NdotL / M_PI;
diffuse = radiance * diffuse_brdf * (vec3(1.0) - F);

high_freq += direct_diffuse;
o_specular += direct_specular;
/*
get_sunlight(
    cluster_idx,
    material_id,
    position,
    normal,
    geo_normal,
    view_direction.xyz,
    primary_base_reflectivity,
    primary_specular_factor,
    primary_roughness,
    primary_medium,
    spec_enable_caustics != 0,
    direct_sun_diffuse,
    direct_sun_specular,
    shadow_cull_mask);
*/
// similar with polygon/dynamic light computation

high_freq += direct_sun_diffuse;
o_specular += direct_sun_specular;

o_specular = demodulate_specular(primary_base_reflectivity, o_specular);

high_freq = clamp_output(high_freq);
o_specular = clamp_output(o_specular);

high_freq *= STORAGE_SCALE_HF;//32
o_specular *= STORAGE_SCALE_SPEC;//32
imageStore(IMG_PT_COLOR_LF_SH, ipos, vec4(0));
imageStore(IMG_PT_COLOR_LF_COCG, ipos, vec4(0));
imageStore(IMG_PT_COLOR_HF, ipos, uvec4(packRGBE(high_freq))); // diffuse
imageStore(IMG_PT_COLOR_SPEC, ipos, uvec4(packRGBE(o_specular))); // specular

indirect_lighting.rgen

// already have primary ray hit surface into
view_direction = texelFetch(TEX_PT_VIEW_DIRECTION, ipos, 0);
normal = decode_normal(texelFetch(TEX_PT_NORMAL_A, ipos, 0).x);
geo_normal = decode_normal(texelFetch(TEX_PT_GEO_NORMAL_A, ipos, 0).x);
primary_base_color = texelFetch(TEX_PT_BASE_COLOR_A, ipos, 0);
primary_specular_factor = primary_base_color.a;
vec2 metal_rough = texelFetch(TEX_PT_METALLIC_A, ipos, 0).xy;
primary_metallic = metal_rough.x;
primary_roughness = metal_rough.y;

get_reflectivity(primary_base_color.rgb, primary_metallic, primary_albedo, primary_base_reflectivity);
float NoV = max(0, -dot(normal, view_direction.xyz));
// 判断光线类型
bool is_specular_ray;

// compute the indirect ray direction towards ggx reflection lobe
if(spec_bounce_index == 0)
{
    specular_pdf = (primary_metallic == 1 && fake_specular_weight == 0) ? 1.0 : 0.5;
    
    // 50%概率进,specular path
    if(rng_frensel < specular_pdf) 
    {
        mat3 basis = construct_ONB_frisvad(normal);

        // Sampling of normal distribution function to compute the reflected ray.
        // See the paper "Sampling the GGX Distribution of Visible Normals" by E. Heitz, 
        // Journal of Computer Graphics Techniques Vol. 7, No. 4, 2018.
        // http://jcgt.org/published/0007/04/01/paper.pdf 

        vec3 N = normal;
        vec3 V = view_direction.xyz;
        vec3 H = ImportanceSampleGGX_VNDF(rng3, primary_roughness, V, basis);
        vec3 L = reflect(V, H);

        float NoL = max(0, dot(N, L));
        float NoH = max(0, dot(N, H));
        float VoH = max(0, -dot(V, H));

        if (NoL > 0 && NoV > 0)
        {
            // See the Heitz paper referenced above for the estimator explanation.
            //   (BRDF / PDF) = F * G2(V, L) / G1(V)
            // Assume G2 = G1(V) * G1(L) here and simplify that expression to just G1(L).
            
            float G1_NoL = G1_Smith(primary_roughness, NoL);
            vec3 F = schlick_fresnel(primary_base_reflectivity, VoH, primary_specular_factor);

            bounce_throughput *= G1_NoL * F;

            bounce_throughput *= 1 / specular_pdf;
            is_specular_ray = true;
            bounce_direction = normalize(L);
        }
    }
}
// 发射diffuse光线
if(!is_specular_ray)
{
    vec3 basis_normal, dir_sphere;
#if ENABLE_SH
    if(spec_bounce_index == 0 && global_ubo.flt_enable != 0)
    {
        dir_sphere = sample_cos_hemisphere_multi(0, 1, rng3, HEMISPHERE_UNIFORMISH);
        basis_normal = geo_normal;
    }
    else
#endif
    {
        dir_sphere = sample_cos_hemisphere(rng3);
        basis_normal = normal;
    }

    mat3 basis = construct_ONB_frisvad(basis_normal);
    bounce_direction = normalize(basis * dir_sphere);
    // diffuse 和 specular 是互斥采样;这里除以选择 diffuse 的概率 (1 - specular_pdf) 做 1/pdf 补偿。
    bounce_throughput *= 1 / (1 - specular_pdf);

    vec3 L = bounce_direction.xyz;
    vec3 V = -view_direction.xyz;
    vec3 H = normalize(V + L);
    float VoH = max(0, dot(V, H));

    vec3 F = schlick_fresnel(primary_base_reflectivity, VoH, primary_specular_factor);

    bounce_throughput *= vec3(1.0) - F;
}

Ray bounce_ray;
bounce_ray.origin = position;
bounce_ray.direction = bounce_direction;
bounce_ray.t_min = 0;
bounce_ray.t_max = 10000;

trace_geometry_ray(bounce_ray, true, bounce_cull_mask);

// specular ray 再 trace 特效
if(is_specular_ray)
{
    if (found_intersection(ray_payload_geometry))
        bounce_ray.t_max = ray_payload_geometry.hit_distance;

    vec4 transparency = trace_effects_ray(bounce_ray, /* skip_procedural = */ true);
    bounce_contrib += transparency.rgb * transparency.a * bounce_throughput * (1.0 - direct_specular_weight);
}
Triangle triangle = get_hit_triangle(ray_payload_geometry);

// hit surface properties
vec3 bary         = get_hit_barycentric(ray_payload_geometry);
vec2 tex_coord    = triangle.tex_coords * bary;
uint bounce_material_id = triangle.material_id;
position;
normal;
geo_normal;
basecolor;
...

 vec3 emissive = sample_emissive_texture(triangle.material_id, bounce_minfo, tex_coord, vec2(0), vec2(0), is_specular_ray ? 2 : 3);
 emissive += get_emissive_shell(triangle.material_id, triangle.shell) * bounce_base_color;
 // other emissive login
 
 // 1 bounce gi
 vec3 bounce_diffuse, bounce_specular;
get_direct_illumination(
    bounce_position, 
    bounce_geo_normal, 
    bounce_geo_normal, 
    bounce_cluster_idx, 
    bounce_material_id, 
    shadow_cull_mask, 
    bounce_direction, 
    bounce_base_color,
    vec3(0), // base_reflectivity
    0.0, // specular_factor
    1.0, // roughness
    MEDIUM_NONE, 
    false, // enable_caustics
    0.0, // direct_specular_weight
    global_ubo.pt_indirect_polygon_lights > 0,
    global_ubo.pt_indirect_dyn_lights > 0,
    is_gradient,
    1, // bounce
    bounce_diffuse,
    bounce_specular);

bounce_contrib += bounce_throughput * bounce_diffuse;

imageStore(IMG_PT_GEO_NORMAL2, ipos, uvec4(encode_normal(bounce_geo_normal)));
imageStore(IMG_PT_SHADING_POSITION, ipos, vec4(bounce_position.xyz, uintBitsToFloat(triangle.material_id)));
imageStore(IMG_PT_VIEW_DIRECTION2, ipos, vec4(bounce_direction, 0));
imageStore(IMG_PT_BOUNCE_THROUGHPUT, ipos, vec4(bounce_throughput, is_specular_ray ? 1 : 0));

// denoise without f0
if (is_specular_ray)
    bounce_contrib = demodulate_specular(primary_base_reflectivity, bounce_contrib); 

if(is_specular_ray)
{
    bounce_contrib *= STORAGE_SCALE_SPEC;

    vec3 specular = unpackRGBE(imageLoad(IMG_PT_COLOR_SPEC, ipos).x);
    specular += bounce_contrib;
    imageStore(IMG_PT_COLOR_SPEC, ipos, uvec4(packRGBE(specular)));
}
else
{
    bounce_contrib *= STORAGE_SCALE_LF;

    SH low_freq = load_SH(TEX_PT_COLOR_LF_SH, TEX_PT_COLOR_LF_COCG, ipos);

#if ENABLE_SH
    if(global_ubo.flt_enable == 0)
        low_freq.shY.xyz += bounce_contrib;
    else
    {
        accumulate_SH(low_freq, irradiance_to_SH(bounce_contrib, bounce_direction), 1.0);
    }
#else
    low_freq.shY.xyz += bounce_contrib;
#endif

    STORE_SH(IMG_PT_COLOR_LF_SH, IMG_PT_COLOR_LF_COCG, ipos, low_freq);
}

总结

Diffuse GI 比较低频,Q2RTX 的 LF 通道不是完整 RGB SH,而是压缩成:

  • PT_COLOR_LF_SH:一阶 SH,也就是 L0 + L1 的 4 个系数,只存 Y/luma 的方向性。
  • PT_COLOR_LF_COCG:存 Co/Cg 色度,不存方向性,近似为平均 tint。
  • RGB 会先转到 YCoCg:Co = R - B,t = B + Co * 0.5,Cg = G - t,Y = t + Cg * 0.5。
  • SH 写入形式是:result.CoCg = vec2(Co, Cg),result.shY = vec4(L11, L1_1, L10, L00) * Y。
  • 后续 ASVGF 滤波的是 SH + CoCg 这个 lighting representation;normal/depth 不被滤波,但会作为 edge-aware guide 参与滤波权重。最终 composite 前再用当前像素 normal 调用 project_SH_irradiance(filtered_lf, normal),把 LF SH 投影回 RGB irradiance。

Checkerboard field 不是单纯半分辨率优化 Q2RTX 把最终屏幕的 checkerboard pixels de-interleave 到左右半屏:左半是 even field,右半是 odd field。这样每个 field 都是 dense image,便于 denoiser 做空间滤波;同时 reflection/refraction 可以分到不同 field,例如一半 field 走 reflection,一半 field 走 refraction。后续再用 checkerboard_interleave.comp 合回 flat image。

Reflect/Refract pass 是 specular guide path,不只是视觉反射 reflect_refract.rgen 会把水、玻璃、chrome、screen 等特殊材质后面看到的 secondary surface 写回 G-buffer,包括 position、normal、base color、material、visbuffer、motion/depth 等。这样后续 direct/indirect lighting 和 denoiser 看到的是“镜子/玻璃后面的表面”,而不是只有 primary glass/mirror surface。

Ray cone / texture LOD 是 secondary ray 质量关键 Primary pass 用相邻像素 primary ray 的夹角估计 half_cone_angle,secondary hit 时用 hit_distance * half_cone_angle 估算 footprint,再调用 compute_anisotropic_texture_gradients 计算纹理 LOD。否则反射/间接命中后的贴图采样会过锐、闪烁或 alias。

Path throughput 是跨 pass 的能量账本 IMG_PT_THROUGHPUT / IMG_PT_BOUNCE_THROUGHPUT 记录 reflection/refraction、medium extinction、Fresnel sampling correction、BRDF sampling correction 等路径权重。后续 lighting pass 算到的 radiance 都要乘这个 throughput 才是对 camera pixel 的贡献。

Specular demodulation 提高 denoiser 稳定性 SPEC channel 在写入前会用 demodulate_specular(base_reflectivity, specular) 除掉材质 F0/specular color,滤波后 composite 再用 modulate_specular 乘回去。这样 denoiser 处理的是更像“光照强度”的信号,而不是光照和金属颜色/贴图细节混在一起的结果。

One-sample NEE + 重要性采样,比遍历所有灯更适合实时 get_direct_illumination 每个 shading point 通常只打一条 shadow ray:先在 polygon lights 中 importance sample 一个候选,再 uniform sample 一个 dynamic light 候选,然后按贡献估计在 polygon/dynamic 之间二选一,并用 1/pdf 补偿。这样一个样本统计上代表整个候选灯集合。

Light shadow statistics 是低成本的下一帧采样反馈 Q2RTX 会统计每个 cluster / light / surface orientation 上 shadow ray 是 unshadowed 还是 shadowed,下一帧在 sample_polygonal_lights 里降低经常被挡住的灯的 mass,但保留下限避免完全不采。

Geometry TLAS 和 Effects TLAS 分离 Q2RTX 把真实几何和粒子、爆炸、sprite、beam 等 effects 放到不同 TLAS。Primary/secondary geometry ray 先找 surface,再用 effects ray 累积透明特效。这样普通 visibility 不必总是遍历 effects,也能给 effects 使用不同 hit shader / any-hit 逻辑。

Half-res GI profile 不是简单降分辨率,而是降低路径复杂度 pt_num_bounce_rays == 0.5 时,indirect pass 只在隔行像素上跑,并且默认不读取真实 metallic/roughness,等价于低质量 diffuse GI profile,避免半分辨率 specular 带来闪烁和错误重建。

Ray Query / RT Pipeline 双路径值得保留抽象边界 Q2RTX 同一套 rgen 逻辑会编译成 .pipeline.spv 和 .query.spv。RT pipeline 路径使用 SBT/hit shaders;Ray Query 路径则在 compute shader 内手动执行查询和 hit 逻辑。上层 pass 调度基本通过同一组 pipeline index 抽象。



Share Tweet +1