Skip to main content

RT_REFLECTIONS

Constant RT_REFLECTIONS 

Source
pub const RT_REFLECTIONS: &str = "// Hardware ray-traced reflections: single source for every backend.\n//\n// A fullscreen fragment pass that, per glossy pixel, rebuilds a world-space\n// surface point + normal from the SSR pre-pass G-buffer, traces a reflection\n// ray against the scene\'s acceleration structure, shades the hit (or falls back\n// to the local reflection probe / IBL prefilter cube on a miss) and writes\n// reflected radiance (.rgb) + the Fresnel/gloss composite weight (.a). The\n// reflection blur + composite then blends it over the scene, exactly as they do\n// for the SSR resolve. Unlike SSR the ray is a real world-space trace, so\n// reflected geometry that is off-screen still appears. Pairs with\n// `fullscreen_vertex` in fullscreen.slang.\n//\n// The traversal itself is the shared RT_TRACE fragment, so this pass and glass\n// run one traversal loop between them; only the bindings and the miss fallback\n// are the pass\'s own.\n//\n// One entry per compile, selected by a define, so each variant declares exactly\n// the resources it binds (Metal and DXIL indices are assigned in declaration\n// order, and an unused declaration would still hold its slot):\n//\n//   default     - flat: the per-object material tint as albedo, the fallback a\n//                 non-bindless world takes.\n//   RT_TEXTURED - samples the hit\'s albedo / normal / emissive maps from the\n//                 bindless texture pool, the path standard worlds take.\n//\n// DXIL_ABI pins every register to the root signature in\n// `directx/post/rt_reflections.rs` and keeps DirectX\'s raw vertex / index SRVs\n// (`ByteAddressBuffer`); the other targets carry one declaration with both a\n// `[[vk::binding]]` and a `register()`, whose number IS the Metal buffer index.\n// The DXIL container needs shader model 6.5 for ray query, above the 6.0 floor\n// the bindless main pass established.\n\n#ifndef POOL_SIZE\n#define POOL_SIZE 1024\n#endif\n#ifndef MAX_PROBES\n#define MAX_PROBES 8\n#endif\n\n{PROBE_TYPES}\n\n{RT_TYPES}\n\n// ---- Resource bindings ----\n\n#ifdef DXIL_ABI\n\nConstantBuffer<RtParams> rt_params : register(b0);\nRaytracingAccelerationStructure scene_tlas : register(t0);\n// Raw vertex / index SRVs: DirectX binds these as byte-address views.\nByteAddressBuffer verts : register(t1);\nByteAddressBuffer indices : register(t2);\nStructuredBuffer<RtGeomEntry> geom : register(t3);\nByteAddressBuffer sverts : register(t8);\nByteAddressBuffer sidx : register(t9);\n\nTexture2D<float4> scene_tex : register(t4);\nTexture2D<float4> gbuffer : register(t5);\nTexture2D<float4> rough_tex : register(t6);\nTextureCube<float4> prefilter : register(t7);\nSamplerState screen_sampler : register(s0);\nSamplerState cube_sampler : register(s1);\n\nfloat vert_float(uint i) { return asfloat(verts.Load(i * 4u)); }\nfloat svert_float(uint i) { return asfloat(sverts.Load(i * 4u)); }\nuint index_at(uint o) { return indices.Load(o * 4u); }\nuint skinned_index_word(uint w) { return sidx.Load(w * 4u); }\n\nfloat4 screen_sample(Texture2D<float4> t, float2 uv) { return t.Sample(screen_sampler, uv); }\nfloat3 prefilter_level(float3 dir, float lod)\n{\n    return prefilter.SampleLevel(cube_sampler, dir, lod).rgb;\n}\n\n#else\n\n[[vk::binding(0, 0)]] ConstantBuffer<RtParams> rt_params : register(b0);\n[[vk::binding(1, 0)]] RaytracingAccelerationStructure scene_tlas : register(t4);\n[[vk::binding(2, 0)]] StructuredBuffer<RtGeomEntry> geom : register(t3);\n// The shared static vertex stream (14 floats / 56 B stride) + its u32 indices.\n[[vk::binding(3, 0)]] StructuredBuffer<float> verts : register(t1);\n[[vk::binding(4, 0)]] StructuredBuffer<uint> indices : register(t2);\n// The deformed (posed) skinned vertex buffer, in the same 14-float layout the\n// skin kernel writes, + the skinned index buffer (two indices per uint).\n// Both bind a 1-element dummy in a scene with no skinned geometry, so the\n// binding stays valid even though the skinned branch is never taken there.\n[[vk::binding(9, 0)]] StructuredBuffer<float> sverts : register(t5);\n[[vk::binding(10, 0)]] StructuredBuffer<uint> sidx : register(t6);\n\n// Screen-space inputs reused from the SSR resolve. Declaration order is the\n// Metal texture index: scene(0), gbuffer(1), roughness(2), prefilter(3), and\n// the probe cube array below takes 4..3+MAX_PROBES.\n[[vk::binding(5, 0)]] Sampler2D<float4> scene_tex;\n[[vk::binding(6, 0)]] Sampler2D<float4> gbuffer;\n[[vk::binding(7, 0)]] Sampler2D<float4> rough_tex;\n[[vk::binding(8, 0)]] SamplerCube<float4> prefilter;\n\nfloat vert_float(uint i) { return verts[i]; }\nfloat svert_float(uint i) { return sverts[i]; }\nuint index_at(uint o) { return indices[o]; }\nuint skinned_index_word(uint w) { return sidx[w]; }\n\nfloat4 screen_sample(Sampler2D<float4> t, float2 uv) { return t.Sample(uv); }\nfloat3 prefilter_level(float3 dir, float lod) { return prefilter.SampleLevel(dir, lod).rgb; }\n\n#endif\n\n// The forward global set, bound here only for its reflection-probe count +\n// per-probe parallax boxes + cube array: a ray that escapes the scene falls\n// back to the local probe capture instead of the foreign sky cube.\n#ifdef DXIL_ABI\nConstantBuffer<ProbeSet> probe_set : register(b4);\n// D3D12 binds a shader sampler array only through a descriptor table, so the\n// probe cubes ride split from the one sampler the root signature hands out\n// statically -- the shape ssr.slang\'s SPLIT_PROBE_SAMPLER and the bindless main\n// pass both use for this array.\nTextureCube<float4> probe_cubes[MAX_PROBES] : register(t10);\nSamplerState probe_cube_sampler : register(s3);\n\nfloat3 probe_cube_sample_bias(uint i, float3 dir, float lod)\n{\n    return probe_cubes[i].SampleBias(probe_cube_sampler, dir, lod).rgb;\n}\n#else\n[[vk::binding(7, 1)]] ConstantBuffer<ProbeSet> probe_set : register(b8);\n[[vk::binding(8, 1)]] SamplerCube<float4> probe_cubes[MAX_PROBES];\n\nfloat3 probe_cube_sample_bias(uint i, float3 dir, float lod)\n{\n    return probe_cubes[i].SampleBias(dir, lod).rgb;\n}\n#endif\n#define PROBE_SET probe_set\n\n#ifdef RT_TEXTURED\n// The bindless albedo / normal / emissive pool, in whichever form its host can\n// bind: a Metal argument buffer of texture handles (its sampler is bound\n// alongside rather than written into the buffer), a Vulkan combined-sampler\n// array on the same set the main bindless pass uses, or an unbounded DXIL array\n// in space 1. `nonuniform_index` is required on the descriptor-indexing targets\n// and rejected by the Metal backend, where argument-buffer indexing needs no\n// annotation.\nuint nonuniform_index(uint i)\n{\n    __target_switch\n    {\n    case metal:\n        return i;\n    default:\n        return NonUniformResourceIndex(i);\n    }\n}\n\n#if defined(METAL_ABI)\nstruct TexturePool\n{\n    Texture2D<float4> tex_pool[POOL_SIZE];\n};\nParameterBlock<TexturePool> pool;\nSamplerState pool_sampler;\n\nfloat3 pool_sample_level0(uint idx, float2 uv)\n{\n    return pool.tex_pool[nonuniform_index(idx)].SampleLevel(pool_sampler, uv, 0.0).rgb;\n}\n#elif defined(DXIL_ABI)\nTexture2D<float4> tex_pool[] : register(t0, space1);\nSamplerState pool_sampler : register(s2);\n\nfloat3 pool_sample_level0(uint idx, float2 uv)\n{\n    return tex_pool[nonuniform_index(idx)].SampleLevel(pool_sampler, uv, 0.0).rgb;\n}\n#else\n[[vk::binding(1, 2)]] Sampler2D<float4> tex_pool[POOL_SIZE];\n\nfloat3 pool_sample_level0(uint idx, float2 uv)\n{\n    return tex_pool[nonuniform_index(idx)].SampleLevel(uv, 0.0).rgb;\n}\n#endif\n#endif\n\n// Surfaces rougher than REFLECTION_ROUGHNESS_CUT get no reflection; glossiness\n// ramps in below it. Locked to concinnity_core::gfx::ssr::REFLECTION_ROUGHNESS_CUT\n// by unit test so the SSR, RT, and composite gates agree.\nstatic const float REFLECTION_ROUGHNESS_CUT = 0.6;\n\n{PROBE_COMMON}\n\n{RT_TRACE}\n\n// Rebuild a view-space position from a UV and its linear (view-space) depth.\n// Matches ssr_view_pos in the SSR resolve.\nfloat3 rt_view_pos(float2 uv, float depth, float tan_y, float aspect)\n{\n    float2 ndc = float2(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);\n    return float3(ndc.x * tan_y * aspect, ndc.y * tan_y, -1.0) * depth;\n}\n\n[shader(\"fragment\")]\nfloat4 rt_reflections_fragment([[vk::location(0)]] float2 uv : TEXCOORD0) : SV_Target\n{\n    float3 base = screen_sample(scene_tex, uv).rgb;\n    float4 g = screen_sample(gbuffer, uv);\n    float depth = g.a;\n    // Background / sky, or a non-reflecting (too-rough) surface: weight 0 so the\n    // reflection composite keeps the scene there. The pass writes reflected\n    // radiance (.rgb) + composite weight (.a), not a blended colour.\n    if (depth <= 0.0)\n    {\n        return float4(base, 0.0);\n    }\n\n    float roughness = screen_sample(rough_tex, uv).r;\n    float gloss = saturate((REFLECTION_ROUGHNESS_CUT - roughness) / REFLECTION_ROUGHNESS_CUT);\n    if (gloss <= 0.0)\n    {\n        return float4(base, 0.0);\n    }\n\n    float3 nv = normalize(g.xyz);\n    float3 pv = rt_view_pos(uv, depth, rt_params.tan_half_fov_y, rt_params.aspect);\n    float3 pw = mul(rt_params.inv_view, float4(pv, 1.0)).xyz;\n    float3 nw = normalize(mul((float3x3)rt_params.inv_view, nv));\n    float3 v = normalize(rt_params.cam_pos.xyz - pw);\n\n    float3 dir = reflect(-v, nw);\n    bool ibl = rt_params.prefilter_mip_count > 0.5;\n    float max_mip = rt_params.prefilter_mip_count - 1.0;\n    float ndv = saturate(dot(nw, v));\n    float fresnel = RT_F0 + (1.0 - RT_F0) * pow(1.0 - ndv, 5.0);\n    float weight = saturate(fresnel * gloss * rt_params.intensity);\n\n    float3 reflected;\n    // Origin nudged off the surface along the normal so the trace cannot\n    // self-intersect the pixel\'s own triangle.\n    if (!rt_trace_reflection(pw + nw * 0.01, dir, ibl, max_mip, reflected))\n    {\n        // The ray escaped the scene: the local reflection probe (box-parallax,\n        // blended across covering probes) when one is baked, else the IBL\n        // prefilter sky, else the base shading.\n        float lod = roughness * max_mip;\n        reflected = probe_set.count > 0u ? probe_set_specular(pw, dir, lod)\n                  : (ibl ? prefilter_level(dir, lod) : base);\n    }\n\n    return float4(reflected, weight);\n}\n";
Expand description

rt_reflections.slang.