| … | … | @@ -1567,6 +1567,13 @@ void SceneShaderRaytracing::init(const String p_defines) { |
| 1567 | 1567 | actions.renames["CUSTOM3"] = "custom3_attrib"; |
| 1568 | 1568 | actions.renames["LIGHT_VERTEX"] = "light_vertex"; |
| 1569 | 1569 | |
| 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 | ||
| 1570 | 1577 | actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; |
| 1571 | 1578 | actions.renames["CAMERA_POSITION_WORLD"] = "inv_view_matrix[3].xyz"; |
| 1572 | 1579 | actions.renames["CAMERA_DIRECTION_WORLD"] = "inv_view_matrix[2].xyz"; |
| … | … | @@ -17,6 +17,11 @@ |
| 17 | 17 | MaterialData rt_mat = materials[rt_geometry_idx]; |
| 18 | 18 | material = CustomMaterialUniforms(rt_mat.uniform_address); |
| 19 | 19 | |
| 20 | // packed_bounces_flags is a plain field on the payload, so this is a mask rather | |
| 21 | // than a full path_unpack. | |
| 22 | rt_bounce_index = get_total_bounces(payload.packed_bounces_flags); | |
| 23 | rt_primary_hit = (rt_bounce_index == 0u); | |
| 24 | ||
| 20 | 25 | // Matrices. |
| 21 | 26 | mat4 rt_view_matrix = transpose(mat4(scene_data_block.data.view_matrix[0], |
| 22 | 27 | scene_data_block.data.view_matrix[1], |
| … | … | @@ -21,6 +21,15 @@ vec4 color_interp = vec4(1.0); |
| 21 | 21 | // SceneShaderRaytracing), so they must exist even when the surface has no such |
| 22 | 22 | // array -- otherwise any shader touching CUSTOM0 fails to compile and takes its |
| 23 | 23 | // 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. | |
| 30 | uint rt_bounce_index = 0u; | |
| 31 | bool rt_primary_hit = true; | |
| 32 | ||
| 24 | 33 | vec4 custom0_attrib = vec4(0.0); |
| 25 | 34 | vec4 custom1_attrib = vec4(0.0); |
| 26 | 35 | vec4 custom2_attrib = vec4(0.0); |
| … | … | @@ -396,7 +396,15 @@ void main() { |
| 396 | 396 | // Normal mapping. |
| 397 | 397 | vec3 tangent_space_normal = vec3(0.0, 0.0, 1.0); |
| 398 | 398 | 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) { | |
| 400 | 408 | vec3 normal_sample = sample_bindless_texture(mat.normal_texture_idx, uv).rgb; |
| 401 | 409 | tangent_space_normal.xy = normal_sample.xy * 2.0 - 1.0; |
| 402 | 410 | 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() { |
| 406 | 414 | // Texture sampling. |
| 407 | 415 | vec4 albedo_tex = sample_material_texture(mat.albedo_texture_idx, uv, mat.flags); |
| 408 | 416 | 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 | } | |
| 412 | 424 | |
| 413 | 425 | vec3 emissive = vec3(0.0); |
| 414 | 426 | if ((mat.flags & 2u) != 0u) { |