NeHe OpenGL教程 (二十九)

來源:互聯網
上載者:User

第29課
  
  Blitter 函數:

類似於DirectDraw的blit函數,過時的技術,我們有實現了它。它非常的簡單,就是把一塊紋理貼到另一塊紋理上。
  
 這篇文章是有Andreas Lffler所寫的,它寫了一份原始的教程。過了幾天,Rob Fletcher發了封郵件給我,他重新改寫了所有的代碼,我在它的基礎上把glut的架構變換為Win32的架構。
現在讓我們開始吧!
 
 下面是一個儲存映像資料的結構 
  
typedef struct Texture_Image
{
    int width;                                    // 寬
    int height;                                    // 高
    int format;                                    // 像素格式
    unsigned char *data;                                // 紋理資料
} TEXTURE_IMAGE;

 接下來定義了兩個指向這個結構的指標 
  
typedef TEXTURE_IMAGE *P_TEXTURE_IMAGE;                           

P_TEXTURE_IMAGE t1;                                    // 指向儲存映像結構的指標
P_TEXTURE_IMAGE t2;                                    // 指向儲存映像結構的指標

 下面的函數為w*h的映像分配記憶體 
  
P_TEXTURE_IMAGE AllocateTextureBuffer( GLint w, GLint h, GLint f)
{
    P_TEXTURE_IMAGE ti=NULL;                           
    unsigned char *c=NULL;                               
    ti = (P_TEXTURE_IMAGE)malloc(sizeof(TEXTURE_IMAGE));                    // 分配映像結構記憶體

    if( ti != NULL ) {
        ti->width  = w;                                // 設定寬度
        ti->height = h;                                // 設定高度
        ti->format = f;                                // 設定格式
        // 分配w*h*f個位元組
        c = (unsigned char *)malloc( w * h * f);
        if ( c != NULL ) {
            ti->data = c;
        }
        else {
            MessageBox(NULL,"記憶體不足","分配映像記憶體錯誤",MB_OK | MB_ICONINFORMATION);
            return NULL;
        }
    }

    else
    {
        MessageBox(NULL,"記憶體不足","分配映像結構記憶體錯誤",MB_OK | MB_ICONINFORMATION);
        return NULL;
    }
    return ti;                                    // 返回指向映像資料的指標
}

 下面的函數釋放分配的記憶體 
  
// 釋放映像記憶體
void DeallocateTexture( P_TEXTURE_IMAGE t )
{
    if(t)
    {
        if(t->data)
        {
            free(t->data);                            // 釋放映像記憶體
        }

        free(t);                                    // 釋放映像結構記憶體
    }
}

 下面我們來讀取*.raw的檔案,這個函數有兩個參數,一個為檔案名稱,另一個為儲存檔案的映像結構指標。 
  
// 讀取*.RAW檔案,並把影像檔上下翻轉一符合OpenGL的使用格式。
int ReadTextureData ( char *filename, P_TEXTURE_IMAGE buffer)
{
    FILE *f;
    int i,j,k,done=0;
    int stride = buffer->width * buffer->format;                    // 記錄每一行的寬度,以位元組為單位
    unsigned char *p = NULL;

    f = fopen(filename, "rb");                            // 開啟檔案
    if( f != NULL )                                // 如果檔案存在
    {

  
 如果檔案存在,我們通過一個迴圈讀取我們的紋理,我們從映像的最下面一行,一行一行的讀取映像。 
  

        for( i = buffer->height-1; i >= 0 ; i-- )                // 迴圈所有的行,從最下面以行開始,一行一行的讀取
        {
            p = buffer->data + (i * stride );
            for ( j = 0; j < buffer->width ; j++ )            // 讀取每一行的資料
            {

 下面的迴圈讀取每一像素的資料,並把alpha設為255 
  
                for ( k = 0 ; k < buffer->format-1 ; k++, p++, done++ )
                {
                    *p = fgetc(f);                    // 讀取一個位元組
                }
                *p = 255; p++;                        // 把255儲存在alpha通道中
            }
        }
        fclose(f);                                // 關閉檔案
    }
  
 如果出現錯誤,彈出一個提示框 
  
    else                       
    {
        MessageBox(NULL,"不能開啟檔案","映像錯誤",MB_OK | MB_ICONINFORMATION);
    }
    return done;                                    // 返回讀取的位元組數
}

 下面的代碼建立一個2D紋理,和前面課程介紹的方法相同 
  
void BuildTexture (P_TEXTURE_IMAGE tex)
{
    glGenTextures(1, &texture[0]);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex->width, tex->height, GL_RGBA, GL_UNSIGNED_BYTE, tex->data);
}

 現在到了blitter函數的地方了,他運行你把一個映像的任意部分複製到另一個映像的任意部分,並混合。
src為原映像
dst為靶心圖表像
src_xstart,src_ystart為要複製的部分在原映像中的位置
src_width,src_height為要複製的部分的寬度和高度
dst_xstart,dst_ystart為複製到靶心圖表像時的起始位置
上面的意思是把原映像中的(src_xstart,src_ystart)-(src_width,src_height)複製到靶心圖表像中(dst_xstart,dst_ystart)-(src_width,src_height)
blend設定是否啟用混合,0為不啟用,1為啟用
alpha設定源映像中顏色在混合時所佔的百分比  
  

