// SPDX-License-Identifier: MIT // // EmulationStation Desktop Edition // core.glsl // // Core shader functionality. // // Vertex section of code: #if defined(VERTEX) uniform mat4 MVPMatrix; in vec2 positionVertex; in vec2 texCoordVertex; in vec4 colorVertex; out vec2 position; out vec2 texCoord; out vec4 color; void main(void) { gl_Position = MVPMatrix * vec4(positionVertex.xy, 0.0, 1.0); position = positionVertex; texCoord = texCoordVertex; color.abgr = colorVertex.rgba; } // Fragment section of code: #elif defined(FRAGMENT) #ifdef GL_ES precision mediump float; #endif in vec2 position; in vec2 texCoord; in vec4 color; uniform vec2 texSize; uniform vec4 clipRegion; uniform float brightness; uniform float saturation; uniform float opacity; uniform float dimming; uniform float cornerRadius; uniform float reflectionsFalloff; uniform uint shaderFlags; uniform sampler2D textureSampler0; uniform sampler2D textureSampler1; out vec4 FragColor; // shaderFlags: // 0x00000001 - Premultiplied alpha (BGRA) // 0x00000002 - Font texture // 0x00000004 - Post processing // 0x00000008 - Clipping // 0x00000010 - Screen rotated 90 or 270 degrees // 0x00000020 - Rounded corners // 0x00000040 - Rounded corners with no anti-aliasing void main() { // Discard any pixels outside the clipping region. if (0x0u != (shaderFlags & 0x8u)) { if (position.x < clipRegion.x) discard; else if (position.y < clipRegion.y) discard; else if (position.x > clipRegion.z) discard; else if (position.y > clipRegion.w) discard; } vec4 sampledColor = texture(textureSampler0, texCoord); // Rounded corners. if (0x0u != (shaderFlags & 0x20u) || 0x0u != (shaderFlags & 0x40u)) { float cornerRadiusClamped = cornerRadius; // Don't go beyond half the width and height. if (cornerRadiusClamped > texSize.x / 2.0) cornerRadiusClamped = texSize.x / 2.0; if (cornerRadiusClamped > texSize.y / 2.0) cornerRadiusClamped = texSize.y / 2.0; vec2 center = position - texSize / 2.0; vec2 q = abs(center) - (vec2(texSize.x / 2.0, texSize.y / 2.0) - cornerRadiusClamped); float pixelDistance = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - cornerRadiusClamped; if (pixelDistance > 0.0) { discard; } else { float pixelValue; if (0x0u != (shaderFlags & 0x20u)) pixelValue = 1.0 - smoothstep(-0.75, 0.5, pixelDistance); else pixelValue = 1.0; sampledColor.a *= pixelValue; sampledColor.rgb *= pixelValue; } } // Brightness. if (brightness != 0.0) { sampledColor.rgb /= sampledColor.a; sampledColor.rgb += 0.3 * brightness; sampledColor.rgb *= sampledColor.a; } // Saturation, except for font textures. if (saturation != 1.0 && 0x0u == (shaderFlags & 0x2u)) { vec3 grayscale; // Premultiplied textures are all in BGRA format. if (0x0u != (shaderFlags & 0x01u)) grayscale = vec3(dot(sampledColor.bgr, vec3(0.114, 0.587, 0.299))); else grayscale = vec3(dot(sampledColor.rgb, vec3(0.299, 0.587, 0.114))); vec3 blendedColor = mix(grayscale, sampledColor.rgb, saturation); sampledColor = vec4(blendedColor, sampledColor.a); } // For fonts the alpha information is stored in the red channel. if (0x0u != (shaderFlags & 0x2u)) sampledColor = vec4(1.0, 1.0, 1.0, sampledColor.r); // We need different color calculations depending on whether the texture contains // premultiplied alpha or straight alpha values. if (0x0u != (shaderFlags & 0x01u)) { sampledColor.rgb *= color.rgb; sampledColor *= color.a; } else { sampledColor *= color; } // Saturation for font textures. if (saturation != 1.0 && 0x0u != (shaderFlags & 0x2u)) { vec3 grayscale = vec3(dot(sampledColor.rgb, vec3(0.299, 0.587, 0.114))); vec3 blendedColor = mix(grayscale, sampledColor.rgb, saturation); sampledColor = vec4(blendedColor, sampledColor.a); } // When post-processing we drop the alpha channel to avoid strange issues with some // graphics drivers. if (0x0u != (shaderFlags & 0x4u)) sampledColor.a = 1.0; // Opacity. if (opacity != 1.0) { if (0x0u == (shaderFlags & 0x01u)) sampledColor.a *= opacity; else sampledColor *= opacity; } // Dimming. if (dimming != 1.0) { vec4 dimColor = vec4(dimming, dimming, dimming, 1.0); sampledColor *= dimColor; } // Reflections falloff. if (reflectionsFalloff > 0.0) sampledColor.argb *= mix(0.0, 1.0, reflectionsFalloff - position.y) / reflectionsFalloff; FragColor = sampledColor; } #endif