转自【翻译】NeHe OpenGL 教程

前言

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

NeHe OpenGL第三十八课:资源文件

从资源文件中载入图像:

如何把图像数据保存到*.exe程序中,使用Windows的资源文件吧,它既简单又实用。

 

欢迎来到NeHe教程第38课。离上节课的写作已经有些时日了,加上写了一整天的code,也许笔头已经开始生锈了 :)

现在你已经学会了如何做方格贴图,如何读入bitmap及各种光栅图像...那么如何做三角形贴图,又如何在.exe文件中体现你的纹理呢?

我每每被问及这两个问题,可是一旦你看到他们是多么简单,你就会大骂自己居然没有想到过 :)

我不会事无巨细地解释每一个细节,只需给你一些抓图,就明白了。我将基于最新的code,请在主页"NeHeGL I Basecode"下或者这张网页最下面下载。

首先,我们把图像加载入资源文件。我向大家已经知道怎么做了,只是,你忽略了几步,于是值得到一些无用的资源文件。里面有bitmap文件,却无法使用。

还记得吧?我们使用Visual C++ 6.0 做的。如果你使用其它工具,这页教材关于资源的部分(尤其是那些图)完全不适用。

* 暂时你只能用24bit BMP 图像。如果读8bit BMP文件要写很多额外的code。我很希望听到你们谁有更小的/更好的loader。我这里的读入8bit 和 24bit BMP 的code实在臃肿。用LoadImage就可以。

打开文件,点击“插入”菜单,选“资源”



然后选择你要插入的资源类型BITMAP文件,单击"插入"



然后是文件窗口,进入DATA目录,选中三个图形文件(用Ctrl啦)然后点“读入”。注意文件类型是否正确。



接下来会弹出三次警告(一个文件一次),说读入正确,但该文件不能被浏览或编辑,因为它有多于256种颜色。没什么的!



一旦所有图形都调入,将会出现一个列表。每个图分配有一个ID,每个ID都是IDB_BITMAP打头的,然后数字1-3。你要是懒得改,就不用管它了。不过我们还都比较勤快!



右健单击每个ID,选"属性",然后重命名,使之与文件名匹配。就像我图片上那样。



接下来,选“文件--〉全部保存”。你刚刚创建一个新的资源文件,所以Windows会问你取什么名字。你随便拉,也可以叫"lesson38.rc" , 然后保存。

到此为止,你有了一个资源文件,里面全是保存在硬盘上的Bitmap 图形文件,要使用这些文件,你还需要完成一系列步骤。



接下来该把资源文件加到你自己的项目里面了。选“项目--〉添加到项目--〉文件”



选择resorce.h文件和资源文件Lesson38.rc(用Ctrl)



最后确认资源文件Lesson38.rc放入RESOURCE FILES文件夹。就像上面图片里那样,点击并拖入RESOURCE FILES文件夹就好了。

移动之后选“文件--〉全部保存”,然后文件部分就好了。好多的图阿:)

然后我们开始code的部分。下面一段最重要的一行是#include

"resource.h".没有这行,编译的时候就会有无数未定义变量的错误。resource.h文件定义了资源文件里的对象。所以要从IDB_BUTTERFLY1里面读取数据的话,最好include这个头文件
!

#include <windows.h>   

#include <gl\gl.h>          

#include <gl\glu.h>          

#include <gl\glaux.h>          

#include "NeHeGL.h"         

#include "resource.h"          // 资源文件的头文件

#pragma comment( lib, "opengl32.lib" )        

#pragma comment( lib, "glu32.lib" )        

#pragma comment( lib, "glaux.lib" )

GL_Window* g_window;

Keys*  g_keys;

下面一段第一行分配三个纹理所需空间,接下来的结构体用于保存关于约50个在屏幕上运动的物体的信息。

tex将跟踪每个物体所用纹理,x是物体的x坐标,y是y坐标,z,z坐标,yi是一个随机数用来控制物体下落速度,

spinz用来控制沿z轴的旋转,spinzi是另一个随机树,记录旋转速度。flap用来控制物体的翅膀(一会在解释这个)随机数fi控制翅膀拍打的速度。

 

// 定义三个保存纹理变量的ID

GLuint texture[3];           // 保存三个纹理

struct object           // 定义一个物体

{

 int   tex;          // 纹理值

 float x;           // 位置

 float y;          

