#version 450 layout(location = 0) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; layout(set = 0, binding = 0) uniform sampler2D sceneSampler; layout(set = 0, binding = 1) uniform sampler2D reflectionSampler; layout(set = 0, binding = 2) uniform sampler2D pbrSampler; layout(set = 0, binding = 3) uniform sampler2D normalSampler; vec4 reflectionAt(ivec2 pixel, ivec2 size) { return texelFetch(reflectionSampler, clamp(pixel, ivec2(0), size - 1), 0); } void main() { vec4 sceneColor = texture(sceneSampler, fragTexCoord); vec4 pbr = texture(pbrSampler, fragTexCoord); vec4 normal = texture(normalSampler, fragTexCoord); float materialReflection = (pbr.a + normal.a - 1.05) * (1.0 - smoothstep(0.05, 0.7, pbr.g)); if (dot(normal.xyz, normal.xyz) < 0.001 || materialReflection <= 0.01) { outColor = sceneColor; return; } // The shared sampler is nearest-filtered. Upsample only premultiplied reflections. ivec2 size = textureSize(reflectionSampler, 0); vec2 pixel = fragTexCoord * vec2(size) - 0.5; ivec2 base = ivec2(floor(pixel)); vec2 blend = fract(pixel); vec4 reflection = mix( mix(reflectionAt(base, size), reflectionAt(base + ivec2(1, 0), size), blend.x), mix(reflectionAt(base + ivec2(0, 1), size), reflectionAt(base + ivec2(1, 1), size), blend.x), blend.y); outColor = vec4(sceneColor.rgb * (1.0 - reflection.a) + reflection.rgb, sceneColor.a); }