27

有没有办法使用 GLSL(片段着色器)有效地改变 2D OpenGL 纹理的色调?

有人有一些代码吗?

更新:这是来自 user1118321 建议的代码:

uniform sampler2DRect texture;
const mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);
const mat3 yiq2rgb = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.1070, 1.7046);
uniform float hue;

void main() {

vec3 yColor = rgb2yiq * texture2DRect(texture, gl_TexCoord[0].st).rgb; 

float originalHue = atan(yColor.b, yColor.g);
float finalHue = originalHue + hue;

float chroma = sqrt(yColor.b*yColor.b+yColor.g*yColor.g);

vec3 yFinalColor = vec3(yColor.r, chroma * cos(finalHue), chroma * sin(finalHue));
gl_FragColor    = vec4(yiq2rgb*yFinalColor, 1.0);
}

这是与参考比较的结果:

在此处输入图像描述

我试图在 atan 中用 Q 切换 I 但即使在 0° 左右结果也是错误的

你有什么提示吗?

如果需要比较,这是未修改的原始图像: 在此处输入图像描述

4

3 回答 3

30

虽然@awoodland 说的是正确的,但我相信这种方法可能会导致亮度变化的问题。

由于多种原因,HSV 和 HLS 颜色系统存在问题。我最近与一位色彩科学家讨论过这个问题,他的建议是转换为 YIQ 或 YCbCr 空间并相应地调整色度通道(I&Q 或 Cb&Cr)。(你可以在这里这里学习如何做到这一点。)

一旦进入其中一个空间,您可以通过执行hue = atan(cr/cb)(注意 cb == 0)从色度通道形成的角度获得色调。这为您提供了一个弧度值。只需通过添加色调旋转量来旋转它。完成此操作后,您可以使用 计算色度的大小chroma = sqrt(cr*cr+cb*cb)。要返回 RGB,请使用 , 计算新的 Cb 和 Cr(或 I & Q Cr = chroma * sin (hue)Cb = chroma * cos (hue)。然后按照上述网页中的说明转换回 RGB。

编辑:这是我测试过的解决方案,似乎给我的结果与您的参考相同。您可能可以将一些点积折叠成矩阵乘法:

uniform sampler2DRect inputTexture;
uniform float   hueAdjust;
void main ()
{
    const vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
    const vec4  kRGBToI     = vec4 (0.596, -0.275, -0.321, 0.0);
    const vec4  kRGBToQ     = vec4 (0.212, -0.523, 0.311, 0.0);

    const vec4  kYIQToR   = vec4 (1.0, 0.956, 0.621, 0.0);
    const vec4  kYIQToG   = vec4 (1.0, -0.272, -0.647, 0.0);
    const vec4  kYIQToB   = vec4 (1.0, -1.107, 1.704, 0.0);

    // Sample the input pixel
    vec4    color   = texture2DRect (inputTexture, gl_TexCoord [ 0 ].xy);

    // Convert to YIQ
    float   YPrime  = dot (color, kRGBToYPrime);
    float   I      = dot (color, kRGBToI);
    float   Q      = dot (color, kRGBToQ);

    // Calculate the hue and chroma
    float   hue     = atan (Q, I);
    float   chroma  = sqrt (I * I + Q * Q);

    // Make the user's adjustments
    hue += hueAdjust;

    // Convert back to YIQ
    Q = chroma * sin (hue);
    I = chroma * cos (hue);

    // Convert back to RGB
    vec4    yIQ   = vec4 (YPrime, I, Q, 0.0);
    color.r = dot (yIQ, kYIQToR);
    color.g = dot (yIQ, kYIQToG);
    color.b = dot (yIQ, kYIQToB);

    // Save the result
    gl_FragColor    = color;
}
于 2012-02-10T21:11:44.553 回答
4

Andrea3000,在比较网上的 YIQ 示例时,我看到了您的帖子,但我认为您的代码的“更新”版本存在问题....我确定您的“mat3”定义是翻转/翻转的列/行排序...(也许这就是您仍然遇到麻烦的原因)...

仅供参考:OpenGL 矩阵排序:“对于更多值,矩阵以列优先顺序填充。也就是说,第一个 X 值是第一列,第二个 X 值是下一列,依此类推。” 见:http ://www.opengl.org/wiki/GLSL_Types

mat2(
float, float,   //first column
float, float);  //second column
于 2012-07-06T16:27:22.507 回答
1

MSL(金属着色器语言)版本,用于更改纹理的色调。我会推荐MetalPetal豆荚。这个吊舱将使生活更轻松。

#include <metal_stdlib>
using namespace metal;

typedef struct {
    float4 position [[ position ]];
    float2 textureCoordinate;
} VertexOut;


fragment float4 hue_adjust_filter(
                            VertexOut vertexIn [[stage_in]],
                            texture2d<float, access::sample> inTexture [[texture(0)]],
                            sampler inSampler [[sampler(0)]],
                            constant float &hueAdjust [[ buffer(0) ]])
{
    const float4  kRGBToYPrime = float4 (0.299, 0.587, 0.114, 0.0);
    const float4  kRGBToI     =  float4 (0.596, -0.275, -0.321, 0.0);
    const float4  kRGBToQ     =  float4 (0.212, -0.523, 0.311, 0.0);
    
    const float4  kYIQToR   =    float4 (1.0, 0.956, 0.621, 0.0);
    const float4  kYIQToG   =    float4 (1.0, -0.272, -0.647, 0.0);
    const float4  kYIQToB   =    float4 (1.0, -1.107, 1.704, 0.0);
    
    // Sample the input pixel
    float2 uv = vertexIn.textureCoordinate;
    float4 color = inTexture.sample(inSampler, uv);
    
    // Convert to YIQ
    float   YPrime  = dot (color, kRGBToYPrime);
    float   I      = dot (color, kRGBToI);
    float   Q      = dot (color, kRGBToQ);
    
    // Calculate the hue and chroma
    float   hue     = atan2 (Q, I);
    float   chroma  = sqrt (I * I + Q * Q);
    
    // Make the user's adjustments
    hue += hueAdjust;
    
    // Convert back to YIQ
    Q = chroma * sin (hue);
    I = chroma * cos (hue);
    
    // Convert back to RGB
    float4    yIQ   = float4 (YPrime, I, Q, 0.0);
    color.r = dot (yIQ, kYIQToR);
    color.g = dot (yIQ, kYIQToG);
    color.b = dot (yIQ, kYIQToB);
    
    return color;
}
于 2021-07-27T09:56:16.310 回答