PDA

View Full Version : Saturation and Hue in GLSL



Java Cool Dude
03-27-2005, 06:04 PM
How do I go about changing the hue or saturation of an RGB color by the mean of a GLSL shader without the RGB to HSV back to RGB cycle?
I already figured how to modify the contrast and brightness through this shader, and I need only the last two components to finish my demo.


<Shaders
linkProcessors = "true"
vertexProcessor = "VERTEX_SHADER"
fragmentProcessor = "FRAGMENT_SHADER">

<VERTEX_SHADER>
<RawData>
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}
</RawData>
</VERTEX_SHADER>

<FRAGMENT_SHADER>

<Uniform name = "videoParams" size = "2" type = "float" x = "0.5"
y = "1.0"/>

<Uniform name = "proccessMatRow0" type = "float" size = "4"/>
<Uniform name = "proccessMatRow1" type = "float" size = "4"/>
<Uniform name = "proccessMatRow2" type = "float" size = "4"/>

<Uniform name = "first" size = "1" type = "int" x = "0" />
<Uniform name = "second" size = "1" type = "int" x = "1" />

<RawData>
uniform sampler2D second;
uniform sampler2D first;

uniform vec2 videoParams;
uniform vec4 proccessMatRow0;
uniform vec4 proccessMatRow1;
uniform vec4 proccessMatRow2;

const vec4 halfVector = vec4(0.5, 0.5, 0.5, 0.0);

vec4 applyPostProcessingMatrix(vec4 color)
{
vec4 outColor;

outColor.x = dot(proccessMatRow0, color);
outColor.y = dot(proccessMatRow1, color);
outColor.z = dot(proccessMatRow2, color);

outColor -= halfVector;
outColor *= videoParams.y;
outColor += halfVector;
return outColor;
}

void main(void)
{
vec4 firstColor = texture2D(first , gl_TexCoord[0].xy)*(1.0 - videoParams.x),
secondColor = texture2D(second, gl_TexCoord[0].xy)*videoParams.x;

gl_FragColor = applyPostProcessingMatrix(firstColor + secondColor);
}
</RawData>
</FRAGMENT_SHADER>
</Shaders>The contrast value is loaded in the y component of videoParams and the brightness is in x;

Screenshot
http://www.realityflux.com/abba/C++/GLSLDVMixer/GLSLDVMixer.jpg

