Skip to main content

REFLECTION

Constant REFLECTION 

Source
pub const REFLECTION: &str = "// Roughness-aware reflection composite, split into two passes so the wide\n// glossy blur runs at reduced resolution. One fragment per compile, selected by\n// a define so each variant declares exactly the resources it binds (Metal and\n// DXIL indices are assigned in declaration order, so an unused declaration\n// would shift the live ones):\n//\n//   REFLECTION_BLUR      - reduced resolution: weight-averages the SSR / RT\n//                          resolve target over a roughness-scaled cone into the\n//                          blur target. This is the expensive part (up to 17\n//                          taps), so running it at a fraction of the pixels is\n//                          the saving.\n//   REFLECTION_COMPOSITE - full resolution: lerps the FULL-RES sharp reflection\n//                          against the upsampled blur by roughness, then\n//                          composites over the scene. A near-mirror\n//                          (roughness ~0) reads the sharp full-res tap so it\n//                          stays razor-sharp; a rough surface reads the cheap\n//                          upsampled blur, which is low-frequency anyway.\n//\n// Pairs with `fullscreen_vertex` in fullscreen.slang.\n\n// Surfaces rougher than this get no reflection (the resolve already wrote\n// weight 0); below it the blur cone ramps from a sharp mirror at 0 to\n// REFL_BLUR_MAX. Locked to concinnity_core::gfx::ssr::REFLECTION_ROUGHNESS_CUT\n// by unit test, so the SSR, RT, and composite passes can never disagree on it.\nstatic const float REFLECTION_ROUGHNESS_CUT = 0.6;\n\n#if defined(REFLECTION_BLUR)\n\n// The resolve target (rgb = reflected radiance, a = Fresnel/gloss composite\n// weight) and the G-buffer roughness the cone radius keys off.\n[[vk::binding(0, 0)]] Sampler2D<float4> reflection;\n[[vk::binding(1, 0)]] Sampler2D<float4> rough_tex;\n\n// Largest blur-cone radius in UV at the cut. This is the composite blur cone, a\n// separate quantity from the resolve\'s own in-tap gather radius.\nstatic const float REFL_BLUR_MAX = 0.02;\n\n// Two 8-tap rings (45-degree steps) at half and full radius approximate the\n// widening glossy reflection cone.\nstatic const float2 REFL_RING[8] = {\n    float2( 1.0,         0.0       ), float2( 0.70710678,  0.70710678),\n    float2( 0.0,         1.0       ), float2(-0.70710678,  0.70710678),\n    float2(-1.0,         0.0       ), float2(-0.70710678, -0.70710678),\n    float2( 0.0,        -1.0       ), float2( 0.70710678, -0.70710678),\n};\n\n[shader(\"fragment\")]\nfloat4 reflection_blur_fragment([[vk::location(0)]] float2 uv : TEXCOORD0) : SV_Target\n{\n    float4 c = reflection.Sample(uv);\n    float roughness = rough_tex.Sample(uv).r;\n    float radius = saturate(roughness / REFLECTION_ROUGHNESS_CUT) * REFL_BLUR_MAX;\n\n    // Weight each tap by its own coverage so weightless (non-reflecting) taps\n    // cannot drag their colour in; a reflective edge then fades smoothly.\n    float3 sum_rw = c.rgb * c.a;\n    float sum_w = c.a;\n    float taps = 1.0;\n    if (radius > 1e-5)\n    {\n        for (int ring = 0; ring < 2; ring++)\n        {\n            float rr = radius * (ring == 0 ? 0.5 : 1.0);\n            for (int i = 0; i < 8; i++)\n            {\n                float4 t = reflection.Sample(uv + REFL_RING[i] * rr);\n                sum_rw += t.rgb * t.a;\n                sum_w  += t.a;\n                taps   += 1.0;\n            }\n        }\n    }\n    float3 blurred = sum_w > 1e-4 ? sum_rw / sum_w : c.rgb;\n    float weight = sum_w / taps;\n    return float4(blurred, weight);\n}\n\n#elif defined(REFLECTION_COMPOSITE)\n\n// reflection: the full-res resolve target (rgb = radiance, a = weight). scene:\n// the base HDR scene. gbuffer: normal+depth, with .a = linear depth. rough_tex:\n// the G-buffer roughness. blur_tex: the reduced-resolution blur from pass 1.\n[[vk::binding(0, 0)]] Sampler2D<float4> reflection;\n[[vk::binding(1, 0)]] Sampler2D<float4> scene;\n[[vk::binding(2, 0)]] Sampler2D<float4> gbuffer;\n[[vk::binding(3, 0)]] Sampler2D<float4> rough_tex;\n[[vk::binding(4, 0)]] Sampler2D<float4> blur_tex;\n\n[shader(\"fragment\")]\nfloat4 reflection_composite_fragment([[vk::location(0)]] float2 uv : TEXCOORD0) : SV_Target\n{\n    float3 base = scene.Sample(uv).rgb;\n    float depth = gbuffer.Sample(uv).a;\n    float4 c = reflection.Sample(uv);\n    // Background, or a pixel that does not reflect (weight 0): keep the scene.\n    // A tiny weight is still honoured so the composite is exact at the edges.\n    if (depth <= 0.0 || c.a <= 0.0)\n    {\n        return float4(lerp(base, c.rgb, c.a), 1.0);\n    }\n\n    // 0 at a mirror -> use the sharp full-res tap; 1 at the cut -> use the cheap\n    // upsampled blur. The blur is low-frequency, so the bilinear upsample is\n    // visually free; only the sharp branch needs full-res detail.\n    float t = saturate(rough_tex.Sample(uv).r / REFLECTION_ROUGHNESS_CUT);\n    float4 b = blur_tex.Sample(uv);\n\n    float3 reflected = lerp(c.rgb, b.rgb, t);\n    float weight = lerp(c.a, b.a, t);\n    return float4(lerp(base, reflected, weight), 1.0);\n}\n\n#else\n#error \"reflection.slang: define REFLECTION_BLUR or REFLECTION_COMPOSITE\"\n#endif\n";
Expand description

reflection.slang.