zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    《活在恩典中》第一章 人类的两难困境
    《真正的修行》把你内心的一切都呈现出来
    Mysql:Plugin:clone=mysql_clone:as of 8.0.17
    Mysql:--init-file && --init-connect
    Mysql:8.0.19:Upgrading Mysql:升级
    Android开发自定义View
    Android控制UI界面
    Android的视图(View)组件
    对Android应用签名
    Android Application的基本组件介绍
  • 原文地址:https://www.cnblogs.com/arxive/p/6239512.html
Copyright © 2011-2022 走看看