#version 430 core // Need SSBOs and atomic counters // Input Texture (the processed image ready for display) // Binding = 0 matches glBindImageTexture unit // Use rgba8 format as we assume display texture is 8-bit sRGB (adjust if needed) layout(binding = 0, rgba8) uniform readonly image2D InputTexture; // Output Histogram Buffer (SSBO) // Binding = 1 matches glBindBufferBase index // Contains 256 bins for R, 256 for G, 256 for B sequentially (total 768 uints) layout(std430, binding = 1) buffer HistogramBuffer { uint bins[]; // Use an unsized array } histogram; // Workgroup size (adjust based on GPU architecture for performance, 16x16 is often reasonable) layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; // Helper to convert linear float (potentially HDR 0.0+) to 0-255 bin index // Applies an approximate gamma curve for perceptual binning. uint linearToVisualBin(float linearVal) { // Clamp linear value to 0.0-1.0 range before gamma/binning // This histograms the displayable range after processing. float clampedVal = clamp(linearVal, 0.0, 1.0); // Apply approximate sRGB gamma for perceptual brightness mapping float displayApprox = pow(clampedVal, 1.0/2.2); // Convert 0.0-1.0 display value to 0-255 bin index return uint(displayApprox * 255.999); // Use 255.999 to ensure 1.0 maps to 255 } void main() { // Get the global invocation ID (like pixel coordinates) ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy); ivec2 textureSize = imageSize(InputTexture); // Boundary check: Don't process outside the image bounds if (pixelCoord.x >= textureSize.x || pixelCoord.y >= textureSize.y) { return; } // Load the pixel color (values are normalized float 0.0-1.0 from rgba8 image load) vec4 pixelColor = imageLoad(InputTexture, pixelCoord); // Calculate bin indices (0-255) // We clamp just in case, although imageLoad from rgba8 should be in range. uint rBin = linearToVisualBin(pixelColor.r); uint gBin = linearToVisualBin(pixelColor.g); uint bBin = linearToVisualBin(pixelColor.b); // Atomically increment the counters in the SSBO // Offset Green bins by 256, Blue bins by 512 atomicAdd(histogram.bins[rBin], 1u); atomicAdd(histogram.bins[gBin + 256u], 1u); atomicAdd(histogram.bins[bBin + 512u], 1u); // Optional: Track Max Value (more complex, requires another SSBO or different strategy) // Example: atomicMax(histogram.maxBinValue, histogram.bins[rBin]); // Needs careful sync }