PBO: Pixel buffer object
FBO: Frame buffer object
VBO: Vertex buffer object
缺点:
方法 2: 拷贝到像素缓冲对像(PBO)
PBO指的是: pixel buffer object,PBO能直接转换为VBO,用作顶点数组的渲染。
优点:
注意:老的GFX显卡不支持多渲染对像。
实现:
方法 1: 在顶点着色其间读取纹理.
步骤 1: 生成 FBO
生成一个FBO,然后把一个用来保存顶点数据的纹理绑定到这个FBO上。
步骤 2: 生成 VBO
生成一个顶点缓冲对像(VBO),用来保保存纹理坐标信息,主要的作用就是为了在顶点着色期间能正确定位并读取到FBO纹理中的数据。
Create a vertex buffer object (VBO) holding texture coordinates for
referencing the FBO texture (e.g. a position VBO with
2 float-coordinates per vertex)
( see further down the text how to create )
步骤 3: 渲染 FBO
( 详细说明请看后面的内容 )
步骤 4: 渲染 VBO
这里,我们先从VBO得到顶点坐标,然后用该坐标来访问FBO纹理,并取得纹理数据。
以下例子是一个顶点程序的代码:
// vertex.position is our // index to the real vertex array!!ARBvp1.0OPTION NV_vertex_program3;PARAM mvp[4] = { state.matrix.mvp };TEMP real_position;TEX real_position, vertex.position, texture[0], 2D;DP4 result.position.x, mvp[0], real_position;DP4 result.position.y, mvp[1], real_position;DP4 result.position.z, mvp[2], real_position;DP4 result.position.w, mvp[3], real_position; END ;
以下列出的是一引些可以被支持的纹理格式。
vertex shader:
Originally Posted by
Nvidia Documentation
顶点纹理的调用会有许多的限制,必须使用GL_TEXTURE_2D的纹理对像,目前只支持GL_LUMINANCE_FLOAT32_ATI 和 GL_RGBA_FLOAT32_ATI 两种数据格式,这两种格式都表示只支持32-bit的浮点数据,前者是单通道,后者是四通道。值得注意的是:如果使用其它的纹理格式,或用了一些不被支持的过滤模式,会造成一些问题,显卡驱动可能会退回到软件模式下进行顶点处理。
以下是一个正确的代码写法。
GLuint vertex_texture; glGenTextures(1, &vertex_texture); glBindTexture(GL_TEXTURE_2D, vertex_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_FLOAT32_ATI, width, height, 0,GL_LUMINANCE, GL_FLOAT, data); |
GLuint vbo_points_handle; glGenBuffersARB(1, &vbo_vertices_handle); glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, vbo_vertices_handle); glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, vbo_points.size()*4* sizeof( float ),NULL, GL_DYNAMIC_DRAW_ARB );
步骤 2: 生成一个 FBO.
多个渲染对像可以帮助我们实现同一时间写入顶点/法线/副法线。以下是一个生成FBO的示例:
GLuint fb_handle; glGenFramebuffersEXT(1,&fb_handle); fbo_tex_vertices = NewFloatTex(tex_width,tex_height,0); fbo_tex_normals = NewFloatTex(tex_width,tex_height,0);
/** * Sets up a floating point texture with NEAREST filtering.* (mipmaps etc. are unsupported for floating point textures)*/void setupTexture ( const GLuint texID, int texSize_w, int texSize_h) {// make active and bindglBindTexture(textureParameters.texTarget,texID);// turn off filtering and wrap modesglTexParameteri(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);// define texture with floating point formatglTexImage2D(textureParameters.texTarget,0,textureParameters.texInternalFormat,texSize_w,texSize_h,0,textureParameters.texFormat,GL_FLOAT,0);// check if that workedif (glGetError() != GL_NO_ERROR){printf("glTexImage2D(): [FAIL] ");// PAUSE();exit (ERROR_TEXTURE);}else if (mode == 0){printf("glTexImage2D(): [PASS] ");}// printf("Created a %i by %i floating point texture. ",texSize,texSize);}
注意:即使我们可以生成RGB的纹理,但它内部格式可能还是RGBA的,用glReadPixels()来读取数据时由于要进行格式转换,可能会减慢运行的速度。因此,多数情况下,我们尽量使用RGBA的格式。
步骤 3: 渲染 FBO
输入纹理中包含有必要的数据(如:顶点位置、法线等),经过运算之后,把数据保存到输出纹理中。
下面代码是绑定FBO缓冲区:
//glBindFramebufferEXT(GL_attach two textures to FBOglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachmentpoints[0],textureParameters.texTarget, outTexID, 0);// check if that workedif (!checkFramebufferStatus()){printf("glFramebufferTexture2DEXT(): [FAIL] ");// PAUSE();exit (ERROR_FBOTEXTURE);}else if (mode == 0){ printf("glFramebufferTexture2DEXT(): [PASS] ");}
// make quad filled to hit every pixel/texel// (should be default but we never know)glPolygonMode(GL_FRONT,GL_FILL);if (textureParameters.texTarget == GL_TEXTURE_2D){// render with normalized texcoordsglBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex2f(0.0, 0.0);glTexCoord2f(1.0, 0.0);glVertex2f(outTexSizeW, 0.0);glTexCoord2f(1.0, 1.0);glVertex2f(outTexSizeW, outTexSizeH);glTexCoord2f(0.0, 1.0);glVertex2f(0.0, outTexSizeH);glEnd();}else{// render with unnormalized texcoordsglBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex2f(0.0, 0.0);glTexCoord2f(outTexSizeW, 0.0);glVertex2f(outTexSizeW, 0.0);glTexCoord2f(outTexSizeW, outTexSizeH);glVertex2f(outTexSizeW, outTexSizeH);glTexCoord2f(0.0, outTexSizeH);glVertex2f(0.0, outTexSizeH);glEnd();}
/*** Creates framebuffer object, binds it to reroute rendering operations* from the traditional framebuffer to the offscreen buffer*/void initFBO(void){// create FBO (off-screen framebuffer)glGetIntegerv(GL_DRAW_BUFFER, &_currentDrawbuf);// Save the current Draw bufferglGenFramebuffersEXT(1, &fb);// bind offscreen framebuffer (that is, skip the window-specific render target)glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);// viewport for 1:1 pixel=texture mappingglMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0.0, outTexSizeW, 0.0, outTexSizeH);glMatrixMode(GL_MODELVIEW); glLoadIdentity();glViewport(0, 0, outTexSizeW, outTexSizeH);}
/***Copy from FBO to PBO**/void copyFromTextureToPBO(GLuint pboID, int texSize_w, int texSize_h){glReadBuffer(attachmentpoints[0]);glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pboID);glReadPixels(0, 0, texSize_w,texSize_h,textureParameters.texFormat,GL_FLOAT, 0);glReadBuffer(GL_NONE);glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0 );}
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo_vertices_handle);glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer ( 4, GL_FLOAT,4* sizeof( float), ( char *) 0);glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo_normals_handle);glEnableClientState(GL_NORMAL_ARRAY);glNormalPointer(GL_FLOAT, 4* sizeof( float), ( char *) 0 );glDrawArrays( GL_TRIANGLES, 0,vbo_vertices.size() );glDisableClientState(GL_NORMAL_ARRAY);glDisableClientState(GL_VERTEX_ARRAY);
示例说明
本文章所带的例子,实现了在GPU中计算B样条曲线的功能,用到的技术有:VBO,FBO,Render to vertex,CG,B-spline.实现过程如图所示:
主要分为三个阶段:
第一阶段:GPU片段着色运算,生成FBO顶点数据。
第二阶段:FBO拷贝到PBO
把插值纹理通过使用glReadPixels()函数,拷贝到PBO中。
第三阶段:渲染VBO
使用glDrawArrays(); 来渲染样条曲线。当然这里我们要把前面生成的PBO数据指定为一个VBO对像。
整个过程的插值运算及数据拷贝,都是在GPU中进行,最终的顶点数据直接用顶点数组来作渲染,数据没有返回到CPU中因此速度会非常快。
结论
声明:
本译文可以自由转载,要求保留原作者信息并注明文章出自物理开发网:www.physdev.com
本文所提供的代码在NV6600的显卡测试通过,如果如果你在其它显卡上测试有问题,可以到物理开发网(www.physdev.com)的GPGPU/CUDA论坛上发表留言进行反溃。
下一步计划将要写一个GUP粒子系统示例,GUP布料系统示例,GPU头发系统示例,敬请关注。
参考
http://oss.sgi.com/projects/ogl-sample/registry/EXT/pixel_buffer_object.txt
http://developer.nvidia.com/object/using_vertex_textures.html
http://wiki.delphigl.com/index.php/GLSL_Partikel
http://www.mathematik.uni-dortmund.de/~goeddeke/gpgpu/tutorial.html#arrays3
http://download.developer.nvidia.com/developer/SDK/Individual_Samples/samples.html
示例代码下载:http://www.physdev.com/phpbb/viewtopic.php?t=144