1

How can I increase the intensity of an image using opengl shadercode? The purpose is that the resulted image should look more brighter than actual image. Found a related link here. But it is for android.

private void CreateShaders()
{
    /***********Vert Shader********************/
    vertShader = GL.CreateShader(ShaderType.VertexShader);
    GL.ShaderSource(vertShader, @"
        attribute vec3 a_position;
        varying vec2 vTexCoord;
        void main() {
            vTexCoord = (a_position.xy + 1) / 2;
            gl_Position = vec4(a_position,1);
        }");
    GL.CompileShader(vertShader);

    /***********Frag Shader ****************/
    fragShader = GL.CreateShader(ShaderType.FragmentShader);
    GL.ShaderSource(fragShader, @"
        precision highp float;
        uniform sampler2D sTexture;
        varying vec2 vTexCoord;
        vec3 RGBtoHSV(in vec3 RGB)
        {
            vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0 / 3.0) : vec4(RGB.gb, 0.0, -1.0 / 3.0);
            vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
            float C = Q.x - min(Q.w, Q.y);
            float H = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
            vec3 HCV = vec3(H, C, Q.x);
            float S = HCV.y / (HCV.z + Epsilon);
            return vec3(HCV.x, S, HCV.z);
        }
        vec3 HSVtoRGB(in vec3 HSV)
        {
            float H = HSV.x;
            float R = abs(H * 6.0 - 3.0) - 1.0;
            float G = 2.0 - abs(H * 6.0 - 2.0);
            float B = 2.0 - abs(H * 6.0 - 4.0);
            vec3 RGB = clamp(vec3(R, G, B), 0.0, 1.0);
            return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
        }

        void main ()
        {
            vec4  color = texture2D (sTexture, vTexCoord);
            float u_saturate=0.9;
            vec3 col_hsv = RGBtoHSV(color.rgb);
            col_hsv.y *= (u_saturate * 2.0);
            vec3 col_rgb = HSVtoRGB(col_hsv.rgb);
            gl_FragColor = vec4(col_rgb.rgb, color.a);
        }");
    GL.CompileShader(fragShader);
}
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
H.NS
  • 75
  • 7

2 Answers2

2

One possibility is to convert the RGB color to a HSV (hue, saturation, value). Change the saturation and convert it back to a RGB value.

A HLSL implementation to convert between RGB and HSV can be found at RGB to HSV/HSL/HCY/HCL in HLSL.

The ported code from HLSL to GLSL is:

RGB to HSV:

const float Epsilon = 1e-10;

vec3 RGBtoHSV(in vec3 RGB)
{
    vec4  P   = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
    vec4  Q   = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
    float C   = Q.x - min(Q.w, Q.y);
    float H   = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
    vec3  HCV = vec3(H, C, Q.x);
    float S   = HCV.y / (HCV.z + Epsilon);
    return vec3(HCV.x, S, HCV.z);
}

HSV to RGB:

vec3 HSVtoRGB(in vec3 HSV)
{
    float H   = HSV.x;
    float R   = abs(H * 6.0 - 3.0) - 1.0;
    float G   = 2.0 - abs(H * 6.0 - 2.0);
    float B   = 2.0 - abs(H * 6.0 - 4.0);
    vec3  RGB = clamp( vec3(R,G,B), 0.0, 1.0 );
    return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
}

The code can be integrated in your fragment shader with ease. u_saturate is a value in the range [0.0, 1.0]. 0.5 means that the image is kept as it is. It u_saturate is greater 0.5 the image is saturated and if it is less than 0.5 the image is bleached:

void main ()
{
    vec4 color  = texture2D (sTexture, vTexCoord);

    vec3 col_hsv = RGBtoHSV(texColor.rgb);
    col_hsv.y *= (u_saturate * 2.0);
    vec3 col_rgb = HSVtoRGB(col_hsv.rgb);

    gl_FragColor = vec4(col_rgb.rgb, color.a);
}

See the WebGL example, which demonstrates the effect:

(function loadscene() {

var gl, canvas, prog, bufObj = {}, textureObj, maskTextureObj;

function render(deltaMS) {

    var saturate = document.getElementById( "saturate" ).value / 100.0;

    gl.viewport( 0, 0, vp_size[0], vp_size[1] );
    gl.enable( gl.DEPTH_TEST );
    gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
    var texUnit = 0;
    gl.activeTexture( gl.TEXTURE0 + texUnit );
    gl.bindTexture( gl.TEXTURE_2D, textureObj );
    ShProg.Use( progDraw );
    ShProg.SetI1( progDraw, "u_texture", texUnit );
    ShProg.SetF2( progDraw, "u_vp_size", vp_size );
    ShProg.SetF1( progDraw, "u_saturate", saturate );
    VertexBuffer.Draw( bufRect );

    requestAnimationFrame(render);
}  

function initScene() {

    canvas = document.getElementById( "texture-canvas");
    gl = canvas.getContext( "experimental-webgl" );
    //gl = canvas.getContext( "webgl2" );
    if ( !gl )
      return;

    var texCX = 128;
    var texCY = 128;
    var texPlan = [];
    for (ix = 0; ix < texCX; ++ix) {
        for (iy = 0; iy < texCY; ++iy) {
            var val_x = Math.sin( Math.PI * 6.0 * ix / texCX )
            var val_y = Math.sin( Math.PI * 6.0 * iy / texCY )
            texPlan.push( 128 + 127 * val_x, 63, 128 + 127 * val_y, 255 );
        }
    }
    
    textureObj = Texture.LoadTexture2D( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/supermario.jpg" );
      
    progDraw = ShProg.Create( 
      [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
        { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
      ] );
    progDraw.inPos = gl.getAttribLocation( progDraw.progObj, "inPos" );
    if ( progDraw.progObj == 0 )
        return;

    bufRect = VertexBuffer.Create(
    [ { data :  [ -1, -1, 1, -1, 1, 1, -1, 1 ], attrSize : 2, attrLoc : progDraw.inPos } ],
      [ 0, 1, 2, 0, 2, 3 ] );

    window.onresize = resize;
    resize();
    requestAnimationFrame(render);
}

function resize() {
    //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
    vp_size = [window.innerWidth, window.innerHeight]
    //vp_size = [256, 256]
    canvas.width = vp_size[0];
    canvas.height = vp_size[1];
}

var ShProg = {
Create: function (shaderList) {
    var shaderObjs = [];
    for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
        var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
        if (shderObj) shaderObjs.push(shderObj);
    }
    var prog = {}
    prog.progObj = this.Link(shaderObjs)
    if (prog.progObj) {
        prog.attrInx = {};
        var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES);
        for (var i_n = 0; i_n < noOfAttributes; ++i_n) {
            var name = gl.getActiveAttrib(prog.progObj, i_n).name;
            prog.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
        }
        prog.uniLoc = {};
        var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS);
        for (var i_n = 0; i_n < noOfUniforms; ++i_n) {
            var name = gl.getActiveUniform(prog.progObj, i_n).name;
            prog.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
        }
    }
    return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: function (source, shaderStage) {
    var shaderScript = document.getElementById(source);
    if (shaderScript)
        source = shaderScript.text;
    var shaderObj = gl.createShader(shaderStage);
    gl.shaderSource(shaderObj, source);
    gl.compileShader(shaderObj);
    var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
    if (!status) alert(gl.getShaderInfoLog(shaderObj));
    return status ? shaderObj : null;
},
Link: function (shaderObjs) {
    var prog = gl.createProgram();
    for (var i_sh = 0; i_sh < shaderObjs.length; ++i_sh)
        gl.attachShader(prog, shaderObjs[i_sh]);
    gl.linkProgram(prog);
    status = gl.getProgramParameter(prog, gl.LINK_STATUS);
    if ( !status ) alert(gl.getProgramInfoLog(prog));
    return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
    var buffer = { buf: [], attr: [], inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
    for (var i=0; i<attribs.length; ++i) {
        buffer.buf.push(gl.createBuffer());
        buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
    if ( buffer.inxLen > 0 ) {
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    }
    return buffer;
},
Draw: function(bufObj) {
    for (var i=0; i<bufObj.buf.length; ++i) {
        gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]);
        gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray( bufObj.attr[i].loc);
    }
    if ( bufObj.inxLen > 0 ) {
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
        gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
    }
    else
        gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
    for (var i=0; i<bufObj.buf.length; ++i)
        gl.disableVertexAttribArray(bufObj.attr[i].loc);
    gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

var Texture = {};
Texture.HandleLoadedTexture2D = function( image, texture, flipY ) {
    gl.activeTexture( gl.TEXTURE0 );
    gl.bindTexture( gl.TEXTURE_2D, texture );
    gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, flipY != undefined && flipY == true );
    gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
   gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
    gl.bindTexture( gl.TEXTURE_2D, null );
    return texture;
}
Texture.LoadTexture2D = function( name ) {
    var texture = gl.createTexture();
    texture.image = new Image();
    texture.image.setAttribute('crossorigin', 'anonymous');
    texture.image.onload = function () {
        Texture.HandleLoadedTexture2D( texture.image, texture, false )
    }
    texture.image.src = name;
    return texture;
}

initScene();

})();
html,body { margin: 0; overflow: hidden; }
#gui-left { position : absolute; top : 0; left : 0; color: #40f040; }
<script id="draw-shader-vs" type="x-shader/x-vertex">
    precision mediump float;

    attribute vec2 inPos;
    varying   vec2 vertPos;
    uniform   vec2 u_vp_size;

    void main()
    {
        vec2 scale  = u_vp_size.x > u_vp_size.y ? vec2(u_vp_size.y/u_vp_size.x, 1.0) : vec2(1.0, u_vp_size.x/u_vp_size.y);
        vertPos     = inPos;
        gl_Position = vec4( inPos * scale, 0.0, 1.0 );
    }
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;

    varying vec2      vertPos;
    uniform sampler2D u_texture;
    uniform float     u_saturate;

    const float Epsilon = 1e-10;
 
    vec3 RGBtoHSV(in vec3 RGB)
    {
        vec4  P   = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
        vec4  Q   = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
        float C   = Q.x - min(Q.w, Q.y);
        float H   = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
        vec3  HCV = vec3(H, C, Q.x);
        float S   = HCV.y / (HCV.z + Epsilon);
        return vec3(HCV.x, S, HCV.z);
    }

    vec3 HSVtoRGB(in vec3 HSV)
    {
        float H   = HSV.x;
        float R   = abs(H * 6.0 - 3.0) - 1.0;
        float G   = 2.0 - abs(H * 6.0 - 2.0);
        float B   = 2.0 - abs(H * 6.0 - 4.0);
        vec3  RGB = clamp( vec3(R,G,B), 0.0, 1.0 );
        return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
    }
    
    void main()
    {
        vec2 texCoord = vec2( vertPos.s, -vertPos.t ) * 0.5 + 0.5;
        vec3 texColor = texture2D( u_texture, texCoord.st ).rgb;

        vec3 col_hsv = RGBtoHSV(texColor.rgb);
        col_hsv.y *= (u_saturate * 2.0); 
        vec3 col_rgb = HSVtoRGB(col_hsv.rgb);

        gl_FragColor  = vec4( col_rgb.rgb, 1.0 );
    }
</script>

<div id="gui-left">
  <table>
  <tr><td>saturate</td><td><input type="range" id="saturate" min="0" max="100" value="50"/></td></tr>
  </table>
</div>

<canvas id="texture-canvas" style="border: none"></canvas>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • please see my edited answer as suggested by you. But it shows a black image. calling RGBtoHSV(texColor.rgb) gives black color – H.NS Jan 11 '19 at 10:17
  • @H.NS It has to be `vTexCoord = (a_position.xy + 1.0) / 2.0;` - focus on `1.0` and `2.0`. Further `const float Epsilon = 1e-10;` is missing in the fragment shader. Possibly the image has to be flipped : `vTexCoord = (vec2(a_position.x, 1.0-a_position.y) + 1.0) / 2.0;` – Rabbid76 Jan 11 '19 at 10:28
  • Hi ,instead of selecting u_saturate, can I find average intensity and set it as col_hsv.y ? – H.NS Feb 01 '19 at 05:54
  • 1
    @H.NS To do this in the fragment shader would be a bad idea, because you would do it once again for every fragment. DO it on the CPU or use a compute shader in an leading process. Alternatively generate mipmaps and read the average from the highest mipmap level. But this is a completely new Task and probably new question, something like: *"How to bet the "average intensity" of a texture in OpenTK?"* – Rabbid76 Feb 01 '19 at 06:00
0

If you just want to increase the intensity of a single Color, in the pixel shader Code

vec4  color = texture2D (sTexture, vTexCoord);
color.g = color.g * 1.2;
gl_FragColor = color;

And green will be 1.2 times brighter. If you want to change stuffs like the intensity of all the colors, then you need to look at stuffs like the HSL color space. I answered a question about that: GLSL shader to boost the color

florent teppe
  • 529
  • 5
  • 13