opengl fbo 紋理 開發步驟

smilestone322發表於2016-05-17

初始化工作;

 

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://www.cnblogs.com/SunnyYue/p/3970262.html

http://blog.csdn.net/huawenguang/article/details/1530547

#include <stdio.h>
#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;
}

你可以在這裡下載到為ATI顯示卡寫的另一個版本。
        --------------CPU----------------      -------------GPU------------         |                               |      |                          |         |   data arr:                   |      |  texture:                |         |    [][][][][][][][][]     --------------> [][][]                |         |                               |      |    [][][]                |         |                               |      |    [][][]                |         |                               |      |           /             |         |   result:                     |      |              FBO:        |         |    [][][][][][][][][]         |      |              [][][]      |         |                              <-----------------     [][][]      |         |                               |      |              [][][]      |         |-------------------------------|      |--------------------------|

以上程式碼是理解GPU程式設計的基礎,如果你完全看得懂,並且能對這程式碼作簡單的修改運用的話,那恭喜你,你已經向成功邁進了一大步,並可以繼續往下看,走向更深入的學習了。但如看不懂,那回頭再看一編吧。


相關文章