刚开始学习GLSL的时候,感觉非常痛苦,资料非常少是一个原因,更重要的原因就是没有现成的可运行的代码可以,后来在ARC公司实习花了一个月吧,终于对GLSL作为加速计算的方面的知识有了一定的了解。感谢ARC,感谢我的师傅兼师兄孙XX!
请不要将将此代码用于商业用途,如转载,请注明出处:http://blog.csdn.net/gningh/article/details/9615631
我所做的工作并不是用GLSL实现各种绚丽的图形渲染,而是用它做图像处理和加速运算。
这里有一些小的注意事项,
1.我们处理图像的时候一般是把图像载入纹理缓冲,当做纹理来处理,这样可以加快速度。
2.纹理坐标默认的是归一化的坐标形式,我们这里用的是原始的图像坐标,这样可以更加容易写代码。可以了解一下texture2DRect与textu2D的区别
3.代码中使用了一个LoadBMP函数,这个函数在<LoadBMP.h>中定义的。这个函数是公司的,所以我不能侵权把它贴出来,大家可以自己从网上搜索读取BMP的函数,一搜一大片,这个函数主要的目的就是取得BMP图像的数据的指针,而且这个函数功能有点弱,只能读取24位的BMP。
4.高斯模板我用的是
下面就是代码了:
//
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include "LoadBMP.h"
//纹理的编号
static GLuint texture;
#define printOpenGLError() printOglError(__FILE__, __LINE__)
//纹理图像的高和宽
const GLint imgHeight = 512, imgWidth = 512;
//顶点着色器
const char *vShader = {
"#version 110 \n "
"void main()"
"{"
"gl_TexCoord[0] = gl_MultiTexCoord0;"
"gl_Position = ftransform();"
"}"
};
/***************片元着色器代码************************
**注意代码中的pos变量代表的是实际的坐标,因为读入bmp图像的
**数据后,数据是从下往上存放的,这里要将坐标纠正一下。加入\n
**是为了在编译着色器代码时可以准确找到出错的行数,否则
**显示的都是一行
**********************************************************/
const char *fShader = {
"#version 100 \n "
"#extension GL_ARB_texture_rectangle : enable \n"
"const int KernelSize = 9; \n"
"uniform vec2 Offset[KernelSize]; \n"
"uniform vec4 KernelValue[KernelSize]; \n"
"uniform sampler2DRect LenaTexture; \n"
"void main() \n"
"{ \n"
"vec2 pos = vec2(gl_TexCoord[0].s,512.0-gl_TexCoord[0].t); \n"
"int i; \n"
"vec4 sum = vec4(0.0); \n"
"for ( i = 0; i < KernelSize; ++i ) \n"
"{ \n"
" vec4 tmp = texture2DRect(LenaTexture,pos + Offset[i]); \n"
" sum += tmp * KernelValue[i]; \n"
"} \n"
"gl_FragColor = sum; \n"
"} \n"
};
//打印错误信息
int printOglError(char *file, int line)
{
GLenum glErr;
int retCode = 0;
glErr = glGetError();
while (glErr != GL_NO_ERROR)
{
printf("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
retCode = 1;
glErr = glGetError();
}
return retCode;
}
//打印openGL出错信息
void printInfoLog(GLhandleARB obj)
{
int infologLength = 0;
int charsWritten = 0;
GLcharARB *infoLog;
printOpenGLError();
glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
printOpenGLError();
if(infologLength > 0)
{
infoLog = (GLcharARB*)malloc(infologLength);
if(infoLog == NULL)
{
printf("ERROR: Could not allocate InfoLog buffer\n");
exit(1);
}
glGetInfoLogARB(obj,infologLength,&charsWritten,infoLog);
printf("InfoLog:\n%s\n\n",infoLog);
free(infoLog);
}
printOpenGLError();
}
/*************************************************************
function name: initShaders
input: 1. const char *vShaderCode,
2. const char *fShaderCode,
output: 1. -1 compile error
2. -2 link error
3. progHandle
description:编译连接并使用着色器程序,输入为上面定义的char*型数组
输出就是一个可用的着色器句柄,返回的句柄主要为了初始化
内置的uniform变量,检查内部错误的用途
*****************************************************************/
GLhandleARB initShaders( const char *vShaderCode, const char *fShaderCode ) //改为初始化的函数
{
GLhandleARB vertHandle, fragHandle, progHandle; //对象句柄
GLint vertCompiled, fragCompiled; //状态值
GLint linked;
//创建顶点着色器对象和片元着色器对象
vertHandle = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
fragHandle = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
//将源代码字符串加载到着色器中
glShaderSource( vertHandle, 1, &vShaderCode, NULL );
glShaderSource( fragHandle, 1, &fShaderCode, NULL );
printf("编译码块顶点着色器并打印编译器日志文件:\n");
//编译码块顶点着色器并打印编译器日志文件
glCompileShaderARB(vertHandle);
printOpenGLError(); //检查opengl错误
glGetObjectParameterivARB(vertHandle,GL_OBJECT_COMPILE_STATUS_ARB, &vertCompiled);
printInfoLog(vertHandle);
printf("编译码块片元着色器并打印编译器日志文件:\n");
//编译码块片元着色器并打印编译器日志文件
glCompileShaderARB(fragHandle);
printOpenGLError(); //检查opengl错误
glGetObjectParameterivARB(fragHandle,GL_OBJECT_COMPILE_STATUS_ARB, &fragCompiled);
printInfoLog(fragHandle);
if(!vertCompiled || !fragCompiled)
return -1;
//创建一个程序对象并附加两个编译好的着色器
progHandle = glCreateProgramObjectARB();
glAttachObjectARB(progHandle, vertHandle);
glAttachObjectARB(progHandle, fragHandle);
printf("链接程序对象并打印信息日志:\n");
//链接程序对象并打印信息日志
glLinkProgramARB(progHandle);
printOpenGLError(); //检查opengl错误
glGetObjectParameterivARB(progHandle, GL_OBJECT_LINK_STATUS_ARB, &linked);
printInfoLog(progHandle);
if(!linked)
return -2;
//将程序对象安装为当前状态的一部分
glUseProgramObjectARB(progHandle); //改为运行的函数,用于测试该算法的时间
return progHandle;
}
//读入一个bmp图像作为纹理,对bmp图像的高斯滤波就是对这个纹理做滤波
int LoadBmpAsTexture(char *textureFilePath, GLuint &texID )
{
unsigned char *pTexData = NULL; //指向数据的指针
long bitCnt = 0;
long iw =0;
long ih = 0;
long status = LoadBMP( textureFilePath, &pTexData,&iw, &ih, &bitCnt );
glGenTextures( 1, &texID );
glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texID);
//当卷积内核超过了图像边界时使用图像边缘的像素值
glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
//纹理过滤的方式不应该设置为线性插值
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, imgWidth, imgHeight,
0,GL_RGB,GL_UNSIGNED_BYTE,pTexData );
glTexEnvi( GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE );
return 0;
}
void init()
{
glShadeModel( GL_FLAT );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glViewport(0,0, imgWidth, imgHeight );
glEnable ( GL_DEPTH_TEST );
}
void display( void )
{
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// glEnable( GL_TEXTURE_2D); //注意:要想使用着色器,一定不能激活纹理!!
glBindTexture(GL_TEXTURE_2D,texture);
//纹理坐标的设置
glBegin( GL_QUADS );
glTexCoord2f( 0.0, 0.0); glVertex2f( -1.0, -1.0);
glTexCoord2f( 0.0, imgHeight); glVertex2f( -1.0, 1.0 );
glTexCoord2f( imgWidth, imgHeight ); glVertex2f( 1.0, 1.0 );
glTexCoord2f( imgWidth, 0.0 ); glVertex2f ( 1.0, -1.0 );
glEnd( );
glFlush();
}
void PerformComputation()
{
LoadBmpAsTexture("texture.bmp",texture );
display();
}
int _tmain(int argc, char* argv[])
{
GLhandleARB progHandle = 0;
//偏移数组
float offset[18]={
-1,-1,
-1,0,
1,1,
-1,0,
0,0,
1,0,
1,-1,
1,0,
1,1
};
//高斯核
float kernelValue[36]={
0.0625,0.0625,0.0625,0.0625,
0.125,0.125,0.125,0.125,
0.0625,0.0625,0.0625,0.0625,
0.125,0.125,0.125,0.125,
0.25,0.25,0.25,0.25,
0.125,0.125,0.125,0.125,
0.0625,0.0625,0.0625,0.0625,
0.125,0.125,0.125,0.125,
0.0625,0.0625,0.0625,0.0625
};
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_SINGLE| GLUT_LUMINANCE);
glutInitWindowSize ( imgWidth, imgHeight);
glutInitWindowPosition( 100, 100 );
glutCreateWindow(" gningh高斯滤波 ");
glewInit();
init();
progHandle = initShaders(vShader, fShader);
if ( progHandle <= 0 )
printf("Failed to run shader.\n");
else{
//设置初始一致变量
glUniform1i( glGetUniformLocation( progHandle, "LenaTexture" ), 0 ); //0 是纹理的句柄
glUniform4fv( glGetUniformLocation( progHandle, "KernelValue"),9,kernelValue);
glUniform2fv( glGetUniformLocation( progHandle, "Offset"),9,offset);
}
PerformComputation();
glutMainLoop();
return 0;
}
处理前的图像为:
高斯滤波后的图像为:
其实高斯滤波的模板可以换为拉普拉斯的模板,均值的模板等等~~这样就可以并行处理图像了