Skip to main content

RT_TRACE

Constant RT_TRACE 

Source
pub const RT_TRACE: &str = "// The inline ray-query reflection trace, spliced into a shader at its RT_TRACE\n// marker (see slang_source.rs). The second half of the ray-tracing splice:\n// RT_TYPES declares the records ahead of a shader\'s bindings, this traverses\n// them. One traversal loop for every target -- `RayQuery` lowers to\n// `raytracing::intersection_query` on Metal, `SPV_KHR_ray_query` on Vulkan and\n// SM 6.5 DXIL on DirectX. Nothing here may spell either marker.\n//\n// Hooks the including shader must provide, because each differs per binding\n// model or per pass:\n//   rt_params, scene_tlas, geom     - the bound params / TLAS / geometry table\n//   vert_float / svert_float        - one float of the static / deformed stream\n//   index_at / skinned_index_word   - one u32 index of the static / skinned IB\n//   prefilter_level(dir, lod)       - the IBL prefilter cube at an explicit mip\n//   pool_sample_level0(idx, uv)     - the bindless pool at mip 0 (RT_TEXTURED)\n\n// Dielectric base reflectance for the Fresnel.\nstatic const float RT_F0 = 0.04;\n// Floats per `Vertex` (56-byte stride): position, normal, tangent, colour, uv.\nstatic const uint VERTEX_FLOATS = 14u;\n// Skinned objects flag bit 31 of `normal_index`; the trace then fetches the hit\n// triangle from the deformed-vertex / skinned index buffers, which mirror\n// the static layout. The flag is masked off before the pool sample, so skinned\n// hits shade textured like static ones. Matches render_types::RT_SKINNED_FLAG.\nstatic const uint RT_SKINNED_FLAG = 0x80000000u;\n\n// Attribute fetchers into the shared 14-float `Vertex` (normal at float 3,\n// tangent at 6, uv at 12), addressed by absolute vertex index `vi`. The\n// `skinned` set reads the deformed buffer, which carries the identical layout.\nfloat3 rt_vertex_normal(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float3(vert_float(b + 3u), vert_float(b + 4u), vert_float(b + 5u));\n}\nfloat3 rt_vertex_tangent(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float3(vert_float(b + 6u), vert_float(b + 7u), vert_float(b + 8u));\n}\nfloat2 rt_vertex_uv(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float2(vert_float(b + 12u), vert_float(b + 13u));\n}\nfloat3 rt_skinned_normal(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float3(svert_float(b + 3u), svert_float(b + 4u), svert_float(b + 5u));\n}\nfloat3 rt_skinned_tangent(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float3(svert_float(b + 6u), svert_float(b + 7u), svert_float(b + 8u));\n}\nfloat2 rt_skinned_uv(uint vi)\n{\n    uint b = vi * VERTEX_FLOATS;\n    return float2(svert_float(b + 12u), svert_float(b + 13u));\n}\n\n// One index from the skinned index buffer. The skinned BLAS bakes absolute\n// indices, so no base_vertex is added.\nuint rt_skinned_index(uint o)\n{\n    return skinned_index_word(o);\n}\n\n// Decode a tangent-space normal map texel. Only X and Y are read; Z is\n// reconstructed from them, so a two-channel source (BC5) decodes the same as an\n// RGBA8 one and normal maps can ship as BC5 blocks.\nfloat3 decode_normal_map(float2 encoded)\n{\n    float2 nxy = encoded * 2.0 - 1.0;\n    return float3(nxy, sqrt(saturate(1.0 - dot(nxy, nxy))));\n}\n\n// Sun visibility at a hit: 0 where a second ray toward the sun is occluded (a\n// cast shadow inside the reflection), 1 where the sun is visible.\n//\n// RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH is what an occlusion query wants and\n// is what the Vulkan and DirectX legs get; the Metal target drops it (slangc\n// does not map the flag to `intersection_params::accept_any_intersection`), so\n// there the query commits the closest hit instead of the first. Same answer,\n// more traversal.\nfloat rt_shadow(float3 hp, float3 n)\n{\n    RayDesc sr;\n    sr.Origin = hp + n * 0.02;\n    sr.Direction = normalize(rt_params.sun_dir.xyz);\n    sr.TMin = 0.001;\n    sr.TMax = rt_params.max_distance;\n\n    RayQuery<RAY_FLAG_FORCE_OPAQUE | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH> sq;\n    sq.TraceRayInline(scene_tlas, RAY_FLAG_NONE, 0xFFu, sr);\n    // Drained rather than stepped once: with the opacity forced there is no\n    // non-opaque candidate to resolve, and discarding the step\'s result trips an\n    // unused-value warning out of the Metal compiler on slangc\'s emitted call.\n    while (sq.Proceed()) {}\n    return (sq.CommittedStatus() == COMMITTED_TRIANGLE_HIT) ? 0.0 : 1.0;\n}\n\n// Metallic/roughness hit shading: a sun diffuse term (dielectric only, masked\n// by the shadow ray) plus split IBL -- diffuse irradiance along N, and a\n// specular tap along the onward reflection at a roughness-selected prefilter\n// mip, tinted by F0. Metals drop the diffuse term and tint the reflected\n// environment by their albedo. The material\'s self-emission is added on top, so\n// glowing surfaces light up in reflections. With no EnvironmentMap bound the\n// IBL pair degrades to a small constant ambient.\nfloat3 rt_shade_hit(float3 n, float3 albedo, float hit_rough, float metallic, float3 emissive,\n                    float3 dir, bool ibl, float max_mip, float shadow)\n{\n    float3 f0 = lerp(float3(RT_F0), albedo, metallic);\n    float3 diff_a = albedo * (1.0 - metallic);\n    float ndl = saturate(dot(n, rt_params.sun_dir.xyz));\n    float3 sun = diff_a * rt_params.sun_color.xyz * ndl * shadow;\n    if (!ibl)\n    {\n        return sun + (diff_a + f0) * 0.03 + emissive;\n    }\n    float3 spec = prefilter_level(reflect(dir, n), hit_rough * max_mip);\n    float3 diff = prefilter_level(n, max_mip) * diff_a;\n    return sun + diff + spec * f0 + emissive;\n}\n\n// Trace one reflection ray from `origin` along `dir` and shade what it hits.\n// Returns false when the ray escaped the scene, leaving `shaded` untouched: the\n// miss fallback is the caller\'s, because a screen-space resolve and a glass\n// pane want different ones.\nbool rt_trace_reflection(float3 origin, float3 dir, bool ibl, float max_mip, out float3 shaded)\n{\n    shaded = float3(0.0);\n\n    RayDesc ray;\n    ray.Origin = origin;\n    ray.Direction = dir;\n    ray.TMin = 0.01;\n    ray.TMax = rt_params.max_distance;\n\n    RayQuery<RAY_FLAG_FORCE_OPAQUE> rq;\n    rq.TraceRayInline(scene_tlas, RAY_FLAG_NONE, 0xFFu, ray);\n    while (rq.Proceed()) {}\n\n    if (rq.CommittedStatus() != COMMITTED_TRIANGLE_HIT)\n    {\n        return false;\n    }\n\n    // The instance\'s position in the TLAS instance array, which every host\n    // builds in geometry-table order. NOT CommittedInstanceID: that is the user\n    // instance id, which the Metal target reads from a descriptor field only\n    // MTLAccelerationStructureUserIDInstanceDescriptor carries, and the engine\n    // builds Default descriptors.\n    uint inst = rq.CommittedInstanceIndex();\n    uint tri = rq.CommittedPrimitiveIndex();\n    float2 b = rq.CommittedTriangleBarycentrics();\n    float hit_t = rq.CommittedRayT();\n\n    RtGeomEntry e = geom[inst];\n    bool skin = (e.normal_index & RT_SKINNED_FLAG) != 0u;\n    uint nidx = e.normal_index & ~RT_SKINNED_FLAG;\n    uint o = e.index_offset + tri * 3u;\n    float w0 = 1.0 - b.x - b.y;\n\n    float3 nl, tl;\n    float2 huv;\n    if (skin)\n    {\n        uint i0 = rt_skinned_index(o);\n        uint i1 = rt_skinned_index(o + 1u);\n        uint i2 = rt_skinned_index(o + 2u);\n        nl = rt_skinned_normal(i0) * w0 + rt_skinned_normal(i1) * b.x + rt_skinned_normal(i2) * b.y;\n        tl = rt_skinned_tangent(i0) * w0 + rt_skinned_tangent(i1) * b.x + rt_skinned_tangent(i2) * b.y;\n        huv = rt_skinned_uv(i0) * w0 + rt_skinned_uv(i1) * b.x + rt_skinned_uv(i2) * b.y;\n    }\n    else\n    {\n        uint i0 = index_at(o) + e.base_vertex;\n        uint i1 = index_at(o + 1u) + e.base_vertex;\n        uint i2 = index_at(o + 2u) + e.base_vertex;\n        nl = rt_vertex_normal(i0) * w0 + rt_vertex_normal(i1) * b.x + rt_vertex_normal(i2) * b.y;\n        tl = rt_vertex_tangent(i0) * w0 + rt_vertex_tangent(i1) * b.x + rt_vertex_tangent(i2) * b.y;\n        huv = rt_vertex_uv(i0) * w0 + rt_vertex_uv(i1) * b.x + rt_vertex_uv(i2) * b.y;\n    }\n\n    float3x3 m3 = (float3x3)e.model;\n    float3 hit_n = normalize(mul(m3, nl));\n    if (dot(hit_n, dir) > 0.0)\n    {\n        hit_n = -hit_n;\n    }\n    float3 hit_tan = mul(m3, tl);\n\n    float3 tint = float3(e.tint_r, e.tint_g, e.tint_b);\n    float3 emissive = float3(e.emissive_r, e.emissive_g, e.emissive_b);\n    float shadow = rt_shadow(origin + dir * hit_t, hit_n);\n\n#ifdef RT_TEXTURED\n    // Explicit level 0: a reflected ray\'s screen-space UV gradients are\n    // unstable (neighbouring pixels hit unrelated triangles), so sampling the\n    // base mip avoids gradient-driven mip thrash. Skinned hits take the same\n    // path -- their textures live in the same pool and the geometry entry\n    // carries the real indices.\n    float3 albedo = tint * pool_sample_level0(e.albedo_index, huv);\n    // Perturb the geometric normal by the tangent-space normal map. The\n    // flat-normal fallback decodes to (0, 0, 1) so N is unchanged when an object\n    // has no map; a degenerate tangent (procedural meshes may carry none) skips\n    // it and keeps the geometric normal.\n    float3 n = hit_n;\n    float tlen = length(hit_tan);\n    if (tlen > 1e-4)\n    {\n        float3 nm = decode_normal_map(pool_sample_level0(nidx, huv).xy);\n        float3 t = hit_tan / tlen;\n        t = normalize(t - n * dot(n, t));  // Gram-Schmidt\n        float3 bt = cross(n, t);\n        n = normalize(t * nm.x + bt * nm.y + n * nm.z);\n    }\n    // Colour the self-emission by the emissive map (the bistro string lights),\n    // mirroring the main bindless pass. Gated on a non-zero pool index; the flat\n    // variant has no pool and keeps the scalar emissive.\n    if (e.emissive_map_index != 0u)\n    {\n        emissive *= pool_sample_level0(e.emissive_map_index, huv);\n    }\n    shaded = rt_shade_hit(n, albedo, e.roughness, e.metallic, emissive, dir, ibl, max_mip, shadow);\n#else\n    shaded = rt_shade_hit(hit_n, tint, e.roughness, e.metallic, emissive, dir, ibl, max_mip, shadow);\n#endif\n    return true;\n}\n";
Expand description

rt_trace.slang.