tedit/shaders/texture.frag
2025-04-07 20:08:16 -04:00

121 lines
4.6 KiB
GLSL

#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);
}