#include "sh/common.hlsl" /* ========================== * * Root signature * ========================== */ #define ROOTSIG \ "RootConstants(num32BitConstants = 9, b0), " \ "DescriptorTable(SRV(t0, space = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE)), " \ "DescriptorTable(SRV(t0, space = 1, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE)), " \ "DescriptorTable(UAV(u0, space = 2, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE)), " \ \ \ "StaticSampler(s0, " \ "filter = FILTER_MIN_MAG_MIP_POINT, " \ "addressU = TEXTURE_ADDRESS_CLAMP, " \ "addressV = TEXTURE_ADDRESS_CLAMP, " \ "addressW = TEXTURE_ADDRESS_CLAMP, " \ "maxAnisotropy = 1)" ConstantBuffer g_constants : register(b0); Texture2D g_gbuff_textures[] : register(t0, space0); Texture2D g_emittance_flood_textures[] : register(t0, space1); RWTexture2D g_write_textures[]: register(u0, space2); SamplerState g_sampler : register(s0); struct cs_input { DECLS(uint3, SV_DispatchThreadID); }; #define SAMPLES 4 #define MARCHES 16 /* ========================== * * Lighting * ========================== */ #define AMBIENT float4(1, 1, 1, 1) INLINE float4 get_light_in_dir(uint2 ray_start, float2 ray_dir) { float4 result = AMBIENT; float2 at_float = ray_start; uint2 at_uint = ray_start; for (uint i = 0; i < MARCHES; ++i) { uint2 flood = g_emittance_flood_textures[g_constants.emittance_flood_tex_urid][at_uint]; float2 dist_vec = at_float - (float2)flood; float dist = length(dist_vec); if (dist <= 1) { result = g_gbuff_textures[g_constants.emittance_tex_urid][flood]; break; } else { at_float += ray_dir * dist; at_uint = round(at_float); if (at_uint.x < 0 || at_uint.x >= g_constants.tex_width || at_uint.y < 0 || at_uint.y >= g_constants.tex_height) { /* Ambient lighting (ray hit edge of screen) */ break; } } } return result; } float rand_float_from_float2(float2 pos) { return frac(sin(dot(pos.xy, float2(12.9898,78.233))) * 43758.5453123); } INLINE float4 get_light_at_pos(uint2 pos) { float4 result = 0; for (int i = 0; i < SAMPLES; ++i) { float angle = TAU * (((float)i + rand_float_from_float2(pos + (float)i + sin(g_constants.time))) / ((float)SAMPLES - 1)); float2 dir = float2(cos(angle), sin(angle)); float4 light_in_dir = get_light_in_dir(pos, dir); result += light_in_dir; } result /= SAMPLES; return result; } /* ========================== * * Tone map * ========================== */ /* ACES approximation by Krzysztof Narkowicz * https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ */ INLINE float3 tone_map(float3 v) { return saturate((v * (2.51f * v + 0.03f)) / (v * (2.43f * v + 0.59f) + 0.14f)); } /* ========================== * * Entry point * ========================== */ [numthreads(8, 8, 1)] SH_ENTRY(ROOTSIG) void cs(struct cs_input input) { uint2 id = input.SV_DispatchThreadID.xy; if (id.x >= g_constants.tex_width || id.y >= g_constants.tex_height) { return; /* Overflow */ } float4 albedo = g_gbuff_textures[g_constants.albedo_tex_urid][id]; float4 lighting = get_light_at_pos(id); float4 color = albedo * lighting; // float4 color = albedo + lighting; /* Tonemap */ /* TODO: Dynamic exposure based on average scene luminance */ color *= g_constants.exposure; color.rgb = tone_map(color.rgb); /* Gamma correct */ color = pow(abs(color), 1/g_constants.gamma); g_write_textures[g_constants.write_tex_urid][id] = color; }