// #type vertex #version 460 core layout (location = 0) in vec2 aPos; layout (location = 1) in vec2 aUV; layout (location = 0) out vec2 oUV; void main() { oUV = aUV; gl_Position = vec4(aPos, 0.0, 1.0); } // #type fragment #version 460 core precision highp float; // ConsoleColor vec3 palette (normalized) const vec3 ConsoleColorVec3[16] = vec3[]( vec3(0.0, 0.0, 0.0), // Black vec3(0.0, 0.0, 0.5), // DarkBlue vec3(0.0, 0.5, 0.0), // DarkGreen vec3(0.0, 0.5, 0.5), // DarkCyan vec3(0.5, 0.0, 0.0), // DarkRed vec3(0.5, 0.0, 0.5), // DarkMagenta vec3(0.5, 0.5, 0.0), // DarkYellow vec3(0.5, 0.5, 0.5), // Gray vec3(0.25, 0.25, 0.25), // DarkGray vec3(0.0, 0.0, 1.0), // Blue vec3(0.0, 1.0, 0.0), // Green vec3(0.0, 1.0, 1.0), // Cyan vec3(1.0, 0.0, 0.0), // Red vec3(1.0, 0.0, 1.0), // Magenta vec3(1.0, 1.0, 0.0), // Yellow vec3(1.0, 1.0, 1.0) // White ); vec3 srgbToLinear(vec3 color) { return mix(color / 12.92, pow((color + 0.055) / 1.055, vec3(2.4)), step(0.04045, color)); } vec3 rgbToXyz(vec3 color) { const mat3 rgbToXyzMatrix = mat3( 0.4124564, 0.3575761, 0.1804375, 0.2126729, 0.7151522, 0.0721750, 0.0193339, 0.1191920, 0.9503041 ); return rgbToXyzMatrix * color; } vec3 xyzToLab(vec3 xyz) { const vec3 whitePoint = vec3(0.95047, 1.0, 1.08883); // D65 white point xyz = xyz / whitePoint; vec3 f = mix(pow(xyz, vec3(1.0 / 3.0)), 7.787 * xyz + 16.0 / 116.0, step(0.008856, xyz)); return vec3( (116.0 * f.y) - 16.0, 500.0 * (f.x - f.y), 200.0 * (f.y - f.z) ); } vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } float perceptualColorDistance(vec3 color1, vec3 color2) { vec3 lab1 = xyzToLab(rgbToXyz(srgbToLinear(color1))); vec3 lab2 = xyzToLab(rgbToXyz(srgbToLinear(color2))); vec3 delta = lab1 - lab2; return dot(delta, delta); } int findMostPerceptuallyAccurateColor(vec3 color) { int bestMatchIndex = 0; float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]); for (int i = 1; i < 16; i++) { float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]); if (currentDistance < minDistance) { minDistance = currentDistance; bestMatchIndex = i; } } return bestMatchIndex; } //int findMostPerceptuallyAccurateColor(vec3 color) { // int closestIndex = 0; // float minDistance = distance(color, ConsoleColorVec3[0]); // // for (int i = 1; i < 16; i++) { // float dist = distance(color, ConsoleColorVec3[i]); // if (dist < minDistance) { // minDistance = dist; // closestIndex = i; // } // } // // return closestIndex; //} // Enhanced luminosity calculation considering human perception float calculatePerceptualLuminance(vec3 color) { // BT.709 luminance coefficients with slight adjustment return pow( 0.2126 * pow(color.r, 2.2) + 0.7152 * pow(color.g, 2.2) + 0.0722 * pow(color.b, 2.2), 1.0 / 2.2 ); } // Dithering function to reduce color banding float interleavedGradientNoise(vec2 pixel) { return fract(52.982919 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y)); } uniform sampler2D uInputTexture; layout (location = 0) in vec2 iUV; layout (location = 0) out vec4 FragColor; void main() { vec3 pixelColor = texture(uInputTexture, iUV * 4).rgb; // Find most perceptually accurate console color int colorIndex = findMostPerceptuallyAccurateColor(pixelColor); // Calculate perceptual luminance with gamma correction float luminance = calculatePerceptualLuminance(pixelColor); // Output with high precision color mapping FragColor = vec4( luminance, // Red: Perceptual luminance float(colorIndex) / 15.0, // Green: Normalized color index 0.0, // Blue: Unused 1.0 // Alpha ); }