初始化工作;
1)首先使用glGenTextures 创建纹理;
2)然后调用glBindTexture 绑定纹理,设置纹理的一些过滤特征(glTexParameterf);
3)glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); 和纹理图像关联,在这里分配
纹理图像的内存;
4)glBindTexture(GL_TEXTURE_2D, 0);
5)创建一个fbo glGenFramebuffersEXT(1, &fboId);
6)绑定fbo glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
7)将fbo绑定在你创建的纹理中(textureid 和fboId 关联);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0);
8) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
9)检查fbo状态;
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
10)从内存将数据拷贝到纹理,纹理是什么东西呢,
glBindTexture(GL_TEXTURE_2D, textureId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, witdh, size, GL_RGBA, GL_UNSIGNED_BYTE, pBuff);
注意:在调用glTexSubImage2D之前一定要绑定某个纹理,该函数将pBuff,拷贝到纹理缓冲区;
GPGPU 概念 1: 数组 = 纹理
一维数组是本地CPU最基本的数据排列方式,多维的数组则是通过对一个很大的一维数组的基准入口进行坐标偏移来访问的(至少目前大多数的编译器都是这样做的)。一个小例子可以很好说明这一点,那就是一个MxN维的数组 a[i][j] = a[i*M+j];我们可能把一个多维数组,映射到一个一维数组中去。这些数组我开始索引都被假定为0;
而对于GPU,最基本的数据排列方式,是二维数组。一维和三维的数组也是被支持的,但本教程的技术不能直接使用。数组在GPU内存中我们把它叫做纹理或者是纹理样本。纹理的最大尺寸在GPU中是有限定的。每个维度的允许最大值,通过以下一小段代码便可能查询得到,这些代码能正确运行,前提是OpenGL的渲染上下文必须被正确初始化。
int maxtexsize; glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxtexsize); printf("GL_MAX_TEXTURE_SIZE, %d ",maxtexsize);
把数据从CPU的数组传输到GPU的纹理
为了把数据传输到纹理中去,我们必须绑定一个纹理作为纹理目标,并通过一个GL函数来发送要传输的数据。实际上就是把数据的首地址作为一个参数传递给该涵数,并指定适当的纹理大小就可以了。如果用LUMINANCE格式,则意味着数组中必须有texSize x texSize个元数。而RGBA格式,则是这个数字的4倍。注意的是,在把数据从内存传到显卡的过程中,是全完不需要人为来干预的,由驱动来自动完成。一但传输完成了,我们便可能对CPU上的数据作任意修改,这不会影响到显卡中的纹理数据。 而且我们下次再访问该纹理的时候,它依然是可用的。在NVIDIA的显卡中,以下的代码是得到硬件加速的。
glBindTexture(texture_target, texID); glTexSubImage2D(texture_target,0,0,0,texSize,texSize, texture_format,GL_FLOAT,data);
这里三个值是0的参数,是用来定义多重映像纹理的,由于我们这里要求一次把整个数组传输一个纹理中,不会用到多重映像纹理,因此把它们都关闭掉。
以上是NVIDIA显卡的实现方法,但对于ATI的显卡,以下的代码作为首选的技术。在ATI显卡中,要想把数据传送到一个已和FBO绑定的纹理中的话,只需要把OpenGL的渲染目标改为该绑定的FBO对像就可以了。
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glRasterPos2i(0,0);
glDrawPixels(texSize,texSize,texture_format,GL_FLOAT,data);
第一个函数是改变输出的方向,第二个函数中我们使用了起点作为参与点,因为我们在第三个函数中要把整个数据块都传到纹理中去。
两种情况下,CPU中的数据都是以行排列的方式映射到纹理中去的。更详细地说,就是:对于RGBA格式,数组中的前四个数据,被传送到纹理的第一个元素的四个分量中,分别与R,G,B,A分量一一对应,其它类推。而对于LUMINANCE 格式的纹理,纹理中第一行的第一个元素,就对应数组中的第一个数据。其它纹理元素,也是与数组中的数据一一对应的。
把数据从GPU纹理,传输到CPU的数组
这是一个反方向的操作,那就是把数据从GPU传输回来,存放在CPU的数组上。同样,有两种不同的方法可供我们选择。传统上,我们是使用OpenGL获取纹理的方法,也就是绑定一个纹理目标,然后调用glGetTexImage()这个函数。这些函数的参数,我们在前面都有见过。
glBindTexture(texture_target,texID);
glGetTexImage(texture_target,0,texture_format,GL_FLOAT,data);
但是这个我们将要读取的纹理,已经和一个FBO对像绑定的话,我们可以采用改变渲染指针方向的技术来实现。
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0,0,texSize,texSize,texture_format,GL_FLOAT,data);
由于我们要读取GPU的整个纹理,因此这里前面两个参数是0,0。表示从0起始点开始读取。该方法是被推荐使用的。
一个忠告:比起在GPU内部的传输来说,数据在主机内存与GPU内存之间相互传输,其花费的时间是巨大的,因此要谨慎使用。由其是从CPU到GPU的逆向传输。
在前面“ 当前显卡设备运行的问题” 中 提及到该方面的问题。
绘图:
1)首先渲染到纹理
void RenderToTarget(int width,int height)
{
glBindTexture(GL_TEXTURE_2D, 0);
// 激活fbo
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
glPushAttrib(GL_VIEWPORT_BIT|GL_COLOR_BUFFER_BIT);
glViewport(0,0,width, height);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
//绘图代码
myDraw();
glPopAttrib();
//恢复绑定默认缓存(有一点值得注意的是:当我用使用数字0,来绑定一个FBO的时候,无论何时,它都会还原window系统的特殊帧缓冲区,这一特性在一些高级应用中会很有用)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
然后将刚才渲染的纹理 渲染回屏幕;
void COpenGlWrapper::Render(int width,int height)
{
// 绑定默认FBO(窗体帧缓冲区的ID是0)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glBindTexture(GL_TEXTURE_2D, textureId);
glViewport(0,0,width, height);
// 渲染
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBegin(GL_POLYGON);
glColor3f(1, 1, 1);
glTexCoord2f(1, 1); //纹理坐标
glVertex3d( 1, 1, 0); //
glTexCoord2f(0, 1);
glVertex3d(-1, 1, 0);
glTexCoord2f(0, 0);
glVertex3d(-1, -1, 0);
glTexCoord2f(1, 0);
glVertex3d( 1, -1, 0);
glEnd();
}
opengl cg 着色器有用链接:
http://blog.csdn.net/why_study/article/details/8205505
http://blog.csdn.net/pizi0475/article/details/7932920
http://www.cnblogs.com/zhouxin/p/3474396.html
http://blog.csdn.net/huawenguang/article/details/1530547
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>
int
main(
int
argc,
char
**argv)
…
{
//
这里声明纹理的大小为:teSize;而数组的大小就必须是texSize*texSize*4
int
texSize = 2;
int
i;
//
生成测试数组的数据
float
* data = (
float
*)malloc(4*texSize*texSize*
sizeof
(
float
));
float
* result = (
float
*)malloc(4*texSize*texSize*
sizeof
(
float
));
for
(i=0; i<texSize*texSize*4; i++)
data[i] = (i+1.0)*0.01F;
//
初始化OpenGL的环境
glutInit (&argc, argv);
glutCreateWindow(“TEST1”);
glewInit();
//
视口的比例是 1:1 pixel=texel=data 使得三者一一对应
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,texSize,0.0,texSize);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0,0,texSize,texSize);
//
生成并绑定一个FBO,也就是生成一个离屏渲染对像
GLuint fb;
glGenFramebuffersEXT(1,&fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);
//
生成两个纹理,一个是用来保存数据的纹理,一个是用作渲染对像的纹理
GLuint tex,fboTex;
glGenTextures (1, &tex);
glGenTextures (1, &fboTex);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB,fboTex);
//
设定纹理参数
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_T, GL_CLAMP);
//
这里在显卡上分配FBO纹理的贮存空间,每个元素的初始值是0;
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB,
texSize,texSize,0,GL_RGBA,GL_FLOAT,0);
//
分配数据纹理的显存空间
glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,GL_DECAL);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB,
texSize,texSize,0,GL_RGBA,GL_FLOAT,0);
//
把当前的FBO对像,与FBO纹理绑定在一起
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_RECTANGLE_ARB,fboTex,0);
//
把本地数据传输到显卡的纹理上。
glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,texSize,texSize,
GL_RGBA,GL_FLOAT,data);
//
——————–begin————————-
//
以下代码是渲染一个大小为texSize * texSize矩形,
//
其作用就是把纹理中的数据,经过处理后,保存到帧缓冲中去,
//
由于用到了离屏渲染,这里的帧缓冲区指的就是FBO纹理。
//
在这里,只是简单地把数据从纹理直接传送到帧缓冲中,
//
没有对这些流过GPU的数据作任何处理,但是如果我们会用CG、
//
GLSL等高级着色语言,对显卡进行编程,便可以在GPU中
//
截获这些数据,并对它们进行任何我们所想要的复杂运算。
//
这就是GPGPU技术的精髓所在.
glColor4f(1.00f,1.00f,1.00f,1.0f);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex2f(0.0, 0.0);
glTexCoord2f(texSize, 0.0);
glVertex2f(texSize, 0.0);
glTexCoord2f(texSize, texSize);
glVertex2f(texSize, texSize);
glTexCoord2f(0.0, texSize);
glVertex2f(0.0, texSize);
glEnd();
//
——————–end————————
//
从帧缓冲中读取数据,并把数据保存到result数组中。
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0, 0, texSize, texSize,GL_RGBA,GL_FLOAT,result);
//
显示最终的结果
printf(“Data before roundtrip: “);
for
(i=0; i<texSize*texSize*4; i++)
printf(“%f “,data[i]);
printf(“Data after roundtrip: “);
for
(i=0; i<texSize*texSize*4; i++)
printf(“%f “,result[i]);
//
释放本地内存
free(data);
free(result);
//
释放显卡内存
glDeleteFramebuffersEXT (1,&fb);
glDeleteTextures (1,&tex);
glDeleteTextures(1,&fboTex);
return
0;
}
--------------CPU---------------- -------------GPU------------ | | | | | data arr: | | texture: | | [][][][][][][][][] --------------> [][][] | | | | [][][] | | | | [][][] | | | | / | | result: | | FBO: | | [][][][][][][][][] | | [][][] | | <----------------- [][][] | | | | [][][] | |-------------------------------| |--------------------------|
以上代码是理解GPU编程的基础,如果你完全看得懂,并且能对这代码作简单的修改运用的话,那恭喜你,你已经向成功迈进了一大步,并可以继续往下看,走向更深入的学习了。但如看不懂,那回头再看一编吧。