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->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);
把圖像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,如需轉載請自行聯系原作者