// BVLE Voxels - AO Apply + Sky + Tone Mapping Compute Shader (Phase 6.3 + 7) // Final post-process pass: sky gradient for empty pixels, AO, saturation, tone mapping. #include "voxelCommon.hlsli" Texture2D aoBlurred : register(t0); Texture2D depthTexture : register(t1); RWTexture2D colorOutput : register(u0); struct ApplyPush { uint width; uint height; uint debugMode; // 0=normal, 2=debug AO (show AO as grayscale) uint pad[9]; }; [[vk::push_constant]] ConstantBuffer push : register(b999); // Soft clamp tone mapping: linear up to shoulder, gentle roll-off to prevent harsh clipping float3 softClampToneMap(float3 color, float exposure) { color *= exposure; return 1.0 - exp(-color); } // Saturation adjustment in linear space float3 adjustSaturation(float3 color, float saturation) { float luma = dot(color, float3(0.2126, 0.7152, 0.0722)); return lerp(float3(luma, luma, luma), color, saturation); } // Procedural sky gradient (Wonderbox-inspired warm atmosphere) float3 computeSky(float2 uv) { // Reconstruct view direction from screen UV float2 ndc = float2(uv.x * 2.0 - 1.0, (1.0 - uv.y) * 2.0 - 1.0); float4 clipDir = float4(ndc, 0.5, 1.0); float4 worldDir4 = mul(inverseViewProjection, clipDir); float3 viewDir = normalize(worldDir4.xyz / worldDir4.w - cameraPosition.xyz); // Vertical gradient: viewDir.y = +1 (zenith) to -1 (nadir) float t = viewDir.y * 0.5 + 0.5; // 0=horizon/below, 1=zenith // Wonderbox palette: warm sandy horizon → soft blue-grey zenith float3 horizonColor = float3(0.85, 0.75, 0.58); // warm sand/beige float3 zenithColor = float3(0.55, 0.62, 0.72); // muted blue-grey float3 nadirColor = float3(0.70, 0.62, 0.48); // darker warm below horizon float3 sky; if (t > 0.5) { // Above horizon: sand → blue float h = (t - 0.5) * 2.0; // 0=horizon, 1=zenith h = pow(h, 0.7); // non-linear: more horizon color in the lower sky sky = lerp(horizonColor, zenithColor, h); } else { // Below horizon: sand → darker warm float h = (0.5 - t) * 2.0; // 0=horizon, 1=nadir sky = lerp(horizonColor, nadirColor, h); } // Sun glow near sun direction (compact disc + subtle haze) float3 L = normalize(-sunDirection.xyz); float sunDot = saturate(dot(viewDir, L)); float sunDisc = pow(sunDot, 256.0) * 0.6; // tight bright disc float sunGlow = pow(sunDot, 64.0) * 0.2; // narrow glow ring float sunHaze = pow(sunDot, 8.0) * 0.08; // subtle atmospheric haze sky += float3(1.0, 0.85, 0.5) * (sunDisc + sunGlow + sunHaze); return sky; } [RootSignature(VOXEL_ROOTSIG)] [numthreads(8, 8, 1)] void main(uint3 DTid : SV_DispatchThreadID) { if (DTid.x >= push.width || DTid.y >= push.height) return; float depth = depthTexture[DTid.xy]; float ao = aoBlurred[DTid.xy]; if (push.debugMode == 2) { colorOutput[DTid.xy] = float4(ao, ao, ao, 1); return; } // Sky pixels: depth == 0 in reverse-Z = far plane if (depth == 0.0) { float2 uv = (float2(DTid.xy) + 0.5) / float2(push.width, push.height); float3 sky = computeSky(uv); colorOutput[DTid.xy] = float4(sky, 1.0); return; } float4 color = colorOutput[DTid.xy]; // Apply AO color.rgb *= ao; // Saturation boost (toneMapParams.x) color.rgb = adjustSaturation(color.rgb, toneMapParams.x); // Tone mapping (toneMapParams.y = exposure) color.rgb = softClampToneMap(color.rgb, toneMapParams.y); colorOutput[DTid.xy] = color; }