这篇GPGPU 概念1: 数组= 纹理 - 文档文章提出的数组与纹理相等让人打开新的眼界与思维,本文在这文基础上,尝试把这部分思想拿来用在VBO粒子系统上.
在前面的文章中,我们把CPU的数据传到GPU后,然后就直接从桢缓冲到显示屏幕上了,那么还能不能把从GPU的数据拿回来放入CPU,然后进行处理。例如最基本的GPGPU编程中,把数组放入GPU运算后返回CPU。以及图片用GPU来加速处理。
和以前把数据从CPU通过相关GLSL,Cg着色器语言传入数据到GPU并进行处理不同,多了个返回数据到CPU中,因为GPU擅长的是并行处理,所以我们一般把一系列数据处理从CPU中交给GPU。在前面的顶点拾取中,获取地形高度中,我们都把一系列数据当做纹理,然后从CPU传入GPU中处理。现在关键问题是,如果把GPU数据送回到CPU中。
在前面这篇文章中(WebGL 利用FBO完成立方体贴图),我们看到前面,可以先在FBO里,把当前的内存数据经过GPU处理后输出到应用程序桢缓冲关联的纹理中,然后此纹理拿来做后面球所需要的立方体贴图。在这过程中,我们可以知道通过FBO实现,数据从CPU处理传入GPU,在着色器中进行处理后,然后我们可以在CPU中得到对应FBO中纹理里的数据。
下面我们完成一个简单实例,通过把一个数组,传入GPU中,在GPU中进行处理,然后返回到CPU中并显示出来。在这我们只介绍,数据一对一的处理,就是传入多少数据,返回多少数据。
结合上面所说,大致过程如下,第一步创建FBO,并关联一个纹理(存放经过GPU处理的数据),然后再创建一个纹理,里面用来存放我们需要处理的数据,因此要求,这个纹理和FBO中关联的纹理大小一样,方便处理。第二步,我们在创建的FBO中,来完成一个输出渲染,因为上步,要求相同大小的纹理,所以这个渲染窗口我们需要特殊处理下,在这窗口,最大保证窗口大小与纹理大小一样,纹理刚好占用整个窗口大小。这样才能保证输出到FBO中的纹理和传入的纹理索引一一对应。这样FBO中的纹理就存放的是原始数据经过GPU处理后的数据。
让我们来完成整个过程,首先,我们把转入的数据整理成我们需要的数组列表。请看代码如下:
1 def __init__(this,*args): 2 this.imageFormat = GL_FLOAT 3 if len(args) == 1 : 4 if isinstance(args[0],Image): 5 image = args[0] 6 this.width = image.size[0] 7 this.height = image.size[1] 8 this.data = image.im 9 this.imageFormat = GL_UNSIGNED_BYTE 10 if isinstance(args[0],list): 11 listf = args[0] 12 this.width = len(listf) if len(listf) > 0 else 1 13 this.height = 1 14 this.data = listf 15 if len(args) == 3: 16 this.width = int(args[0]) 17 this.height = int(args[1]) 18 this.data = args[2] 19 this.imagedata = [] 20 for i in range(this.height): 21 for j in range(this.width): 22 index = i * this.width + j 23 f4 = this.data[index] if index < len(this.data) else float(j) / float(this.width) 24 r,g,b,a = 0.0,0.0,0.0,0.0 25 if isinstance(f4,tuple): 26 if len(f4) == 2: 27 r,g = f4 28 elif len(f4) == 3: 29 r,g,b = f4 30 else: 31 r,g,b,a = f4 32 else: 33 r = f4 34 this.imagedata.append(r) 35 this.imagedata.append(g) 36 this.imagedata.append(b) 37 this.imagedata.append(a) 38 #this.imagedata = ny.array(this.imagedata,dtype=ny.float) 39 #print this.imagedata