 float z;          

 float yi;           // 速度

 float spinz;          // 沿Z轴旋转的角度和速度

 float spinzi;          

 float flap;          // 是否翻转三角形

 float fi;          

};

object obj[50];           // 创建50个物体

下面一段代码是物体obj[loop]的初始化,loop从0到49(表示50个物体中的一个)。首先是随机纹理从0到2表示

一个随机着色的蝴蝶。x坐标随机的取-17.0f到+17.0f之间的值,y取18.0f,也就是从屏幕的上面一点点开始,

这样一开始时看不到物体的。z也是-10.0f到-40.f之间的随机数,spinzi取-1.0f到1.0f之间。flap取翅膀中心

位置,为0.0f。最后拍打速度fi和下落速度yi也是随机的。

 

void SetObject(int loop)         // 循环设置50个物体

{

 obj[loop].tex=rand()%3;        // 纹理

 obj[loop].x=rand()%34-17.0f;       // 位置

 obj[loop].y=18.0f;         

 obj[loop].z=-((rand()%30000/1000.0f)+10.0f);      

 obj[loop].spinzi=(rand()%10000)/5000.0f-1.0f;      // 旋转

 obj[loop].flap=0.0f;         

 obj[loop].fi=0.05f+(rand()%100)/1000.0f;      

 obj[loop].yi=0.001f+(rand()%1000)/10000.0f;      

}

这回该到了最有意思的地方了。从资源文件中读入bitmap,转为纹理。hBMP是指向这个bitmap文件的指针,

它将告诉我们的程序从哪里读取数据。BMP是一个bitmap结构体,我们把从资源文件中读取的数据保存在里面。

第三行是告诉我们的程序我们将使用哪些ID:IDB_BUTTERFLY1,IDB_BUTTERFLY2,IDB_BUTTERFLY3。要用更多

的图像的话,只需增加资源文件中的图像,并在Texture[]中增加新的ID。

 

void LoadGLTextures()          // 资源文件中读入bitmap,转为纹理

{

 HBITMAP hBMP;          // 位图句柄

 BITMAP BMP;          // 位图结构

// 纹理句柄

 byte Texture[]={ IDB_BUTTERFLY1, IDB_BUTTERFLY2, IDB_BUTTERFLY3 };

下面一行使用sizeof(Texture)来计算要创建多少个纹理。我们有3个ID,也就是3个纹理。

glGenTextures(sizeof(Texture), &texture[0]);       // 创建三个纹理

 for (int loop=0; loop<sizeof(Texture); loop++)      // 循环载入所有的位图

 {

LoadImage需要如下参数:GetModuleHandle(NULL)-指向实例的句柄,MAKEINTRESOURCE(Texture[loop])-把Texture[loop]从整型转为一个资

源值,也就是要读的图形文件。IMAGE_BITMAP-告诉我们要读的是一个bitmap文件。

接下来两个参数(0,0)是读入图像的高度和宽度像素数,使用默认大小就设为0。

最后一个参数(LR_CREATEDIBSECTION)返回DIB section bitmap??这是一个没有保存颜色信息的bitmap。也正是我们需要的。

hBMP 指向从LoadImage()读入的bitmap数据。

hBMP=(HBITMAP)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(Texture[loop]), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);

检查指针hBMP是否有效,即指向有用数据。如果没有指向任何数据,也可以弹出错误提示。

如果数据存在,用GetObject()从hBMP取得数据(sizeof(BMP))并存储在BMP中。

glPixelStorei告诉OpenGL这些数据是以word alignments存储的,也就是每像素4字节。

绑定纹理,设置滤波方式为GL_LINEAR_MIPMAP_LINEAR(又好又光滑),然后生成纹理。

注意到我们使用BMP.bmWidth和BMP.bmHeight获取图像的高度和宽度。并用GL_BGR_EXT交换红蓝,实际使用的资源数据是从BMP.bmBits中取得的



最后删除bitmap对象,释放所有与之相联系的系统资源空间。

if (hBMP)         // 位图是否存在

  {         // 存在

   GetObject(hBMP,sizeof(BMP), &BMP);     // 获得位图

   glPixelStorei(GL_UNPACK_ALIGNMENT,4);    // 以四字节方式对其内存

   glBindTexture(GL_TEXTURE_2D, texture[loop]);    // 绑定位图

   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);  // 设置纹理过滤器

   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

  // 创建纹理

   gluBuild2DMipmaps(GL_TEXTURE_2D, 3, BMP.bmWidth, BMP.bmHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits);

   DeleteObject(hBMP);      // 删除位图对象

  }

 }

}

