GLSL实现图像处理

来源:互联网 发布:保密式网络机柜 编辑:程序博客网 时间:2024/05/15 00:04

今天晚上没事将以前弄的OPENGL着色语言实现滤镜效果的实现和大家分享一下,滤镜主要包括图像的对比度调整、浮雕效果、彩色图片灰度化、卷积等操作。

         这些操作其本质上是属于图像处理的东西,OpenGL实现图像处理主要是用到了GLSL着色语言。具体到着色器就是片段着色器。

         说白了就是如何用OpenGL做通用GPU计算,OpenGL做通用计算的步骤主要如下:

读取数据->顶点着色器->片段着色器->渲染到纹理->从纹理读写数据。

 

         步骤具体细化如下:

(1)初始化。

         初始化窗口系统。

void InitGLUT(int argc,char**argv)

{

    glutInit(&argc,argv);

    glutWindow = glutCreateWindow("GPGPU");

}

 

初始化FBO。在此阶段还需要将投影设置为正摄投影的方式,视口与图像的幅宽一样大,并且还要讲纹理重采样方式设置为最邻近采样,这样就保证了片段操作的时候取得每个数据都是原始数据。

void InitFBO(int nWidth,intnHeight,GLuint*pFb)

{

    //创建FBO并绑定

    glewInit();

    GLuint fb;

    glGenFramebuffersEXT(1,&fb);

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);

    *pFb = fb;

 

    //用绘制来调用

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    gluOrtho2D(0,nWidth,0,nHeight);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    glViewport(0,0,nWidth,nHeight);

}

 

初始化shader,包括从文件中读取数据以及编译等操作。

void InitGLSL()

{

    //创建顶点shader

    vertShader = glCreateShader(GL_VERTEX_SHADER);

    if( 0 == vertShader)

    {

        fprintf(stderr,"Error creating vertex shader.\n");

        exit(1);

    }

 

    GLint result;

 

    //加载片元着色器

    fragShader = glCreateShader(GL_FRAGMENT_SHADER);

    if( 0 == fragShader)

    {

        fprintf(stderr,"片元着色器创建失败.\n");

        exit(1);

    }

 

    //c拷贝shader源码

    const GLchar *shaderCode2 = loadShaderAsString("BrightNess.glsl");

    glShaderSource( fragShader,1, &shaderCode2, NULL );

 

    delete []shaderCode2;

 

    //编译shader

    glCompileShader(fragShader);

 

    //检查编译状态

    glGetShaderiv( fragShader,GL_COMPILE_STATUS, &result );

    if (GL_FALSE ==result)

    {

        fprintf( stderr,"片元着色器编译失败!\n" );

        GLint logLen;

        glGetShaderiv( fragShader,GL_INFO_LOG_LENGTH, &logLen );

        if( logLen > 0)

        {

            char * log = (char *)malloc(logLen);

            GLsizei written;

            glGetShaderInfoLog(fragShader,logLen, &written,log);

            fprintf(stderr,"Shaderlog:\n%s",log);

            free(log);

        }

    }

 

    //创建程序对象

    programHandle = glCreateProgram();

    if( 0 == programHandle)

    {

        fprintf(stderr,"Error creating program object.\n");

        exit(1);

    }

 

    //将着色器链接到程序对象

    glAttachShader(programHandle,fragShader);

 

    //链接程序

    glLinkProgram(programHandle);

 

    //检查链接状态

    GLint status;

    glGetProgramiv( programHandle,GL_LINK_STATUS, &status );

    if( GL_FALSE == status )

    {

        fprintf( stderr,"链接失败!\n" );

        GLint logLen;

        glGetProgramiv(programHandle,GL_INFO_LOG_LENGTH,

            &logLen);

        if( logLen > 0)

        {

            char * log = (char *)malloc(logLen);

            GLsizei written;

            glGetProgramInfoLog(programHandle,logLen,

                &written, log);

            fprintf(stderr,"Program log: \n%s",log);

            free(log);

        }

 

        glDeleteProgram(programHandle);

    }

 

    else

    {

        glUseProgram(programHandle);

    }

}

 

(2)创建纹理对象以及设置参数

这里需要创建两个纹理对象,一个用于输入图像,另外一个用于保存输出的结果,即将保存结果的纹理对象绑定到帧缓存对象上,即实现渲染到纹理的目标。

