用VC6.0与OpenGL实现游戏场景设计

我酷爱软件编程,学了一段时间的OpenGL游戏编程,有了一点点经验,拿来与大家分享,欢迎大家拍砖。

效果图:

主要类有加载3DS模型类:C3DS;雪花粒子效果类:CSnow;加载纹理类:CWenLi以及字体类:CFont;

主要用到了纹理,光照材质,雾,粒子系统,3DS模型设计与加载等技术。

纹理主要代码如下:

 FILE *file=fopen(name,"r");
 if(!file)
 {
  ::MessageBox(NULL,"Open file failed!","Load texture error!",MB_OK);
  return FALSE;
 }
 fclose(file);
 AUX_RGBImageRec* pImage[1];
 memset(pImage,0,sizeof(void*)*1);
 pImage[0]=auxDIBImageLoad(name);
 if(m_First)
 {
  glGenTextures(WENLIMAXNUMBER,&m_pMem[0]);
     m_First=false;
 }
 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
 //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
 //glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
 glBindTexture(GL_TEXTURE_2D,m_pMem[m_Num]);
 glTexImage2D(GL_TEXTURE_2D,
                       0,
        3,
        pImage[0]->sizeX,
        pImage[0]->sizeY,
        0,
        GL_RGB,
        GL_UNSIGNED_BYTE,
        pImage[0]->data);
 free(pImage[0]->data);
 free(pImage[0]);

光照材质雾主要代码如下:

/*******************设置灯光********************/
 float ambient[]={1.0,1.0,1.0};
 float position[]={-20.0,20.0,-50};
 float direction[]={0.0,-0.5,-1.0};
 glLightfv(GL_LIGHT0,GL_SPECULAR,ambient);
 glLightfv(GL_LIGHT0,GL_POSITION,position);
 glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,direction);
 glLightf(GL_LIGHT0,GL_SPOT_EXPONENT,2.0);
 glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,30.0);
 //glLightf(GL_LIGHT0,GL_LINEAR_ATTENUATION,0.5);
 /**********************设置材质*****************/
 float mat_diffuse[]={1.0,0.5,0.5};
 glMaterialfv(GL_LIGHT0,GL_DIFFUSE,mat_diffuse);
 glMaterialf(GL_LIGHT0,GL_SHININESS,10.0);
 /**********************设置雾效果*****************/
 float fog_color[]={0.5,0.5,0.5};
 glFogf(GL_FOG_MODE,GL_LINEAR);
 glFogf(GL_FOG_START,0.0);
 glFogf(GL_FOG_END,300);
 glFogfv(GL_FOG_COLOR,fog_color);

创建好后不要忘记启用它们:

 glEnable(GL_LIGHTING);
 glEnable(GL_LIGHT0);
 glEnable(GL_AUTO_NORMAL);
 glEnable(GL_FOG);

粒子系统使由多个粒子组成的,初始化是为每个例子赋予生命,坐标,速度,加速度等变量,主要代码如下:

typedef struct
{
 float x,y,z;
}Vertex,*pVertex;

typedef struct
{
 Vertex position;
 Vertex speed;
 Vertex a;//acceleration int three directions
 float lifetime;
 float size;
 Vertex color;
 float dec;//the decreasing speed of the particle
 bool alive;
 float alpha;
}Particle,*pParticle;

class CSnow
{
public:
 Particle m_Pat[MAXNUMBER];
public:
 void Display();
 void Update();
 void Init();
 CSnow();
 ~CSnow();
 bool InitSingleParticle(int number);
};

bool CSnow::InitSingleParticle(int number)
{
 m_Pat[number].a.x=m_Pat[number].a.y=m_Pat[number].a.z=0.0;
 m_Pat[number].alive=true;
 m_Pat[number].alpha=1.0;
 m_Pat[number].color.x=m_Pat[number].color.y=m_Pat[number].color.z=1.0;
 m_Pat[number].dec=0.0;
 m_Pat[number].lifetime=100.0;
 m_Pat[number].position.x=GROUND_MAX_WIDTH/2.0-rand()%GROUND_MAX_WIDTH;
 m_Pat[number].position.y=300;
 m_Pat[number].position.z=-50.0-rand()%(GROUND_MAX_LENGTH-50);
 m_Pat[number].size=0.4;
 m_Pat[number].speed.x=0.5-rand()%10/10;
 m_Pat[number].speed.z=0.5-rand()%10/10;
 m_Pat[number].speed.y=-1*rand()%4-2.0;
 return true;
}

3DS模型加载比较复杂,需要先自己用3DsMax制作房子,树等模型并贴好贴图,然后在用递归读取的方法将3DS文件中模型顶点,纹理等信息读入到程序中,在用OpenGL渲染管线(Pipline)渲染出来,其递归代码如下:

void CLoad3DS::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk)
{ t3DObject newObject = {0};     // 用来添加到对象链表
 tMatInfo newTexture = {0};    // 用来添加到材质链表
 unsigned int version = 0;     // 保存文件版本
 int buffer[50000] = {0};     // 用来跳过不需要的数据
 m_CurrentChunk = new tChunk;    // 为新的块分配空间  
 //  下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行
 //  如果是不需要读入的块,则略过
 // 继续读入子块,直到达到预定的长度
 while (pPreChunk->bytesRead < pPreChunk->length)
 { // 读入下一个块
  ReadChunk(m_CurrentChunk);
  // 判断块的ID号
  switch (m_CurrentChunk->ID)
  {
  case VERSION:       // 文件版本号
   // 在该块中有一个无符号短整型数保存了文件的版本
   // 读入文件的版本号,并将字节数添加到bytesRead变量中
   m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
   // 如果文件版本号大于3,给出一个警告信息
   if (version > 0x03)
    MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
   break;
  case OBJECTINFO:      // 网格版本信息
   // 读入下一个块
   ReadChunk(m_TempChunk);
   // 获得网格的版本号
   m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
   // 增加读入的字节数
   m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
   // 进入下一个块
   ReadNextChunk(pModel, m_CurrentChunk);
   break;
  case MATERIAL:       // 材质信息
   // 材质的数目递增
   pModel->numOfMaterials++;
   // 在纹理链表中添加一个空白纹理结构
   pModel->pMaterials.push_back(newTexture);
   // 进入材质装入函数
   ReadNextMatChunk(pModel, m_CurrentChunk);
   break;
  case OBJECT:       // 对象的名称
   // 该块是对象信息块的头部,保存了对象了名称
   // 对象数递增
   pModel->numOfObjects++;
   // 添加一个新的tObject节点到对象链表中
   pModel->pObject.push_back(newObject);
   // 初始化对象和它的所有数据成员
   memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
   // 获得并保存对象的名称,然后增加读入的字节数
   m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
   // 进入其余的对象信息的读入
   ReadNextObjChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
   break;
  case EDITKEYFRAME:
   // 跳过关键帧块的读入,增加需要读入的字节数
   m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
   break;
  default:
   //  跳过所有忽略的块的内容的读入,增加需要读入的字节数
   m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
   break;
  }
  // 增加从最后块读入的字节数
  pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
 }
 // 释放当前块的内存空间
 delete m_CurrentChunk;
 m_CurrentChunk = pPreChunk;
}

有了以上知识基础后就可以开发游戏场景程序了^_^,当然,制作一个完整的游戏还有许多技术要用到,比如模型骨骼动画,碰撞检测,视觉漫游,图像读取等高级技术,大家一起努力学习吧。

你可能感兴趣的:(用VC6.0与OpenGL实现游戏场景设计)