参考:nehe中文第十课
在第九课基础上继续
------------------------------------------------
数据结构
当您想要使用一系列的数字来完美的表达3D环境时,随着环境复杂度的上升,这个工作的难度也会随之上升。出于这个原因,我们必须将数据归类,使其具有更多的可操作性风格。在程序清单头部出现了sector(区段)的定义。每个3D世界基本上可以看作是sector(区段)的集合。一个sector(区段)可以是一个房间、一个立方体、或者任意一个闭合的区间。
Nehe教程定义了区段,这里我们先省略这一步,数据结构比基本的漫游更复杂,我觉得放在后面更加合适。我们已经有了带纹理的立方体,首先让我们来做漫游吧,这应该很cool。
-----------------------------------------------------------------
显示世界
现在几何模型已经载入内存,我们下一步要在屏幕上显示它。到目前为止,我们所作过的都是些简单的旋转和平移。但我们的镜头始终位于原点(0,0,0)处。任何一个不错的3D引擎都会允许用户在这个世界中游走和遍历,我们的这个也一样。实现这个功能的一种途径是直接移动镜头(gluLookAt)并绘制以镜头为中心的3D环境。这样做会很慢并且不易用代码实现。我们的解决方法如下:
- 根据用户的指令旋转并变换镜头位置。
- 围绕原点(镜头在原点),以与镜头相反的旋转方向来旋转世界。(让人产生镜头旋转的错觉)
- 以与镜头平移方式相反的方式来平移世界(让人产生镜头移动的错觉)。
这样实现起来就很简单.
下面从第一步开始吧(平移并旋转镜头)。
因为用到数学函数,添加
在view.cpp中添加
#include <math.h>
#ifndef PIOVER180
#define PIOVER180 0.0174532925f
#else
#endif
//
//第十课3D漫游
float m_fHeading;
float m_xPos;
float m_zPos;
GLfloat walkbias ;
GLfloat walkbiasangle ;
GLfloat lookupdown f;
//
初始化
m_fHeading=0;
m_xPos=0;
m_zPos=0;
walkbias = 0;
walkbiasangle = 0;
lookupdown = 0.0f;
void COpenglbaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch(nChar)
{
case VK_ESCAPE:
{
//获取主框架窗口指针(app文件openbase.cpp包含了mainfrm.h)
CMainFrame * pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;
//调用主窗口类的自定义函数EndFullScreen,退出全屏显示状态
pFrame->EndFullScreen();
Invalidate(FALSE);
break;
}
//灯光控制
case 'L':
{
m_bLight=!m_bLight;
Invalidate(FALSE);
break;
}
//滤波方式
case 'F':
{
m_nFilter++;
if (m_nFilter>2)
{
m_nFilter=0;
}
Invalidate(FALSE);
break;
}
case VK_PRIOR: // PageUp按下了?
{
m_fZ-=0.02f; // 若按下,将木箱移向屏幕内部
}
Invalidate(FALSE);
break;
//接着四行检查PageDown键是否按下,若是的话,增加z变量的值。
case VK_NEXT: // PageDown按下了么
{
m_fZ+=0.02f; // 若按下的话,将木箱移向观察者
}
Invalidate(FALSE);
break;
//现在检查方向键。
case VK_UP: // Up方向键按下了么?
m_xPos -= (float)sin(m_fHeading*PIOVER180) * 0.05f; // 沿游戏者所在的X平面移动 世界向相反移动
m_zPos -= (float)cos(m_fHeading*PIOVER180) * 0.05f; // 沿游戏者所在的Z平面移动
if (walkbiasangle >= 359.0f) // 如果walkbiasangle大于359度
{
walkbiasangle = 0.0f; // 将 walkbiasangle 设为0
}
else // 否则
{
walkbiasangle+= 10; // 如果 walkbiasangle < 359 ,则增加 10
}
walkbias = (float)sin(walkbiasangle * PIOVER180)/20.0f; // 使游戏者产生跳跃感
Invalidate(FALSE);
break;
case VK_DOWN: // Down方向键按下了么?
m_xPos += (float)sin(m_fHeading*PIOVER180) * 0.05f; // 沿游戏者所在的X平面移动
m_zPos += (float)cos(m_fHeading*PIOVER180) * 0.05f; // 沿游戏者所在的Z平面移动
if (walkbiasangle <= 1.0f) // 如果walkbiasangle小于1度
{
walkbiasangle = 359.0f; // 使 walkbiasangle 等于 359
}
else // 否则
{
walkbiasangle-= 10; // 如果 walkbiasangle > 1 减去 10
}
walkbias = (float)sin(walkbiasangle * PIOVER180)/20.0f; // 使游戏者产生跳跃感
Invalidate(FALSE);
break;
case VK_RIGHT: // Right方向键按下了么?
m_fYRot -= 1.5f; // 镜头向右旋转则可以看做向左旋转场景
Invalidate(FALSE);
break;
case VK_LEFT: // Left方向键按下了么?
m_fYRot += 1.5f; // 眼睛(镜头)向左旋转则可以看做向右旋转场景
Invalidate(FALSE);
break;
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
渲染部分
BOOL COpenglbaseView::RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕及深度缓存
glLoadIdentity();
//
//第10课3D漫游
GLfloat xtrans = -m_xPos; // 用于游戏者沿X轴平移时的大小
GLfloat ztrans = -m_zPos; // 用于游戏者沿Z轴平移时的大小
GLfloat ytrans = -walkbias-0.25f; // 用于头部的上下摆动
GLfloat sceneroty = 360.0f - m_fYRot; // 位于游戏者方向的360度角
glRotatef(lookupdown,1.0f,0,0); // 上下旋转
glRotatef(sceneroty,0,1.0f,0); // 根据游戏者正面所对方向所作的旋转
glTranslatef(xtrans, ytrans, ztrans); // 以游戏者为中心的平移场景
//
glBindTexture(GL_TEXTURE_2D, texture[m_nFilter]); // 选择纹理
glBegin(GL_QUADS);
// 前侧面
glNormal3f( 0.0f, 0.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); // 纹理和四边形的右下
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( 0.0f, 0.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); // 纹理和四边形的左上
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的左下
// 顶面
glNormal3f( 0.0f, 1.0f, 0.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); // 纹理和四边形的右下
glTexCoord2f(1.0f, 1.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( 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();
return TRUE; // 继续运行
}