//创建纹理对象并绑定

    GLuint xTexID,yTexID;

    glGenTextures(1,&xTexID);

    glGenTextures(1,&yTexID);

 

设置纹理属性如下:

void SetupTexture(stTextureParatextureParameters,constGLuinttexID,intnWidth,int nHeight)

{

    glBindTexture(textureParameters.texTarget,texID);

 

    //设置纹理参数

    glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

    glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

    glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_S,GL_CLAMP);

    glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_T,GL_CLAMP);

    //定义纹理数据单元类型

    glTexImage2D(textureParameters.texTarget,0,textureParameters.texInternalFormat,nWidth,nHeight,0,

        textureParameters.texFormat,GL_FLOAT,0);

 

}

 

然后将输入数据加载到输入纹理缓存上面

//传入输入数据

    glBindTexture(textureParameters.texTarget,xTexID);

glTexSubImage2D(textureParameters.texTarget,0,0,0,nWidth,nHeight,textureParameters.texFormat,GL_FLOAT,pfInput);

//设置映射参数,替换原来的数据

    glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);

 

最后是将输出纹理绑定到帧缓存对象上并激活输入纹理对象。

//绑定输出纹理缓存

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,textureParameters.texTarget,yTexID,0);

 

    //激活当前输入的纹理单元

    glActiveTexture(GL_TEXTURE0);

 

绑定的关联点就是GL_COLOR_ATTACHMENT0_EXT,它代表是帧缓存中的一个颜色缓存。

 

(3)设置渲染的对象

glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

glPolygonMode(GL_FRONT,GL_FILL);

 

(4)通过绘制来调用“核函数”

这里是一个形象的比喻,就是通过绘制操作来实现对图像的处理,类似于CUDA和opencl里面的核函数调用。

//用绘制进行调用

    glBegin(GL_QUADS);

    glTexCoord2f(0,0);

    glVertex2f(0,0);

    glTexCoord2f(nWidth,0);

    glVertex2f(nWidth,0);

    glTexCoord2f(nWidth,nHeight);

    glVertex2f(nWidth,nHeight);

    glTexCoord2f(0,nHeight);

    glVertex2f(0,nHeight);

    glEnd();

 

    glFinish();

 

(5)读取结果数据以及进一步处理

//从当前纹理缓存拷贝数据

void TransformFromTex(intnWidth,intnHeight,float *pfData)

{

    //读取数据

    glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

    glReadPixels(0,0,nWidth,nHeight,GL_RGB,GL_FLOAT,pfData);

}

 

这相当于将数据从显存拷贝到主机内存,然后可以写入文件等。

 

说了这么多,最后还剩下一个片段着色器的编写。上面相当于把框架已经搭建好,然后只需要更改片段着色器实现不同的图像处理效果了。

这边就以一副风景画为例子实现集中不同的效果。

原图如下:


1、对比度调整,代码如下:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. #version 400 compatibility  
  2.   
  3. #extension GL_ARB_texture_rectangle : enable  
  4. uniform sampler2DRect uImageUint;  
  5.   
  6. float uT = 0.5;  
  7.   
  8. out vec4 outColor;  
  9. //图像对比度增强  
  10.   
  11. void main()  
  12. {  
  13.     vec2 pos = gl_TexCoord[0].st;  
  14.     vec3 irgb = texture(uImageUint,pos).rgb;  
  15.     //vec3 target = vec3(128.0,128.0,128.0);  
  16.       
  17.     vec3 target = vec3(0.0,0.0,0.0);  
  18.       
  19.     outColor = vec4(mix(target,irgb,uT),1.0);  
  20. }  

处理后效果如下:


