pub const GLASS: &str = "// Glass panel pass: single source for every backend.\n//\n// The simplest consumer of the engine\'s transparent pass -- a flat, fixed\n// rectangular pane, drawn in the `PassId::Transparent` slot after the SSR\n// resolve and before TAA. The quad is already in world space (see\n// geometry::glass_quad), so the vertex stage only projects it. The fragment\n// stage discards where nearer opaque geometry occludes the pane (a manual depth\n// test: the transparent pass binds no depth attachment), refracts the\n// pre-transparent scene snapshot and tints it, then mixes a reflection over the\n// refraction by a Schlick Fresnel term. The pipeline straight-alpha blends the\n// result.\n//\n// One fragment entry per compile, selected by a define; everything outside the\n// reflection itself is shared, so toggling RT only changes where the reflection\n// comes from:\n//\n// default - the sharp planar reflection when this pane has\n// one, else the box-projected reflection-probe\n// set, else the sky prefilter cube, else a white\n// rim.\n// GLASS_RT - a real per-pixel reflection ray against the\n// scene acceleration structure (the shared\n// RT_TRACE fragment), falling back to the same\n// probe / sky chain when the ray escapes.\n// GLASS_RT with RT_TEXTURED - the same trace, with reflected hits taking\n// their albedo / normal / emissive maps from the\n// bindless pool.\n//\n// USE_MSAA is a HOST difference rather than a target one: Vulkan reads the\n// multisampled main depth while Metal and DirectX read the resolved copy.\n//\n// DXIL_ABI pins every register to the root signatures in `directx/glass.rs`;\n// the other targets carry one declaration with both a `[[vk::binding]]` and a\n// `register()`, whose number IS the Metal buffer index. Those Metal indices are\n// shared with the hand-written water and glass-mesh shaders, which the same\n// transparent encoder feeds, so they are not free to move.\n\n#ifndef POOL_SIZE\n#define POOL_SIZE 1024\n#endif\n#ifndef MAX_PROBES\n#define MAX_PROBES 8\n#endif\n#ifndef USE_MSAA\n#define USE_MSAA 0\n#endif\n\n{PROBE_TYPES}\n\n#ifdef GLASS_RT\n{RT_TYPES}\n#endif\n\n// Per-frame view shared by every transparent draw. Layout matches\n// `TransparentView` / the TransparentViewBlock UBO (160 B).\nstruct TransparentView\n{\n float4x4 vp; // world -> clip (jittered when TAA is on)\n float4x4 inv_vp; // clip -> world\n float4 camera_pos; // xyz: world-space camera\n float2 viewport; // attachment dimensions in pixels\n float time; // seconds since startup\n // Mips in the sky prefilter cube; 0 = no EnvironmentMap bound, and the\n // reflection keeps the white rim where no probe covers.\n float prefilter_mip_count;\n};\n\n// Per-pane tunables. Layout matches `GlassParams` / the GlassParamsBlock UBO\n// (64 B); the vec3 fields are float4 so MSL\'s 16-byte constant-buffer float3\n// cannot desynchronise them from the CPU struct.\nstruct GlassParams\n{\n float4 centre; // world-space pane centre\n float4 normal; // unit pane normal (facing direction)\n float4 tint; // colour multiplied into the refracted scene\n float opacity;\n float refraction_strength;\n float fresnel_power;\n // 1.0 when this pane has a planar reflection slot: sample the sharp mirror\n // render projectively instead of the box-projected probe. Never set on the\n // RT path, which traces a sharper reflection than a planar render.\n float planar;\n};\n\n// ---- Resource bindings ----\n\n#ifdef DXIL_ABI\n\nConstantBuffer<TransparentView> view : register(b0);\nConstantBuffer<GlassParams> params : register(b1);\nTexture2D<float4> scene_color : register(t0);\n#if USE_MSAA\nTexture2DMS<float> scene_depth : register(t1);\n#else\nTexture2D<float> scene_depth : register(t1);\n#endif\nTextureCube<float4> prefilter_cube : register(t2);\nTexture2D<float4> planar_reflection : register(t3);\nSamplerState post_samp : register(s0);\nSamplerState cube_sampler : register(s2);\n\nfloat4 scene_sample(float2 uv) { return scene_color.Sample(post_samp, uv); }\nfloat4 planar_sample(float2 uv) { return planar_reflection.Sample(post_samp, uv); }\nfloat3 prefilter_level(float3 dir, float lod)\n{\n return prefilter_cube.SampleLevel(cube_sampler, dir, lod).rgb;\n}\n\n#else\n\n// Metal buffer(5) / buffer(6): the shared per-frame view and the per-pane\n// params, both written with setBytes by the transparent encoder.\n[[vk::binding(0, 0)]] ConstantBuffer<TransparentView> view : register(b5);\n[[vk::binding(0, 1)]] ConstantBuffer<GlassParams> params : register(b6);\n\n// Declaration order is the Metal texture index, and these are the transparent\n// pass\'s shared slots: the scene snapshot at 0, the resolved depth at 1, the sky\n// prefilter cube at 2, the probe cubes at 3..2+MAX_PROBES, and this pane\'s\n// planar resolve at 11.\n[[vk::binding(1, 0)]] Sampler2D<float4> scene_color;\n#if USE_MSAA\n[[vk::binding(2, 0)]] Texture2DMS<float> scene_depth;\n#else\n[[vk::binding(2, 0)]] Texture2D<float> scene_depth;\n#endif\n[[vk::binding(5, 2)]] SamplerCube<float4> prefilter_cube;\n\nfloat4 scene_sample(float2 uv) { return scene_color.Sample(uv); }\nfloat3 prefilter_level(float3 dir, float lod) { return prefilter_cube.SampleLevel(dir, lod).rgb; }\n\n#endif\n\n// The reflection-probe set + cube array, from the forward global set: glass\n// reflects the same local scene capture the forward IBL specular and the SSR /\n// RT miss fallback use, rather than only the foreign sky cube.\n#ifdef DXIL_ABI\nConstantBuffer<ProbeSet> probe_set : register(b4);\n// The array spans MAX_PROBES registers from its base, so the RT variant moves it\n// clear of the ray-tracing SRVs at t4..t10 rather than starting at t7.\n#ifdef GLASS_RT\nTextureCube<float4> probe_cubes[MAX_PROBES] : register(t20);\n#else\nTextureCube<float4> probe_cubes[MAX_PROBES] : register(t7);\n#endif\n\nfloat3 probe_cube_sample_bias(uint i, float3 dir, float lod)\n{\n return probe_cubes[i].SampleBias(cube_sampler, dir, lod).rgb;\n}\n#else\n[[vk::binding(7, 2)]] ConstantBuffer<ProbeSet> probe_set : register(b7);\n[[vk::binding(8, 2)]] 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#ifndef DXIL_ABI\n// This pane\'s planar reflection target, bound per pane and sampled projectively\n// when `planar > 0.5`. Declared after the probe array so it lands on Metal\'s\n// texture(11); a pane with no planar slot binds a valid stand-in and never\n// samples it. Unused on the RT path, but the transparent encoder binds the slot\n// for every draw, so the declaration stays.\n[[vk::binding(1, 1)]] Sampler2D<float4> planar_reflection;\n\nfloat4 planar_sample(float2 uv) { return planar_reflection.Sample(uv); }\n#endif\n\n#ifdef GLASS_RT\n// The ray-tracing scene resources. On Metal they ride the transparent pass\'s\n// otherwise-free fragment buffers (0..4 and 8..10, since 5/6/7 are the view,\n// the params and the probe set); on Vulkan they are a set of their own, past\n// the view / params / global sets glass already owns; on DirectX they follow\n// the registers glass already occupies.\n#ifdef DXIL_ABI\n\nConstantBuffer<RtParams> rt_params : register(b5);\nRaytracingAccelerationStructure scene_tlas : register(t4);\nByteAddressBuffer verts : register(t5);\nByteAddressBuffer indices : register(t6);\nByteAddressBuffer sverts : register(t8);\nByteAddressBuffer sidx : register(t9);\nStructuredBuffer<RtGeomEntry> geom : register(t10);\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\n#else\n\n[[vk::binding(0, 3)]] ConstantBuffer<RtParams> rt_params : register(b0);\n[[vk::binding(1, 3)]] RaytracingAccelerationStructure scene_tlas : register(t4);\n[[vk::binding(2, 3)]] StructuredBuffer<RtGeomEntry> geom : register(t3);\n[[vk::binding(3, 3)]] StructuredBuffer<float> verts : register(t1);\n[[vk::binding(4, 3)]] StructuredBuffer<uint> indices : register(t2);\n[[vk::binding(5, 3)]] StructuredBuffer<float> sverts : register(t8);\n[[vk::binding(6, 3)]] StructuredBuffer<uint> sidx : register(t9);\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\n#endif\n\n#ifdef RT_TEXTURED\n// The bindless pool. Metal keeps it at buffer(10): buffer(7), where the main\n// pass puts it, is the probe set in the transparent pass.\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(s1);\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, 4)]] 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#endif\n\n{PROBE_COMMON}\n\n#ifdef GLASS_RT\n{RT_TRACE}\n#endif\n\n// ---- Stage interface ----\n\nstruct GlassVertexIn\n{\n [[vk::location(0)]] float3 pos : POSITION;\n};\n\nstruct GlassVertexOut\n{\n [[vk::location(0)]] float3 world_pos : TEXCOORD0;\n float4 position : SV_Position;\n};\n\n[shader(\"vertex\")]\nGlassVertexOut glass_vertex(GlassVertexIn v)\n{\n GlassVertexOut o;\n // Quad vertices are pre-transformed into world space at build time.\n o.world_pos = v.pos;\n o.position = mul(view.vp, float4(v.pos, 1.0));\n return o;\n}\n\n// Depth stored at this pixel by the main pass, for the manual occlusion test.\nfloat glass_scene_depth(int2 pixel)\n{\n#if USE_MSAA\n return scene_depth.Load(pixel, 0);\n#else\n return scene_depth.Load(int3(pixel, 0));\n#endif\n}\n\n// The pane surface at this fragment: the view-facing normal (two-sided, so a\n// pane lit from behind still Fresnels correctly), the fragment\'s screen UV and\n// the refracted, tinted background behind it.\nstruct GlassSurface\n{\n float3 normal;\n float2 frag_uv;\n float3 refracted;\n};\n\nGlassSurface glass_surface(float4 position, float3 view_dir)\n{\n GlassSurface s;\n s.normal = normalize(params.normal.xyz);\n if (dot(s.normal, view_dir) < 0.0)\n {\n s.normal = -s.normal;\n }\n\n float2 vp_dim = max(view.viewport, float2(1.0));\n s.frag_uv = position.xy / vp_dim;\n\n // Refraction: perturb the screen lookup by the pane normal\'s screen-plane\n // component so the background bends across the pane.\n float2 refract_uv = clamp(s.frag_uv + s.normal.xy * params.refraction_strength,\n float2(0.001), float2(0.999));\n s.refracted = scene_sample(refract_uv).rgb * params.tint.rgb;\n return s;\n}\n\n// Schlick Fresnel (F0 = 0.04 dielectric) mix of the reflection over the\n// refraction: ~4% head-on, rising to a full mirror at grazing. `fresnel_power`\n// stays the author\'s grazing-rim shaping control for the opacity ramp.\nfloat4 glass_resolve(GlassSurface s, float3 view_dir, float3 reflection)\n{\n float n_dot_v = saturate(dot(s.normal, view_dir));\n float rim = pow(1.0 - n_dot_v, max(params.fresnel_power, 1e-3));\n float refl_weight = saturate(0.04 + 0.96 * rim);\n float3 colour = lerp(s.refracted, reflection, refl_weight);\n float alpha = saturate(lerp(params.opacity, 1.0, rim));\n return float4(colour, alpha);\n}\n\n// The reflection a ray that hit nothing (or a pane with no trace at all) falls\n// back to: the box-projected probe set where a probe actually covers this pane,\n// else the sky prefilter cube, else a white rim so a probe-less, env-less world\n// still reads as glass. A pane is smooth, so every path is sharp (mip 0).\nfloat3 glass_environment(float3 world_pos, float3 r)\n{\n if (probe_set.count > 0u && probe_set_covers(world_pos))\n {\n return probe_set_specular(world_pos, r, 0.0);\n }\n return view.prefilter_mip_count > 0.5 ? prefilter_level(r, 0.0) : float3(1.0);\n}\n\n#ifdef GLASS_RT\n\n[shader(\"fragment\")]\nfloat4 glass_rt_fragment(GlassVertexOut i) : SV_Target\n{\n float3 view_dir = normalize(view.camera_pos.xyz - i.world_pos);\n GlassSurface s = glass_surface(i.position, view_dir);\n\n int2 pixel = min(int2(i.position.xy), int2(max(view.viewport, float2(1.0))) - int2(1, 1));\n if (glass_scene_depth(pixel) < i.position.z)\n {\n discard;\n }\n\n // A per-pixel reflection ray off the world-space pane surface point, so a\n // window mirrors real off-screen geometry. A pane is smooth, so the trace is\n // sharp and the miss falls back to the probe / sky chain.\n float3 r = reflect(-view_dir, s.normal);\n float3 reflection;\n if (!rt_trace_reflection(i.world_pos + s.normal * 0.02, r,\n view.prefilter_mip_count > 0.5,\n view.prefilter_mip_count - 1.0, reflection))\n {\n reflection = glass_environment(i.world_pos, r);\n }\n return glass_resolve(s, view_dir, reflection);\n}\n\n#else\n\n[shader(\"fragment\")]\nfloat4 glass_fragment(GlassVertexOut i) : SV_Target\n{\n float3 view_dir = normalize(view.camera_pos.xyz - i.world_pos);\n GlassSurface s = glass_surface(i.position, view_dir);\n\n // Manual depth occlusion: discard where the stored scene depth at this pixel\n // is nearer than the pane. Every backend rasterises this depth under the\n // same viewport convention the main pass used, so the fragment position\n // lines up with the stored texel.\n int2 pixel = min(int2(i.position.xy), int2(max(view.viewport, float2(1.0))) - int2(1, 1));\n if (glass_scene_depth(pixel) < i.position.z)\n {\n discard;\n }\n\n // A flat pane is a perfect mirror, so an active planar reflection (the scene\n // re-rendered mirrored across this pane\'s plane) lands exactly under the\n // reflector and is sampled at the fragment\'s own screen UV with no\n // distortion.\n float3 r = reflect(-view_dir, s.normal);\n float3 reflection = params.planar > 0.5 ? planar_sample(s.frag_uv).rgb\n : glass_environment(i.world_pos, r);\n return glass_resolve(s, view_dir, reflection);\n}\n\n#endif\n";Expand description
glass.slang.