有没有办法使用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);
}
这是与参考文献相比的结果:
我试图用at内部的Q切换,但结果是错误的,甚至在0°
附近你有任何暗示吗?
如果需要进行比较,这是原始未修改的图像:
答案 0 :(得分:26)
虽然@awoodland所说的是正确的,但我相信这种方法可能会引起亮度变化的问题。
由于多种原因,HSV和HLS颜色系统存在问题。我最近与一位色彩科学家谈过这个问题,他的建议是转换为YIQ或YCbCr空间并相应地调整色度通道(I& Q,或Cb& Cr)。 (您可以了解如何执行此操作here和here。)在其中一个空格中,您可以通过hue = atan(cr/cb)
(观察cb == 0)从色度通道形成的角度获得色调。这给你一个弧度值。只需通过添加色调旋转量来旋转它。完成后,您可以使用chroma = sqrt(cr*cr+cb*cb)
计算色度的大小。要返回RGB,请使用Cr = chroma * sin (hue)
,Cb = chroma * cos (hue)
计算新的Cb和Cr(或I& Q)。然后转换回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;
}
答案 1 :(得分:3)
Andrea3000,在比较网上的YIQ示例时,我遇到了你的帖子,但我认为代码的“更新”版本存在问题....我确信你的'mat3'定义是翻转的/在列/行排序上失败...(也许这就是你仍然遇到麻烦的原因)......
仅供参考:OpenGL矩阵排序:“对于更多值,矩阵按列主要顺序填充。即,第一个X值是第一列,第二个X值是下一列,依此类推。”请参阅:http://www.opengl.org/wiki/GLSL_Types
mat2(
float, float, //first column
float, float); //second column
答案 2 :(得分: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;
}