#version 330 core /* * Advanced Texture Control Shader * ------------------------------- * This shader emulates Adobe Lightroom/Photoshop's Texture slider by: * * 1. Using frequency separation techniques to isolate medium-frequency details * 2. Employing multi-scale edge detection for more natural enhancement * 3. Adding tone-aware processing to protect highlights and shadows * 4. Implementing perceptual weighting to preserve color relationships * 5. Using non-linear response curves similar to Lightroom's implementation * * Negative values: Smooth medium-frequency texture details (skin smoothing) * Positive values: Enhance medium-frequency texture details (fabric, hair, etc.) * * Unlike Clarity (which affects larger contrast structures) or Sharpening (which * affects fine details), Texture specifically targets medium-frequency details * that represent surface textures while avoiding edges. */ float luminance(vec3 color) { return dot(color, vec3(0.2126, 0.7152, 0.0722)); } // Improved blur function with Gaussian weights for better quality vec3 gaussianBlur(sampler2D tex, vec2 uv, vec2 texelSize, float radius) { vec3 blurred = vec3(0.0); float weightSum = 0.0; float sigma = radius / 2.0; float sigma2 = 2.0 * sigma * sigma; int kernelSize = int(ceil(radius * 2.0)); for (int x = -kernelSize; x <= kernelSize; ++x) { for (int y = -kernelSize; y <= kernelSize; ++y) { float dist2 = float(x*x + y*y); float weight = exp(-dist2 / sigma2); vec3 sample = texture(tex, uv + vec2(x, y) * texelSize).rgb; blurred += sample * weight; weightSum += weight; } } return blurred / max(weightSum, 0.0001); } // Edge-aware weight function to prevent halos float edgeWeight(float lumaDiff, float threshold) { return 1.0 - smoothstep(0.0, threshold, abs(lumaDiff)); } // Sigmoid function for smoother transitions float sigmoid(float x, float strength) { return x * (1.0 + strength) / (1.0 + strength * abs(x)); } out vec4 FragColor; in vec2 TexCoord; uniform sampler2D InputTexture; uniform float textureValue; // -100 (smooth) to 100 (enhance) vec3 applyTexture(vec3 originalColor, vec2 uv, float textureAdj) { if (abs(textureAdj) < 0.01) return originalColor; vec2 texelSize = 1.0 / textureSize(InputTexture, 0); float origLuma = luminance(originalColor); // Multi-scale approach for medium frequency targeting // For texture, we want medium frequencies (not too small, not too large) float smallRadius = 2.0; // For high frequency details float mediumRadius = 4.0; // For medium frequency details (texture) float largeRadius = 8.0; // For low frequency details vec3 smallBlur = gaussianBlur(InputTexture, uv, texelSize, smallRadius); vec3 mediumBlur = gaussianBlur(InputTexture, uv, texelSize, mediumRadius); vec3 largeBlur = gaussianBlur(InputTexture, uv, texelSize, largeRadius); // Extract medium frequencies (texture details) vec3 highFreq = smallBlur - mediumBlur; vec3 mediumFreq = mediumBlur - largeBlur; // Calculate local contrast for edge-aware processing float smallLuma = luminance(smallBlur); float mediumLuma = luminance(mediumBlur); float largeLuma = luminance(largeBlur); // Edge detection weights float edgeMask = edgeWeight(smallLuma - mediumLuma, 0.1); // Highlight & shadow protection float highlightProtect = 1.0 - smoothstep(0.75, 0.95, origLuma); float shadowProtect = smoothstep(0.05, 0.25, origLuma); float tonalWeight = highlightProtect * shadowProtect; // Map texture value to a perceptually balanced strength // Use non-linear mapping to match Lightroom's response curve float strength = sign(textureAdj) * pow(abs(textureAdj / 100.0), 0.8); // Apply different processing for positive vs negative values vec3 result = originalColor; if (strength > 0.0) { // Enhance texture (positive values) float enhanceFactor = strength * 1.5 * tonalWeight * edgeMask; result = originalColor + mediumFreq * enhanceFactor; } else { // Smooth texture (negative values) float smoothFactor = -strength * tonalWeight; result = mix(originalColor, mediumBlur, smoothFactor); } // Apply sigmoid function for more natural transitions result = mix(originalColor, result, sigmoid(abs(strength), 0.5)); return result; } void main() { vec4 color = texture(InputTexture, TexCoord); color.rgb = applyTexture(color.rgb, TexCoord, textureValue); FragColor = vec4(max(color.rgb, vec3(0.0)), color.a); }