OverviewFilesCommitsIssuesDocsReleasesPeers

nvidia-rtx-godot commits / 343336d

Skip detail-map sampling on indirect bounces, and expose bounce depth to shaders
A path tracer evaluates the material once per bounce per sample, so with pt_bounces up to 8 the per-hit material cost is multiplied accordingly. Indirect bounces reach the image only through the throughput, where the detail maps are not worth what they cost. Default hit group: the normal-map fetch and the ORM fetch now happen only on the camera-visible hit. Past it the shading falls back to the geometric normal and the material's scalar roughness/metallic -- two texture fetches saved per hit per bounce. Albedo and emission are still sampled at every depth, since they carry the colour of bounced light and dropping them would visibly change the result. Custom hit groups are opaque to the engine -- their fragment code is user shader source, so the engine cannot pick which lookups to skip. They instead get two built-ins to make the same decision themselves: BOUNCE_INDEX uint, 0 on the camera-visible hit PRIMARY_HIT bool, BOUNCE_INDEX == 0 Both are RT-only and absent under rasterization. packed_bounces_flags is a plain field on the payload, so reading it is a mask, not a path_unpack. This lets dc_terrain's shader drop its four textureGrad atlas lookups on indirect bounces, which is where most of its texture work goes; that change is separate and lives in dc-terrain. Verified by compiling the real closest-hit stage offline in both variants: the default path, and a custom hit group exercising PRIMARY_HIT and BOUNCE_INDEX. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
343336dba9d928d57752d2c9d361e14c75abb552
by plankatron <fleeky@gmail.com> · 2026-07-29T18:12:22.000Z

Changed files (4) +37 -4

servers/rendering/renderer_rd/forward_clustered/scene_shader_raytracing.cpp +7 -0
……@@ -1567,6 +1567,13 @@ void SceneShaderRaytracing::init(const String p_defines) {
15671567 actions.renames["CUSTOM3"] = "custom3_attrib";
15681568 actions.renames["LIGHT_VERTEX"] = "light_vertex";
15691569
1570 // RT-only. A path tracer evaluates the material once per bounce per
1571 // sample, so letting a shader cheapen itself away from the camera-visible
1572 // hit scales with pt_bounces. Absent under rasterization, so guard uses
1573 // behind the RT define.
1574 actions.renames["BOUNCE_INDEX"] = "rt_bounce_index";
1575 actions.renames["PRIMARY_HIT"] = "rt_primary_hit";
1576
15701577 actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
15711578 actions.renames["CAMERA_POSITION_WORLD"] = "inv_view_matrix[3].xyz";
15721579 actions.renames["CAMERA_DIRECTION_WORLD"] = "inv_view_matrix[2].xyz";
servers/rendering/renderer_rd/shaders/raytracing/raytracing_custom_fragment_inc.glsl +5 -0
……@@ -17,6 +17,11 @@
1717MaterialData rt_mat = materials[rt_geometry_idx];
1818material = CustomMaterialUniforms(rt_mat.uniform_address);
1919
20// packed_bounces_flags is a plain field on the payload, so this is a mask rather
21// than a full path_unpack.
22rt_bounce_index = get_total_bounces(payload.packed_bounces_flags);
23rt_primary_hit = (rt_bounce_index == 0u);
24
2025// Matrices.
2126mat4 rt_view_matrix = transpose(mat4(scene_data_block.data.view_matrix[0],
2227 scene_data_block.data.view_matrix[1],
servers/rendering/renderer_rd/shaders/raytracing/raytracing_custom_globals_inc.glsl +9 -0
……@@ -21,6 +21,15 @@ vec4 color_interp = vec4(1.0);
2121// SceneShaderRaytracing), so they must exist even when the surface has no such
2222// array -- otherwise any shader touching CUSTOM0 fails to compile and takes its
2323// whole hit group down.
24// Path depth of the hit being shaded. 0 is the camera-visible hit; anything
25// higher is an indirect bounce contributing through the throughput, where a
26// cheaper material is usually indistinguishable. Shaders can use these to skip
27// detail work on secondary bounces -- in a path tracer the material is evaluated
28// once per bounce per sample, so that scales with pt_bounces.
29// Exposed to GDShader as BOUNCE_INDEX and PRIMARY_HIT.
30uint rt_bounce_index = 0u;
31bool rt_primary_hit = true;
32
2433vec4 custom0_attrib = vec4(0.0);
2534vec4 custom1_attrib = vec4(0.0);
2635vec4 custom2_attrib = vec4(0.0);
servers/rendering/renderer_rd/shaders/raytracing/scene_raytracing_raygen.glsl +16 -4
……@@ -396,7 +396,15 @@ void main() {
396396 // Normal mapping.
397397 vec3 tangent_space_normal = vec3(0.0, 0.0, 1.0);
398398 vec3 final_normal = h.geometry_normal;
399 if ((mat.flags & 1u) != 0u) {
399 // Indirect bounces reach the image only through the throughput, so the detail
400 // maps are not worth their cost there: the material is evaluated once per
401 // bounce per sample. Albedo and emission still matter (they carry the colour
402 // of bounced light), but the normal map and the ORM lookup are skipped past
403 // the camera-visible hit, falling back to the geometric normal and the
404 // material's scalar roughness/metallic.
405 bool rt_full_detail = (get_total_bounces(payload.packed_bounces_flags) == 0u);
406
407 if (rt_full_detail && (mat.flags & 1u) != 0u) {
400408 vec3 normal_sample = sample_bindless_texture(mat.normal_texture_idx, uv).rgb;
401409 tangent_space_normal.xy = normal_sample.xy * 2.0 - 1.0;
402410 tangent_space_normal.z = sqrt(max(0.0, 1.0 - dot(tangent_space_normal.xy, tangent_space_normal.xy)));
……@@ -406,9 +414,13 @@ void main() {
406414 // Texture sampling.
407415 vec4 albedo_tex = sample_material_texture(mat.albedo_texture_idx, uv, mat.flags);
408416 vec3 albedo = albedo_tex.rgb * mat.albedo_color.rgb;
409 vec3 orm = sample_material_texture(mat.orm_texture_idx, uv, mat.flags).rgb;
410 float roughness = saturate(orm.g * mat.roughness);
411 float metalness = saturate(orm.b * mat.metallic);
417 float roughness = saturate(mat.roughness);
418 float metalness = saturate(mat.metallic);
419 if (rt_full_detail) {
420 vec3 orm = sample_material_texture(mat.orm_texture_idx, uv, mat.flags).rgb;
421 roughness = saturate(orm.g * mat.roughness);
422 metalness = saturate(orm.b * mat.metallic);
423 }
412424
413425 vec3 emissive = vec3(0.0);
414426 if ((mat.flags & 2u) != 0u) {