Binaries (http://www.realityflux.com/abba/C++/GLSLDVMixer/GLSLDVMixer.zip)
Demo source (http://www.realityflux.com/abba/C++/GLSLDVMixer/GLSLDVMixerSRC.zip)
Engine source (http://www.realityflux.com/abba/C++/SXMLEngine/SXMLEngine.zip)

Java Cool Dude
03-28-2005, 05:27 PM
Am I posting this on the wrong forum? :confused:

Humus
03-28-2005, 05:36 PM
Originally posted by Java Cool Dude:
How do I go about changing the hue or saturation of an RGB color by the mean of a GLSL shader without the RGB to HSV back to RGB cycle?Is this an absolute requirement, or are you just concerned that the pretty large chunk of math for that would slow you down? If the latter, then consider using two 3D textures as a lookup table. With linear filter a 32x32x32 is usually good enough. There's a sample in the Radeon SDK called PostProcessing that does this. The shader looks like this:


uniform sampler2D Image;
uniform sampler3D RGB2HSI;
uniform sampler3D HSI2RGB;

uniform float hueShift;
uniform float satBoost;

varying vec2 texCoord;

void main(){
// Sample the image
vec3 rgb = texture2D(Image, texCoord).rgb;
// Look up the corresponding HSI value
vec3 hsi = texture3D(RGB2HSI, rgb).xyz;

// Manipulate hue and saturation
hsi.x = fract(hsi.x + hueShift);
hsi.y *= satBoost;

// Look up the corresponding RGB value
gl_FragColor = texture3D(HSI2RGB, hsi);
}

Java Cool Dude
03-28-2005, 05:41 PM
Man you're awesome, how ya been? Haven't seen ya around in a while, how's your job at ATi's? I myself might be going to Nvidia this summer and I'm really looking forward to it :)
Peace.

Java Cool Dude
03-28-2005, 09:56 PM
The RGB to HSI back to RGB conversion through the 3D textures look ups appear to be quite hideous... to be honest :p

Java Cool Dude
03-29-2005, 07:15 AM
All settings including Saturation and Hue are now working. The thing is, I compute a single post processing matrix on the CPU that applies the brightness contrast and hue alteration before uploading it to the GPU via uniforms.


<Shaders
linkProcessors = "true"
vertexProcessor = "VERTEX_SHADER"
fragmentProcessor = "FRAGMENT_SHADER">

<VERTEX_SHADER>
<RawData>
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}
</RawData>
</VERTEX_SHADER>

<FRAGMENT_SHADER>

<Uniform name = "videoParams" size = "2" type = "float" x = "0.5"
y = "1.0"/>

<Uniform name = "proccessMatRow0" type = "float" size = "4"/>
<Uniform name = "proccessMatRow1" type = "float" size = "4"/>
<Uniform name = "proccessMatRow2" type = "float" size = "4"/>

<Uniform name = "first" size = "1" type = "int" x = "0" />
<Uniform name = "second" size = "1" type = "int" x = "1" />

<RawData>
uniform sampler2D second;
uniform sampler2D first;

uniform vec2 videoParams;
uniform vec4 proccessMatRow0;
uniform vec4 proccessMatRow1;
uniform vec4 proccessMatRow2;

const vec4 halfVector = vec4(0.5, 0.5, 0.5, 0.0);

vec4 applyPostProcessingMatrix(vec4 color)
{
vec4 outColor;

outColor.x = dot(proccessMatRow0, color);
outColor.y = dot(proccessMatRow1, color);
outColor.z = dot(proccessMatRow2, color);

outColor -= halfVector;
outColor *= videoParams.y;
outColor += halfVector;
return outColor;
}

void main(void)
{
vec4 firstColor = texture2D(first , gl_TexCoord[0].xy)*(1.0 - videoParams.x),
secondColor = texture2D(second, gl_TexCoord[0].xy)*videoParams.x;

gl_FragColor = applyPostProcessingMatrix(firstColor + secondColor);
}
</RawData>
</FRAGMENT_SHADER>
</Shaders>

Humus
03-29-2005, 03:48 PM
Originally posted by Java Cool Dude:
Man you're awesome, how ya been? Haven't seen ya around in a while, how's your job at ATi's? I myself might be going to Nvidia this summer and I'm really looking forward to it :)
Peace.Hi, I'm fine, and things are going fine at ATI too. :) I've been around here pretty much on a daily basis with a few exceptions for quite a long time. I haven't seen you around though in quite a while.

Humus
03-29-2005, 03:53 PM
Originally posted by Java Cool Dude:
The RGB to HSI back to RGB conversion through the 3D textures look ups appear to be quite hideous... to be honest :p But it works pretty darn well. With tables as small as 32x32x32 I've not been able to see any visual difference compared to using the full math. 16x16x16 gives good quality as well. 32x32x32 32bit is only 128kb, which is less than a regular 256x256 texture.

Java Cool Dude
03-29-2005, 06:34 PM
Hmmm, I noticed a lot of artifacts when I retrieved the HSV values and then used them immidiately to look up the source RGB.
Could be the way I load up the volume textures (my DDS loader is still in its infancy).
Besides, computing the hue processing matrix on the CPU and then uploading its first 3 rows to the fragment shader seems to give quite a performance boost. That and you don't have to recompute the matrix at every frame, the need to do so only arises when the Hue or Saturation values are modified.

Here's the code snippet

void SceneFrame::updatePostProcessMatrix()
{
float c = fastCos(hue),
valSquare = 0.57735f*0.57735f *(1.0f - c),
oneMinusSat = 1.0f - saturation,
valTimesSine = 0.57735f*fastSin(hue);

float weightX = oneMinusSat*0.3086f,
weightY = oneMinusSat*0.6094f,
weightZ = oneMinusSat*0.0820f;

postProcessingMatrix.setScale(brightness, brightness, brightness);
temp1.setElements(weightX + saturation, weightX, weightX, 0.0f,
weightY, weightY + saturation, weightY, 0.0f,
weightZ, weightZ, weightZ + saturation, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);

postProcessingMatrix *= temp1;

temp1.setElements(valSquare + c,
valSquare - valTimesSine,
valSquare + valTimesSine,
0.0f,
valSquare + valTimesSine,
valSquare + c,
valSquare - valTimesSine,
0.0f,
valSquare - valTimesSine,
valSquare + valTimesSine,
valSquare + c,
0.0f,
0.0f, 0.0f, 0.0f, 1.0f);

postProcessingMatrix *= temp1;
postProcessingMatrix.setTranspose();
}PS: yeah I've been busy with school lately, and I went to Europe for about 10 days :)