OpenGL Pixel Buffer

  一般電腦繪圖的流程大致是這樣:算出投影矩陣、產生模型的幾何資料、設定貼圖、打上光源、最後由硬體產生一張2D平面的影像。而這個2D的影像會放在一塊記憶體中,稱之為Frame buffer。

  Frame Buffer 是一塊直接對應螢幕的記憶體,所以通常是放在顯示卡裡。為了讓連續畫面顯示流暢,我們都會將Frame Buffer設為前景跟背景兩個。先對背景Frame buffer進行繪圖,等確定畫完整個場景後,再將前景跟背景進行對調(Swap)。如此就可把剛才畫完的場景顯示在螢幕上,而我們可以繼續在背景Frame buffer畫下一個場景。

  問題來了,我們能不能把Frame buffer讀出來?因為我們可能會將還沒送出螢幕的影像做一些加工處理。比如將場景影像當成貼圖貼在某個模型,像鏡子這樣的效果。
  早期OpenGL只有提供glReadPixels()這樣的函式可以讀出Frame buffer的資料,但是這會將資料讀在主記憶體中,而我們又要當成貼圖放在顯示記憶體中,這樣一來一往就浪費了不少時間。後來改用glCopyTexImage(), glCopyTexSubImage()直接在顯示記憶體中存取後,速度上就改善了許多。但仍然有使用上的不便性,因為就只是對一塊背景Frame buffer進行存取,換另一張貼圖則整個Frame buffer就必須重畫。

  直到2002年OpenGL 提供了一套解決方法,稱為PBuffer( Pixel Buffer)。這是讓使用者自行建立一塊纇似Frame Buffer的記憶體,差別是PBuffer是不能直接顯示在螢幕上的。我們可以先對PBuffer進行繪圖,然後再將PBuffer當成貼圖來用。這個功能一般的顯示卡都有支援,它是建立在wgl extension中,所以我們在程式開頭要先含入wglext.h檔。要知道您的顯示卡是否支援PBuffer,可以用這個方法來確認:

#include <gl/wglext.h>


 

/*   在此之前要先設定好OpenGL的基本環境 */
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
char *ext = NULL;
if( wglGetExtensionsStringARB ){
    ext = (char*)wglGetExtensionsStringARB( wglGetCurrentDC() );
    if( strstr( ext, "WGL_ARB_pbuffer" ) == NULL ){
    // 不支援 PBuffer!
    }
}

  接下來要介紹如何使用PBuffer。整個使用流程大致是這樣:設定好OpenGL的基本環境、建立PBuffer、啟動PBuffer、對PBuffer繪圖、將PBuffer當成貼圖、啟動原來的Frame Buffer、對Frame Buffer畫圖、解除貼圖的連結、刪除PBuffer。

1. 設定OpenGL的基本環境:請參考我之前的文章 -- "在VC++ .NET framework 使用OpenGL",這邊不多介紹。這個步驟主要是取得HDC及HGLRC。

2. 建立PBuffer:

    a. 取得WGL extension 的函式位置,我們會需要這些函式:wglMakeContextCurrentARB , wglChoosePixelFormatARB , wglCreatePbufferARB , wglDestroyPbufferARB , wglGetPbufferDCARB , wglReleasePbufferDCARB , wglReleasePbufferDCARB , wglQueryPbufferARB , wglBindTexImageARB , wglReleaseTexImageARB , wglSetPbufferAttribARB .

    b. 設定PBuffer每個Pixel的格式 ,用wglChoosePixelFormatARB

    c. 建立PBuffer,用wglCreatePbufferARB

    d. 取得PBuffer的環境DC,用wglGetPbufferDCARB及wglCreateContext

3. 啟動PBuffer:
    wglMakeContextCurrentARB(hPBufDC, hPBufDC, hPBufRC);

4. 對PBuffer繪圖:這個步驟有一點需要特別注意,就是在PBuffer的繪圖環境是與Frame Buffer完全獨立的。也就是說投影矩陣、幾何資訊、光源、貼圖...等,都需要重新設定!

5. 啟動原來的Frame Buffer:
    wglMakeContextCurrentARB(hGLDC, hGLDC, hGLRC);

6. 將PBuffer當成貼圖:注意!這個步驟一定要Frame Buffer的繪圖環境下執行!

    GLuint nPbufTexID;
    glGenTextures(1, &nPbufTexID);
    glBindTexture (GL_TEXTURE_2D, nPbufTexID);
    glTexImage2D( .... );
    glTexParameterf(....);
    wglBindTexImageARB( hPBuf, WGL_FRONT_LEFT_ARB);

7. 對Frame Buffer畫圖:即一般的繪圖流程,要注意的是所有繪圖環境需重新設定。

8. 解除貼圖的連結:
    wglReleaseTexImageARB(hPBuf, WGL_FRONT_LEFT_ARB);

9. 刪除PBuffer:這個步驟是當PBuffer不再使用的情況下才執行。
    wglReleaseTexImageARB(hPBuf, WGL_FRONT_LEFT_ARB);
    glDeleteTextures (1, &nPbufTexID);
    wglDeleteContext(hPBufRC);
    wglReleasePbufferDCARB(hPBuf, hPBufDC);
    wglDestroyPbufferARB(hPBuf);

  對直接在Texture上作畫,PBuffer是目前比較常用的方法,但不是最佳的方法!因為它仍有許多限制,如它只能在視窗化的作業系統上運作(如 Microsoft Windows、XWindow、Mac OS...),以及兩個PBuffer無法直接分享資源等。於是OpenGL又在2003年提供了一個更好的方法 -- Frame Buffer Object(FBO),這也是目前GPGPU(General Purpose GPU, 繪圖晶片泛用運算)所必須的基本功能。我將在下一篇文章為各位介紹。

參考資料:

[1] Christopher Oat, "Rendering to an off-screen buffer with WGL_ARB_pbuffer", ATI Research, Inc.

[2] PBuffer範例程式

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL可以用于创建像素艺术。下面是一个使用OpenGL加载纹理并设置wrap参数的示例代码: ```cpp #include <GL/glut.h> #include <SOIL/SOIL.h> GLuint texture; void init() { glClearColor(0.0,0.0, 0.0, 0.0); glEnable(GL_TEXTURE_2D); glShadeModel(GL_FLAT); // 加载纹理 int picWidth, picHeight; GLubyte *imageData = SOIL_load_image("pixel_art.png", &picWidth, &picHeight, 0, SOIL_LOAD_RGB); // 创建纹理对象 glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 定义纹理图像 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData); SOIL_free_image_data(imageData); } void display() { glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(-0.5, -0.5); glTexCoord2f(1.0, 0.0); glVertex2f(0.5, -0.5); glTexCoord2f(1.0, 1.0); glVertex2f(0.5, 0.5); glTexCoord2f(0.0, 1.0); glVertex2f(-0.5, 0.5); glEnd(); glFlush(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow("Pixel Art"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 这段代码使用了SOIL库来加载纹理图像,并使用OpenGL的纹理对象来渲染图像。在`init()`函数中,我们加载了纹理图像并设置了纹理参数。在`display()`函数中,我们绘制了一个正方形,并将纹理映射到正方形上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值