116 lines
3.6 KiB
GLSL
116 lines
3.6 KiB
GLSL
#version 460
|
|
|
|
#extension GL_GOOGLE_include_directive : require
|
|
#extension GL_EXT_nonuniform_qualifier : enable
|
|
|
|
#include "bindless.glsl"
|
|
|
|
// The selection image is R32_UINT, read through the same bindless array as an unsigned texture
|
|
layout (set = 0, binding = 0) uniform utexture2D uintTextures[];
|
|
|
|
layout (location = 0) in vec2 inUV;
|
|
layout (location = 0) out vec4 outColor;
|
|
|
|
// Must match PostProcessPipeline::PushConstants and the Tonemapper enum
|
|
#define TONEMAPPER_NONE 0
|
|
#define TONEMAPPER_ACES 1
|
|
#define TONEMAPPER_PBR_NEUTRAL 2
|
|
#define NO_TEXTURE 0xFFFFFFFFu
|
|
#define SELECTED_FLAG 0x80000000u
|
|
|
|
layout (push_constant) uniform PostPushConstants {
|
|
uint sceneColorTexture;
|
|
uint selectionTexture;
|
|
uint tonemapper;
|
|
float exposure;
|
|
ivec2 hoverPixel;
|
|
} pcs;
|
|
|
|
// Narkowicz 2015, a fit of the ACES filmic curve
|
|
vec3 tonemapAces(vec3 color)
|
|
{
|
|
const float a = 2.51;
|
|
const float b = 0.03;
|
|
const float c = 2.43;
|
|
const float d = 0.59;
|
|
const float e = 0.14;
|
|
return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0);
|
|
}
|
|
|
|
// Khronos PBR Neutral, keeps base colors as they are up to about 0.8 and only rolls off the highlights
|
|
vec3 tonemapPbrNeutral(vec3 color)
|
|
{
|
|
const float startCompression = 0.8 - 0.04;
|
|
const float desaturation = 0.15;
|
|
|
|
float x = min(color.r, min(color.g, color.b));
|
|
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
|
|
color -= offset;
|
|
|
|
float peak = max(color.r, max(color.g, color.b));
|
|
if (peak < startCompression) {
|
|
return color;
|
|
}
|
|
|
|
const float d = 1.0 - startCompression;
|
|
float newPeak = 1.0 - d * d / (peak + d - startCompression);
|
|
color *= newPeak / peak;
|
|
|
|
float g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);
|
|
return mix(color, vec3(newPeak), g);
|
|
}
|
|
|
|
uint objectAt(ivec2 pixel, ivec2 size)
|
|
{
|
|
pixel = clamp(pixel, ivec2(0), size - 1);
|
|
return texelFetch(nonuniformEXT(usampler2D(uintTextures[pcs.selectionTexture], samplers[NEAREST_SAMPLER_ID])), pixel, 0).r;
|
|
}
|
|
|
|
vec3 applyOutlines(vec3 color, ivec2 pixel)
|
|
{
|
|
if (pcs.selectionTexture == NO_TEXTURE) {
|
|
return color;
|
|
}
|
|
|
|
ivec2 size = textureSize(nonuniformEXT(usampler2D(uintTextures[pcs.selectionTexture], samplers[NEAREST_SAMPLER_ID])), 0);
|
|
uint center = objectAt(pixel, size);
|
|
uint hovered = pcs.hoverPixel.x >= 0 ? (objectAt(pcs.hoverPixel, size) & ~SELECTED_FLAG) : 0u;
|
|
|
|
bool selectedEdge = false;
|
|
bool hoveredEdge = false;
|
|
for (int y = -2; y <= 2; ++y) {
|
|
for (int x = -2; x <= 2; ++x) {
|
|
if (abs(x) + abs(y) > 3) {
|
|
continue;
|
|
}
|
|
uint neighbor = objectAt(pixel + ivec2(x, y), size);
|
|
selectedEdge = selectedEdge || ((neighbor & SELECTED_FLAG) != 0u && (center & SELECTED_FLAG) == 0u);
|
|
hoveredEdge = hoveredEdge || (hovered != 0u && (neighbor & ~SELECTED_FLAG) == hovered &&
|
|
(center & ~SELECTED_FLAG) != hovered);
|
|
}
|
|
}
|
|
|
|
if (selectedEdge) {
|
|
return vec3(1.0, 0.55, 0.1);
|
|
}
|
|
if (hoveredEdge) {
|
|
return mix(color, vec3(1.0), 0.65);
|
|
}
|
|
return color;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
vec3 color = texelFetch(nonuniformEXT(sampler2D(textures[pcs.sceneColorTexture], samplers[NEAREST_SAMPLER_ID])), pixel, 0).rgb;
|
|
color *= pcs.exposure;
|
|
|
|
if (pcs.tonemapper == TONEMAPPER_ACES) {
|
|
color = tonemapAces(color);
|
|
} else if (pcs.tonemapper == TONEMAPPER_PBR_NEUTRAL) {
|
|
color = tonemapPbrNeutral(color);
|
|
}
|
|
|
|
outColor = vec4(applyOutlines(clamp(color, 0.0, 1.0), pixel), 1.0);
|
|
}
|