init code没有什么新鲜的,只是增加了LoadGLTextures()调用上面的code。清屏的颜色是黑色,不进行深度检测,这样比较快。启用纹理映

射和混色效果。

 

BOOL Initialize (GL_Window* window, Keys* keys)       // 初始化

{

 g_window = window;

 g_keys  = keys;

LoadGLTextures();          //载入纹理

glClearColor (0.0f, 0.0f, 0.0f, 0.5f);       // 设置背景

 glClearDepth (1.0f);         

 glDepthFunc (GL_LEQUAL);        

 glDisable(GL_DEPTH_TEST);         // 启用深度测试

 glShadeModel (GL_SMOOTH);        

 glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    

 glEnable(GL_TEXTURE_2D);         // 启用2D纹理

 glBlendFunc(GL_ONE,GL_SRC_ALPHA);        // 使用混合

 glEnable(GL_BLEND);         

  

初始化所有的物体

for (int loop=0; loop<50; loop++)

 {

  SetObject(loop);       

 }

return TRUE;          // 成功返回

}

void Deinitialize (void)         

{

}

void Update (DWORD milliseconds)         // 更新,执行动画

{

 if (g_keys->keyDown [VK_ESCAPE] == TRUE)       // 按ESC退出

 {

  TerminateApplication (g_window);      

 }

if (g_keys->keyDown [VK_F1] == TRUE)        // 按F1切换显示模式

 {

  ToggleFullscreen (g_window);       

 }

}

接下来看看绘制代码。在这部分我将讲解如何用尽可能简单的方式将一个图像映到两个三角形上。有些人认为有理由相信,一个图像到三角形

上的单一映射是不可能的。

实际上,你可以轻而易举地将图像映到任何形状的区域内。使得图像与边界匹配或者完全不考虑形式。根本没关系的。(译者:我想作者的意

思是,从长方形到三角形的解析影射是不存在的,但不考虑那么多的话,任意形状之间的连续影射总是可以存在的。他说的使纹理与边界匹配

,大概是指某一种参数化的方法,简单地说使得扭曲最小。)

首先清屏,循环润色50个蝴蝶对象。

 

void Draw (void)          // 绘制场景

{

 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for (int loop=0; loop<50; loop++)       

 {

调用glLoadIdentify()重置投影矩阵,然后选择对象的纹理。用glTranslatef()为蝴蝶定位,然后沿x轴旋转45度。使之向观众微略倾斜,这

样比较有立体感。最后沿z轴旋转,蝴蝶就旋转下落了。

glLoadIdentity ();        // 重置矩阵

  glBindTexture(GL_TEXTURE_2D, texture[obj[loop].tex]);    // 绑定纹理

  glTranslatef(obj[loop].x,obj[loop].y,obj[loop].z);    // 绘制物体

  glRotatef(45.0f,1.0f,0.0f,0.0f);      

  glRotatef((obj[loop].spinz),0.0f,0.0f,1.0f);     

  

其实到三角形上的映射和到方形上并没有很大区别。只是你只有三个定点,要小心一点。

下面的code中,我们将会值第一个三角形。从一个设想的方形的右上角开始,到左上角,再到左下角。润色的结果像下面这样:



注意半个蝴蝶出现了。另外半个出现在第二个三角形里。同样地将三个纹理坐标与顶点坐标非别对应,这给出充分的信息定义一个三角形上的映射。 

 glBegin(GL_TRIANGLES);

glTexCoord2f(1.0f,1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);   

   glTexCoord2f(0.0f,1.0f); glVertex3f(-1.0f, 1.0f, obj[loop].flap); 

   glTexCoord2f(0.0f,0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);   

  

下面的code润色另一半。同上,只是我们的三角变成了从右上到左下,再到右下。



第一个三角形的第二点和第二个三角形的第三点(也就是翅膀的尖端)在z方向往复运动(即z=-1.0f和1.0f之间),两个三角形沿着蝴蝶的身

体折叠起来,产生拍打的效果,简易可行。

glTexCoord2f(1.0f,1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);   

   glTexCoord2f(0.0f,0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);   

   glTexCoord2f(1.0f,0.0f); glVertex3f( 1.0f,-1.0f, obj[loop].flap);

glEnd();

下面一段通过从obj[loop].y中递减obj[loop].yi使蝴蝶自上而下运动。spinz值递增spinzi(可正可负)flap递增fi.fi的正负取决于翅膀向

上还是向下运动。

//移动,选择图像

  obj[loop].y-=obj[loop].yi;       

  obj[loop].spinz+=obj[loop].spinzi;      

  obj[loop].flap+=obj[loop].fi;

当蝴蝶向下运行时,需要检查是否越出屏幕,如果是,就调用SetObject(loop)来给蝴蝶赋新的纹理,新下落速度等。

if (obj[loop].y<-18.0f)        //判断是否超出了屏幕,如果是重置它

  {

   SetObject(loop);       

  }

翅膀拍打的时候,还要检查flap是否小于-1.0f或大于1.0f,如果是,令fi=-fi,以改变运动方向。Sleep(15)是用来减缓运行速度,每帧15毫

秒。在我朋友的机器上,这让蝴蝶疯狂的飞舞。不过我懒得改了:)

if ((obj[loop].flap>1.0f) || (obj[loop].flap<-1.0f))    

  {

   obj[loop].fi=-obj[loop].fi;

  }

 }

