NeHe OpenGL教程 第二十九课:Blt函数

转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

NeHe OpenGL第二十九课:Blt函数

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() 没有变化

原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=29

时间: 2024-10-18 10:37:25

NeHe OpenGL教程 第二十九课:Blt函数的相关文章

NeHe OpenGL教程 第二十五课:变形

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第二十五课:变形 变形和从文件中加载3D物体: 在这一课中,你将学会如何从文件加载3D模型,并且平滑的从一个模型变换为另一个模型. 欢迎来到这激动人心的一课,在这一课里,我们将介绍模型的变形.需要注意的是各个模型必须要有相同的顶点,

NeHe OpenGL教程 第二十八课:贝塞尔曲面

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第二十八课:贝塞尔曲面 贝塞尔曲面: 这是一课关于数学运算的,没有别的内容了.来,有信心就看看它吧. 贝塞尔曲面 作者: David Nikdel ( [email protected] ) 这篇教程旨在介绍贝塞尔曲面,希望有比我更

NeHe OpenGL教程 第二十二课:凹凸映射

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第二十二课:凹凸映射 凹凸映射,多重纹理扩展: 这是一课高级教程,请确信你对基本知识已经非常了解了.这一课是基于第六课的代码的,它将建立一个非常酷的立体纹理效果. 这一课由Jens Schneider所写,它基本上是由第6课改写而来

NeHe OpenGL教程 第十八课:二次几何体

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第十八课:二次几何体 二次几何体: 利用二次几何体,你可以很容易的创建球,圆盘,圆柱和圆锥. 二次曲面是一种画复合对象的方法,这种方法通常并不需要很多的三角形.我们将要使用第七课的代码.我们将要增加7个变量以及修改纹理以增加一些变化

NeHe OpenGL教程 第十四课:图形字体

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第十四课:图形字体 图形字体: 在一课我们将教你绘制3D的图形字体,它们可像一般的3D模型一样被变换. 这节课继续上一节课课的内容.在第13课我们学习了如何使用位图字体,这节课,我们将学习如何使用轮廓字体. 创建轮廓字体的方法类似于

NeHe OpenGL教程 第十二课:显示列表

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第十二课:显示列表 显示列表: 想知道如何加速你的OpenGL程序么?这一课将告诉你如何使用OpenGL的显示列表,它通过预编译OpenGL命令来加速你的程序,并可以为你省去很多重复的代码. 这次我将教你如何使用显示列表,显示列表将

NeHe OpenGL教程 第十课:3D世界

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第十课:3D世界 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由Lionel Brits (βtelgeuse)所写的

NeHe OpenGL教程 第二十三课:球面映射

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第二十三课:球面映射 球面映射: 这一个将教会你如何把环境纹理包裹在你的3D模型上,让它看起来象反射了周围的场景一样. 球体环境映射是一个创建快速金属反射效果的方法,但它并不像真实世界里那么精确!我们从18课的代码开始来创建这个教程

NeHe OpenGL教程 第三十七课:卡通映射

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第三十七课:卡通映射 卡通映射: 什么是卡通了,一个轮廓加上少量的几种颜色.使用一维纹理映射,你也可以实现这种效果. 看到人们仍然e-mail我请求在文章中使用我方才在GameDev.net上写的源代码,还看到文章的第二版(在那每一