2、浮雕效果代码如下:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. #version 400 compatibility  
  2.   
  3. #extension GL_ARB_texture_rectangle : enable  
  4. uniform sampler2DRect tex;  
  5.   
  6. out vec4 outColor;  
  7. //浮雕效果  
  8.   
  9. void main()  
  10. {  
  11.     vec2 pos = gl_TexCoord[0].st;  
  12.     //获得纹理的尺寸  
  13.     ivec2 vecSize = textureSize(tex);  
  14.     float ResS = float(vecSize.s);  
  15.     float ResT = float(vecSize.t);  
  16.     vec3 irgb = texture(tex,pos).rgb;  
  17.       
  18.     vec2 stpp = vec2(-1.0,-1.0);  
  19.     vec3 c00 = texture(tex,pos).rgb;  
  20.     vec3 cp1p1 = texture(tex,pos+stpp).rgb;  
  21.       
  22.     vec3 diffs = c00 - cp1p1;  
  23.     float maxV = diffs.r;  
  24.     if(abs(diffs.g) > abs(maxV)) maxV = diffs.g;  
  25.     if(abs(diffs.b) > abs(maxV)) maxV = diffs.b;  
  26.       
  27.     float gray = clamp(maxV + 128,0.0,255.0);  
  28.     outColor = vec4(gray,gray,gray,1.0);  
  29. }  


处理后的图像如下:



3、颠倒显示

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. #version 400 compatibility  
  2.   
  3. #extension GL_ARB_texture_rectangle : enable  
  4. uniform sampler2DRect tex;  
  5.   
  6. out vec4 outColor;  
  7. //图像翻转效果  
  8.   
  9. void main()  
  10. {  
  11.     vec2 pos = gl_TexCoord[0].st;  
  12.     //获得纹理的尺寸  
  13.     ivec2 vecSize = textureSize(tex);  
  14.     int nWidth = (vecSize.s);  
  15.     int nHeight = (vecSize.t);  
  16.     pos = vec2(nWidth- pos.x-1,nHeight - pos.y - 1);  
  17.     vec3 irgb = texture(tex,pos).rgb;  
  18.       
  19.     outColor = vec4(irgb,1.0);  
  20. }  

处理后效果如下:



4、图像黑白化
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. #version 400 compatibility  
  2.   
  3. #extension GL_ARB_texture_rectangle : enable  
  4. uniform sampler2DRect tex;  
  5.   
  6. const vec3 W = vec3(0.2125,0.7154,0.0721);  
  7.   
  8. out vec4 outColor;  
  9. void main()  
  10. {  
  11.     vec2 pos = gl_TexCoord[0].st;  
  12.       
  13.     vec3 color = texelFetch(tex,ivec2(pos.x+0.5,pos.y+0.5)).rgb;  
  14.     float luminace = dot(color,W);  
  15.     outColor = vec4(luminace,luminace,luminace,1.0);  
  16.     //gl_FragColor = fResult;  
  17. }  
处理后图片如下:


5、图像的卷积,这个稍微复杂一点,其实也很简单,基本图像处理的东西。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. #version 400 compatibility  
  2.   
  3. #extension GL_ARB_texture_rectangle : enable  
  4. uniform sampler2DRect tex;  
  5. float fRadius = 2;  
  6.   
  7. out vec4 outColor;  
  8.   
  9. void main()  
  10. {  
  11.     vec2 pos = gl_TexCoord[0].st;  
  12.     vec4 fSum = vec4(0.0,0.0,0.0,0.0);  
  13.     vec4 fTotal = vec4(0.0,0.0,0.0,0.0);  
  14.     vec4 fResult = vec4(0.0,0.0,0.0,0.0);  
  15.       
  16.     ivec2 vecSize = textureSize(tex);  
  17.       
  18.     for (float i = pos.x - fRadius; i < pos.x + fRadius + 0.5; i += 1.0)  
  19.         for (float j = pos.y - fRadius; j < pos.y + fRadius + 0.5; j += 1.0)  
  20.         {  
  21.             if (i >=0 && j >= 0 && i < vecSize.x && j < vecSize.y)  
  22.             {  
  23.                 fSum += texture2DRect(tex,vec2(i+0.5,j+0.5));  
  24.                 fTotal += vec4(1.0,1.0,1.0,1.0);  
  25.             }  
  26.         }  
  27.     vec4 color = texelFetch(tex,ivec2(pos.x+0.5,pos.y+0.5));  
  28.     fResult = fSum/fTotal;  
  29.     outColor = fResult;  
  30.     //gl_FragColor = fResult;  
  31. }  


处理后的效果如下:


上面这些都是最基本的图像处理操作,使用shader其实就是利用了显卡的并行数据处理能力,在今天的大数据时代以及资源不足的手机上,如果能充分利用GPU的优势,对于图像的处理将会有速度上的很大提升。
0 0
原创粉丝点击