天天看點

NeHe OpenGL第二十九課:Blt函數

NeHe OpenGL第二十九課:Blt函數

<a href="http://blog.51cto.com/attachment/201008/220813365.png" target="_blank"></a>

<a href="http://blog.51cto.com/attachment/201008/220821391.jpg" target="_blank"></a>

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-&gt;width  = w;        // 設定寬度

  ti-&gt;height = h;        // 設定高度

  ti-&gt;format = f;        // 設定格式

  // 配置設定w*h*f個位元組

  c = (unsigned char *)malloc( w * h * f); 

  if ( c != NULL ) {

   ti-&gt;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-&gt;data)

  {

   free(t-&gt;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-&gt;width * buffer-&gt;format;     // 記錄每一行的寬度,以位元組為機關

 unsigned char *p = NULL;

 f = fopen(filename, "rb");       // 打開檔案

 if( f != NULL )        // 如果檔案存在

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

  for( i = buffer-&gt;height-1; i &gt;= 0 ; i-- )    // 循環所有的行,從最下面以行開始,一行一行的讀取

   p = buffer-&gt;data + (i * stride );

   for ( j = 0; j &lt; buffer-&gt;width ; j++ )   // 讀取每一行的資料

   {

下面的循環讀取每一像素的資料,并把alpha設為255 

    for ( k = 0 ; k &lt; buffer-&gt;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, &amp;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-&gt;width, tex-&gt;height, GL_RGBA, GL_UNSIGNED_BYTE, tex-&gt;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 &gt; 255 ) alpha = 255;

 if( alpha &lt; 0 ) alpha = 0;

 // 判斷是否啟用混合

 if( blend &lt; 0 ) blend = 0;

 if( blend &gt; 1 ) blend = 1;

 d = dst-&gt;data + (dst_ystart * dst-&gt;width * dst-&gt;format);     // 要複制的像素在目标圖像資料中的開始位置 

 s = src-&gt;data + (src_ystart * src-&gt;width * src-&gt;format);   // 要複制的像素在源圖像資料中的開始位置

 for (i = 0 ; i &lt; src_height ; i++ )      // 循環每一行

  s = s + (src_xstart * src-&gt;format);     // 移動到下一個像素

  d = d + (dst_xstart * dst-&gt;format);    

  for (j = 0 ; j &lt; src_width ; j++ )     // 循環複制一行

   for( k = 0 ; k &lt; src-&gt;format ; k++, d++, s++)   // 複制每一個位元組

    if (blend)      // 如果啟用了混合

     *d = ( (*s * alpha) + (*d * (255-alpha)) ) &gt;&gt; 8; // 根據混合複制顔色

    else       

     *d = *s;      // 否則直接複制

  d = d + (dst-&gt;width - (src_width + dst_xstart))*dst-&gt;format;  // 移動到下一行

  s = s + (src-&gt;width - (src_width + src_xstart))*src-&gt;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);

把圖像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);

 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(0.0f, 1.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, 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() 沒有變化 

原文及其個版本源代碼下載下傳:

<a href="http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=29">http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=29</a>

本文轉自 yarin 51CTO部落格,原文連結:http://blog.51cto.com/yarin/381869,如需轉載請自行聯系原作者

繼續閱讀