Sleep(15);

glFlush ();         

}

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

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

 
 

NeHe OpenGL教程 第三十八课:资源文件的更多相关文章

  1. NeHe OpenGL教程 第四十八课:轨迹球

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第三十九课:物理模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第三十六课:从渲染到纹理

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第三十五课:播放AVI

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. NeHe OpenGL教程 第三十四课:地形

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. NeHe OpenGL教程 第三十二课:拾取游戏

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. NeHe OpenGL教程 第三十课:碰撞检测

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. NeHe OpenGL教程 第四十二课:多重视口

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

随机推荐

  1. 论文笔记之:Learning Multi-Domain Convolutional Neural Networks for Visual Tracking

    Learning Multi-Domain Convolutional Neural Networks for Visual Tracking CVPR 2016 本文提出了一种新的CNN 框架来处理 ...

  2. Softmax回归(使用theano)

    # coding:utf8 import numpy as np import cPickle import theano import os import theano.tensor as T cl ...

  3. IIS管理器的快捷方式在哪里?

    两种重新创建IIS快捷方式的方法,希望对大家有所帮助 1.首先需要明白它本来就是个快捷方式,所以可以重新创建一个新的快捷方式:右击桌面>>新建>>快捷方式.弹出创建快捷方式向导 ...

  4. php 开启COM组件

    1.先到PHP.INI中打开COM选项,com.allow_dcom = true 2.我这里的环境是PHP5.4.7,PHP 5.4.5后,com/dotnet 模块已经成了单独的扩展,所以需要在P ...

  5. 1kkk

    代码: # !usr/bin/python3.4 # -*- coding:utf-8 -*- import requests import os import time import re from ...

  6. get跟post编码--转

    1.Get是用来从服务器上获得数据(没有请求体),而Post是用来向服务器上传递数据(包含请求体). 2.Get将表单中数据的按照variable=value的形式,添加到action(服务)所指向的 ...

  7. 简述oracle视图

    1.视图的概述 视图其实就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关数据.视图将一个查询的结果作为一个表来使用,因此视图可以被看作是存储的查询或一个虚拟表.视图来源于表,所有对视图数 ...

  8. 本地计算机上的OracleOraDb11g_home1TNSListener服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。——Oracle监听器服务无法启动!

    问题: oracle服务设置为手动启动.但是开机后手动启动监听服务后弹出框,提示“本地计算机上的OracleOraDb11g_home1TNSListener服务启动后停止.某些服务在未由其他服务或程 ...

  9. deb、rpm、tar.gz三种Linux软件包的区别

    初接解LINUX的,同样都是for linux,但rpm.tar.gz.deb包还是有很大区别的, 这种区别可使安装过程进行不下去.那我们应该下载什么格式的包呢? rpm包-在红帽LINUX.SUSE ...

  10. Presto集群安装配置

    Presto是一个运行在多台服务器上的分布式系统. 完整安装包括一个coordinator(调度节点)和多个worker. 由客户端提交查询,从Presto命令行CLI提交到coordinator. ...