void Blit( P_TEXTURE_IMAGE src, P_TEXTURE_IMAGE dst, int src_xstart, int src_ystart, int src_width, int src_height,
       int dst_xstart, int dst_ystart, int blend, int alpha)
{
    int i,j,k;
    unsigned char *s, *d;                               

    // 掐斷alpha的值
    if( alpha > 255 ) alpha = 255;
    if( alpha < 0 ) alpha = 0;

    // 判斷是否啟用混合
    if( blend < 0 ) blend = 0;
    if( blend > 1 ) blend = 1;

    d = dst->data + (dst_ystart * dst->width * dst->format);              // 要複製的像素在靶心圖表像資料中的開始位置
    s = src->data + (src_ystart * src->width * src->format);            // 要複製的像素在源映像資料中的開始位置

    for (i = 0 ; i < src_height ; i++ )                        // 迴圈每一行
    {

        s = s + (src_xstart * src->format);                    // 移動到下一個像素
        d = d + (dst_xstart * dst->format);               
        for (j = 0 ; j < src_width ; j++ )                    // 迴圈複製一行
        {

            for( k = 0 ; k < src->format ; k++, d++, s++)            // 複製每一個位元組
            {
                if (blend)                        // 如果啟用了混合
                    *d = ( (*s * alpha) + (*d * (255-alpha)) ) >> 8;    // 根據混合複製顏色
                else                           
                    *d = *s;                        // 否則直接複製
            }
        }
        d = d + (dst->width - (src_width + dst_xstart))*dst->format;        // 移動到下一行
        s = s + (src->width - (src_width + src_xstart))*src->format;       
    }
}

 初始化代碼基本不變,我們使用新的函數,載入*.raw紋理。並把紋理t2的一部分blit到t1中混合,接著按常規的方法設定2D紋理。 
  

int InitGL(GLvoid)
{
    t1 = AllocateTextureBuffer( 256, 256, 4 );                        // 為映像t1分配記憶體
    if (ReadTextureData("Data/Monitor.raw",t1)==0)                    // 讀取映像資料
    {                                        // 失敗則彈出對話方塊
        MessageBox(NULL,"不能讀取 'Monitor.raw' 檔案","讀取錯誤",MB_OK | MB_ICONINFORMATION);
        return FALSE;
    }

    t2 = AllocateTextureBuffer( 256, 256, 4 );                        // 為映像t2分配記憶體
    if (ReadTextureData("Data/GL.raw",t2)==0)                        // 讀取映像資料
    {                                        // 失敗則彈出對話方塊
        MessageBox(NULL,"不能讀取 'GL.raw' 檔案","讀取錯誤 ",MB_OK | MB_ICONINFORMATION);
        return FALSE;
    }

 把映像t2的(127,127)-(256,256)部分和映像t1的(64,64,196,196)部分混合 
  
    // 把映像t2的(127,127)-(256,256)部分和映像t1的(64,64,196,196)部分混合
    Blit(t2,t1,127,127,128,128,64,64,1,127);                   

 下面的代碼和前面一樣,釋放分配的空間,建立紋理 
  

    BuildTexture (t1);                                // 把t1映像載入為紋理

    DeallocateTexture( t1 );                            // 釋放映像資料
    DeallocateTexture( t2 );                       

    glEnable(GL_TEXTURE_2D);                            // 使用2D紋理

    glShadeModel(GL_SMOOTH);                            // 使用光滑著色
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 設定背景色為黑色
    glClearDepth(1.0);                                // 設定深度緩衝清楚值為1
    glEnable(GL_DEPTH_TEST);                            // 使用深度緩衝
    glDepthFunc(GL_LESS);                            // 設定深度測試函數

    return TRUE;
}

 下面的代碼繪製一個盒子 
  
GLvoid DrawGLScene(GLvoid)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                // 清楚顏色緩衝和深度緩衝
    glLoadIdentity();                           
    glTranslatef(0.0f,0.0f,-5.0f);

    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);
    glRotatef(zrot,0.0f,0.0f,1.0f);

    glBindTexture(GL_TEXTURE_2D, texture[0]);

    glBegin(GL_QUADS);
        // 前面
        glNormal3f( 0.0f, 0.0f, 1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        // 後面
        glNormal3f( 0.0f, 0.0f,-1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        // 上面
        glNormal3f( 0.0f, 1.0f, 0.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        // 下面
        glNormal3f( 0.0f,-1.0f, 0.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        // 右面
        glNormal3f( 1.0f, 0.0f, 0.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        // 左面
        glNormal3f(-1.0f, 0.0f, 0.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
    glEnd();

    xrot+=0.3f;
    yrot+=0.2f;
    zrot+=0.4f;
    return TRUE; // 一切 OK
}

 KillGLWindow() 函數沒有變化 
  
 CreateGLWindow函數沒有變化 
  
 WinMain() 沒有變化 
  
 好了,現你可以很輕鬆的繪製很多混合效果。如果你有什麼好的建議,請告訴我。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.