NeHe OpenGL教程 第二十九课:Blt函数
前言
声明,此 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() 没有变化
原文及其个版本源代码下载:
NeHe OpenGL教程 第二十九课:Blt函数的更多相关文章
- NeHe OpenGL教程 第十九课:粒子系统
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十八课:贝塞尔曲面
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十六课:反射
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十五课:变形
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十四课:扩展
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十二课:凹凸映射
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十课:蒙板
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第十八课:二次几何体
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第十四课:图形字体
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
随机推荐
- ZOJ-3933 Team Formation (二分图最佳完美匹配)
题目大意:n个人,分为两个阵营.现在要组成由若干支队伍,每支队伍由两个人组成并且这两个人必须来自不同的阵营.同时,每个人都有m个厌恶的对象,并且厌恶是相互的.相互厌恶的人不能组成一支队伍.问最多能组成 ...
- IOS请求H5页面、要求自定义agent判断是电脑、安卓还是iPhone登录
//自定制的userAgent- (void)createMyAgent{ NSString *userAgent = [[[UIWebView alloc]init]stringByE ...
- Lua5.1基本函数库介绍
Lua5.1基本函数库介绍assert (v [, message])功能:相当于C的断言,参数:v:当表达式v为nil或false将触发错误,message:发生错误时返回的信息,默认为" ...
- C++泛型编程原理
1.什么是泛型编程前面我们介绍的vector,list,map都是一种数据结构容器,容器本身的存储结构不同,各容器中存在的数据类型也可以不同.但我们在访问这些容器中数据时,拥有相同的方式.这种方式就叫 ...
- XML 命名空间(XML Namespaces)
XML 应用程序 XML CDATA XML 命名空间提供避免元素命名冲突的方法. 命名冲突 在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突. 这个 X ...
- ios 获取屏幕的属性和宽度
app尺寸,去掉状态栏 CGRect r = [ UIScreen mainScreen ].applicationFrame; r=0,20,320,460 屏幕尺寸 CGRect rx = [ U ...
- [转]php和html混编的三种方式
php和html混编的三种方式 以下内容转自:http://blog.i1728.com/post/110.html 原文标题是:<PHP的(<<>,新标题是我加的,文章里的红 ...
- jsoncpp 生成 json 字符串
Json::Value root; Json::Value arrayObj; Json::Value item; for (int i=0; i<10; i++) { item["k ...
- java.sql.SQLException: Io 异常: Connection reset
当数据库连接池中的连接被创建而长时间不使用的情况下,该连接会自动回收并失效,但客户端并不知道,在进行数据库操作时仍然使用的是无效的数据库连接,这样,就导致客户端程序报“ java.sql.SQLExc ...
- SqlServer性能急剧下降,查看所有会话的状态及等待类型---Latch_Ex
当某个数据库文件空间用尽,做自动增长的时候,同一时间点只能有一个用户人员可以做文件自动增长动作,其他任务必须等待,此时会出现Latch资源的等待.使用sp_helpdb查看业务数据库时发现:该数据库设 ...