121 lines
4.6 KiB
GLSL
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);
|
|
} |