参考: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; // 继续运行 }