我们一起写opengl程序

我们知道,现在的3D引擎有OPENGL(Openning Graphics Language,开放性图形语言,opengl是状态机)和微软公司的D3D(Directed 3 Dimentional),D3D虽然是后起之秀,而且在windows操作系统上具有良好的工作效率,即使如此,我个人认为D3D永远无法取代opengl在业界的地位,毕竟opengl是3D的标准,因为它是跨平台的(不管是windows操作系统,或者Linux,UINX,Android....都能很好的运行,),高效率的,虽然D3D在某些方面做得比opengl要好,但我个人认为,没有opengl,就没有D3D,微软是实实在在的剽窃者(包括微软的MS SQL SERVER都是剽窃来的),尽管现在opengl一直世风日下,DX的出现更使其应用量大大减少,但是opengl毕竟是3D引擎的标准,是永远不会被替代的重要角色,在某些方面还是要用到opengl的....

好了,现在我们以C+win32 sdk+Opengl开发库的方式一起写opengl程序,虽然我自己也是初学者,但是这全当做备份好了,如果读者对所贴出的东东有任何意见,可以与我联系。

(注:我学习opengl参考的书籍是《opengl开发宝典》和《NeHe的OPENGL教程》,不过如果想真正成为一个游戏开发或者虚拟开发人员,深入了解OPENGL,有关计算机图形学的知识必须精通(我自己是自学的),高等数学必须好,线性代数必须精通),我们知道,写opengl程序有多种开发方式(基于c,基于win32...),不过,为了与前面我写的WIN32 sdk相关联,我用的是基于WIN32 的方式,因为这样会更加良好的工作在Windows操作系统之上(opengl是跨平台的,如果你想让你的opengl程序在其他操作系统上跑起来,可以采取其他方式编写,当然库还是opengl库),下面就开始写opengl程序。

 

《opengl开发宝典》采取的是C方式,《NeHe的OPENGL教程》采取的是WIN32方式,两本书都很好,不过,我发现Nehe的教程简直是太经典了,所以一下代码均采用他的方案。

 

opengl第一天:完善opengl程序框架(openglFrame):

一个程序的框架的搭建是程序友好性的体现,Nehe的程序框架是我见过的算是最好的了,下面就列出Nehe框架源码(没有任何更改,只有注释是我自己加上的):

 

//#include "stdafx.h"

//头文件
#include<gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>
//库


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

//全局
HDC hDC=NULL;//设备上下文
HGLRC hRC=NULL;//着色描述表
HWND hWnd=NULL;//窗口句柄
HINSTANCE hInstance;//程序实例句柄

bool keys[256];//按键状态
bool active=TRUE;//窗口激活状态
bool fullscreen=TRUE;//全屏模式标记

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);//整口过程,opengl程序内所有消息都在这里响应


GLvoid ReSizeGLScreen(GLsizei width,GLsizei height)//当窗口大小改变时进行窗口窗调
{
 if (height==0)
 {
  height=1;//防止被零除,因为在调整窗口大小时,为不改变窗口内容的显示效果(原来在左上角的东东调整后还在左上角。。。),利用的是窗口宽度与高度的比例进行调整的(width/height)
 }

 glViewport(0,0,width,height);//重新调整当前视口

 glMatrixMode(GL_PROJECTION);//选择投影矩阵

 


 glLoadIdentity();//重置投影矩阵

 gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);//选择模型观察矩阵


 glMatrixMode(GL_MODELVIEW);// 选择模型观察矩阵

 glLoadIdentity();//重置模型观察矩阵

}

//下面对opengl程序进行初始化
int InitGL(GLvoid)
{
 glShadeModel(GL_SMOOTH);//启用阴影平滑


 glClearColor(0.0f,0.0f,0.0f,0.0f);//设置清屏颜色(类似于win32c程序的背景色)

 glClearDepth(1.0f);//设置深度缓存
 glEnable(GL_DEPTH_TEST);//启用深度测试
 glDepthFunc(GL_LEQUAL);//选择深度测试类型
 glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//修正透视(如果有必要的话)
 return TRUE;
}

//下面的函数用来实现你想要绘制的任何东东,不过这个程序只是opengl的框架程序,没有绘制其他东西
int DrawGLScreen(GLvoid)
{
 glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象

 glLoadIdentity();//重置模型观察矩阵,
 return TRUE;
}

//下面的函数处理窗口销毁,释放一些资源,如果不处理,就会泄露,导致CPU工作异常
GLvoid KillGLWindow(GLvoid)
{
 if (fullscreen)
 {
  ChangeDisplaySettings(NULL,0);//Nehe教程是这样解释的:
  //我们使用ChangeDisplaySettings(NULL,0)回到原始桌面。将NULL作为第一个参数,0作为第二个参数传递强制Windows使用当前存放在注册表中的值(缺省的分辨率、色彩深度、刷新频率,等等)来有效的恢复我们的原始桌面。切换回桌面后,我们还要使得鼠标指针重新可见。
  ShowCursor(TRUE);//显示光标
 }

 if (hRC)//判断是否拥有渲染描述表,这里解释下什么是DC,RC,我们知道,opengl工作强烈依赖于计算机的显卡(显示器),显卡是一种设备,外部程序访问设备,然后让CPU去处理,就必须找到设备的接口,这里的DC(着色描述表)和RC(渲染描述表)其实是与显示器相关的一个内存字段,我们就是通过他们来访问显卡设备,然后对其进行操作的(个人的见解,可能有错误)
 {
  if (!wglMakeCurrent(NULL,NULL))//释放DC和RC
  {
   MessageBox(NULL,TEXT("释放DC和RC失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);

  }
  if (!wglDeleteContext(hRC))//删除RC
  {
   MessageBox(NULL,TEXT("释放渲染上下文失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
  }

  hRC=NULL;
 }

 if (hDC && !ReleaseDC(hWnd,hDC))//删除DC
 {
  MessageBox(NULL,TEXT("释放设备上下文失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
  hDC=NULL;
 }

 if (hWnd && !DestroyWindow(hWnd))//销毁窗口
 {
  MessageBox(NULL,TEXT("销毁窗口失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);

  hWnd=NULL;
 }

 if (!UnregisterClass(TEXT("OpenGL"),hInstance))//销毁注册的窗口类
 {
  MessageBox(NULL,TEXT("销毁注册类失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);

  hInstance=NULL;
 }
}

//下面的函数创建一个安全的opengl窗口,包括标题,窗口大小,显示模式,像素....
BOOL CreateGLWindow(TCHAR *title,int width,int height,int bits,bool fullscreenflag)
{
 GLuint PixelFormat;//像素模式
 WNDCLASS wc;//窗口类
 DWORD dwExStyle;//扩展的窗口模式
 DWORD dwStyle;//窗口模式
 RECT WindowRect;//窗口大小
 WindowRect.left=(long)0;
 WindowRect.right=(long)width;
 WindowRect.top=(long)0;
 WindowRect.bottom=(long)height;

 fullscreen=fullscreenflag;//显示模式标志

 hInstance=GetModuleHandle(NULL);//获取主调进程的实例句柄

 wc.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC;//设置窗口类型
 wc.lpfnWndProc=(WNDPROC)WndProc;//关联窗口过程
 wc.cbClsExtra=0;//未设置
 wc.cbWndExtra=0;//未设置
 wc.hInstance=hInstance;
 wc.hIcon=LoadIcon(NULL,IDI_WINLOGO);//设置图标
 wc.hCursor=LoadCursor(NULL,IDC_ARROW);//设置光标
 wc.hbrBackground=NULL;//没有背景,因为上面已经设置了窗口背景
 wc.lpszMenuName=NULL;//没有菜单,当然你也可以设置菜单,具体方案可参考我前面的win32SDK篇的菜单篇
 wc.lpszClassName=TEXT("OpenGL");//窗口类名

 if (!RegisterClass(&wc))//尝试注册窗口类
 {
  MessageBox(NULL,TEXT("注册窗口类失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);
  return FALSE;
 }

 if(fullscreen)//判断是否处于全屏模式,是的话,进行一下处理
 {
  DEVMODE dmScreenSettings;//设备模式

  memset(&dmScreenSettings,0,sizeof(dmScreenSettings));//清零,当用户多次选择全屏时,都会执行这里的代码,但我们并不知道我们所申请的内存是否可用,所以每次进来都要清零

  //初始化
  dmScreenSettings.dmSize=sizeof(dmScreenSettings);//dmScreenSettings结构大小
  dmScreenSettings.dmPaperWidth=width;//屏幕宽度
  dmScreenSettings.dmPelsHeight=height;//屏幕高度
  dmScreenSettings.dmBitsPerPel=bits;//每位像素的色彩深度
  dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;//设置设备相关量,包括宽度,色彩深度,高度。

  if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)//尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。

  {
   //如果失败,让用户区处理选择
   if (MessageBox(NULL,TEXT("您当前PC不支持全屏模式,确定是否使用窗口模式!"),TEXT("提示"),MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
   {
    fullscreen=FALSE;
   }
   else
   {
    MessageBox(NULL,TEXT("应用程序即将推出"),TEXT("失败"),MB_OK|MB_ICONSTOP);

    return FALSE;
    
   }
  }
 }

 //这里是紧接着上面的设置显示模式,如果成功的话(fullscreen为真),设置窗口显示模式
 if(fullscreen)
 {
  dwExStyle=WS_EX_APPWINDOW;
  dwStyle=WS_POPUP;

  ShowCursor(FALSE);
 }
 //如果失败的话,也要进行相应的处理
 else
 {
  dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  dwStyle=WS_OVERLAPPEDWINDOW;
  
 }
 
 //用户已经改变了窗口(大小或方位),要进行调整
 AdjustWindowRectEx(&WindowRect,dwStyle,FALSE,dwExStyle);

 //创建窗口,如果失败,销毁它(包括一切相关资源)
 if (!(hWnd=CreateWindowEx(
  dwExStyle,
  TEXT("OpenGL"),
  title,
  dwStyle|
  WS_CLIPSIBLINGS|
  WS_CLIPCHILDREN,
  0,
  0,
  WindowRect.right-WindowRect.left,
  WindowRect.bottom-WindowRect.top,
  NULL,
  NULL,
  hInstance,
  NULL)))
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("创建窗口失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);

  return FALSE;
 }


 //下面利用一个结构体来设置像素模式
 static PIXELFORMATDESCRIPTOR pfd=
 {
  sizeof(PIXELFORMATDESCRIPTOR),//上述格式描述符的大小

  1,// 版本号

  PFD_DRAW_TO_WINDOW|//格式支持窗口

  PFD_SUPPORT_OPENGL|//格式支持opengl
  PFD_DOUBLEBUFFER,//支持双缓冲
  PFD_TYPE_RGBA,//支持RGBA(Red,Green,Blue,Alpha,即三原色和透明度,任何色彩都有这组成)
  bits,//色彩深度
  0,0,0,0,0,0,//忽略的某些色彩位
  0,//没有ALPHA缓冲
  0,//忽略Shift Bit

  0,//无累加缓存

  0,0,0,0,// 忽略聚集位

  16,// 16位 Z-缓存 (深度缓存)

  0,//无蒙板缓存

  0,//无辅助缓存

  PFD_MAIN_PLANE,//主会绘图层
  0,//保留位

  0,0,0//忽略层遮罩

 };


 //尝试获取设备上下文
 if (!(hDC=GetDC(hWnd)))
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("创建设备上下文失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION );

  return FALSE;

 }

 //尝试寻找响应的像素模式
 if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("没有发现可使用的像素模式"),TEXT("失败"),MB_OK |MB_ICONEXCLAMATION);

  return FALSE;


 }

 //找到后设置它
 if (!SetPixelFormat(hDC,PixelFormat,&pfd))
 {
  KillGLWindow();        
  MessageBox(NULL,L"设置像素模式失败.",L"失败",MB_OK|MB_ICONEXCLAMATION);
  return FALSE;        
 }
 //创建渲染上下文
 if (!(hRC=wglCreateContext(hDC)))
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("创建渲染上下文失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);

  return FALSE;
 }

 //激活渲染上下文
 if (!wglMakeCurrent(hDC,hRC))
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("激活渲染上下文失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);

  return FALSE;
 }


 
 ShowWindow(hWnd,SW_SHOW);//如果一切顺利,就显示窗口
 SetForegroundWindow(hWnd);//将窗口暂时挂在Z轴最前端
 
 SetFocus(hWnd);//将焦点调整到窗口

 ReSizeGLScreen(width,height);//重新设置窗口大小
 
 //初始化opengl环境
 if (!InitGL())
 {
  KillGLWindow();
  MessageBox(NULL,TEXT("初始化opengl环境失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);

  return FALSE;
 }

 //一切成功,顺利返回
 return TRUE;


}

//下面的东东我就不解释了,和WIN32sdk没多大区别,
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
 switch(uMsg)
 {
 case WM_ACTIVATE://监视窗口激活消息

  {
   if (!HIWORD(wParam))
   {
    active=TRUE;
   }
   else
   {
    active=FALSE;
   }
   return 0;
  }
 case WM_SYSCOMMAND://监视系统中断,阻止一切中断
  {
   switch(wParam)
   {
   case SC_SCREENSAVE://屏保中断
   case SC_MONITORPOWER://显示器节电中断
    return 0;
   }
   break;
  }
 case WM_CLOSE://退出
  {
   PostQuitMessage(0);
   return 0;
  }
 case WM_KEYDOWN://按键
  {
   keys[wParam]=TRUE;

   return 0;
  }
 case WM_KEYUP://释放按键
  {
   keys[wParam] = FALSE;
   return 0;
  }
 case WM_SIZE://窗口大小改变
  {
   ReSizeGLScreen(LOWORD(lParam),HIWORD(lParam));
   return 0;
  }
 }
 return DefWindowProc(hWnd,uMsg,wParam,lParam);//缺省处理
}
int WINAPI WinMain(HINSTANCE hInstance,
 HINSTANCE hPrevInstance,
 LPSTR lpCmdLine,
 int nCmdShow)
{
 MSG msg;
 BOOL done=FALSE;

 if (MessageBox(NULL,TEXT("是否在全屏模式下运行"),TEXT("提示"),MB_YESNO |MB_ICONQUESTION )==IDNO)
 {
  fullscreen=FALSE;
 }
 if (!CreateGLWindow(TEXT("天策的opengl程序框架"),640,480,16,fullscreen))
 {
  return 0;
 }

 while (!done)
 {
  if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  {
   if (msg.message==WM_QUIT)
   {
    done=TRUE;
   }
   else
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
  }
  else
  {
   if (active)
   {
    if (keys[VK_ESCAPE])
    {
     done=TRUE;
    }
    else
    {
        DrawGLScreen();
     SwapBuffers(hDC);
    }
   }
   if (keys[VK_F1])
   {
    keys[VK_F1]=FALSE;
    KillGLWindow();
    fullscreen=!fullscreen;
    if (!CreateGLWindow(TEXT("天策的opengl程序框架"),640,480,16,fullscreen))
    {
     return 0;
    }
   }
  }
 }

 KillGLWindow();
 return (msg.wParam);
}

 

 

 

 OpenGL第二天:在窗口内绘制简单图形

 

上面的框架实现了一个友好的OpenGL程序窗口,下面我们来在窗口内绘制一点简单的东西(以绘制三角形和正方形为例):

框架不需要修改,只要在 DrawGLScreen(GLvoid)里添加即可:

int DrawGLScreen(GLvoid)
{
 glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象

 glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
 glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
 //顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
 glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
 
  glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
  glVertex3f(-1.0f,-1.0f,0.0f);//左
  glVertex3f(1.0f,-1.0f,0.0f);//右
 glEnd();//绘制结束
 
 glTranslatef(4.0f,0.0f,0.0f);      // 右移3单位
 glBegin(GL_QUADS);       //  绘制正方形
  glVertex3f(-1.0f, 1.0f, 0.0f);     // 左上
  glVertex3f( 1.0f, 1.0f, 0.0f);     // 右上
  glVertex3f( 1.0f,-1.0f, 0.0f);     // 左下
  glVertex3f(-1.0f,-1.0f, 0.0f);     // 右下
 glEnd();        // 正方形绘制结束

 return TRUE;
}

 

 

 

 OpenGL第三天:为所绘制的图形添加色彩

上面画出的图形默认为白色,实在太单调,下面让它看起来更加美观一点,只要在绘制每个点时指定颜色即可,每个区域的颜色取决于离他最近的你所设定的颜色:

 

int DrawGLScreen(GLvoid)
{
 glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象

 glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
 glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
 //顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
 glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
 glColor3f(1.0f,0.0f,0.0f);    // 设置当前色为红色
  glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
  glColor3f(0.0f,1.0f,0.0f);    // 设置当前色为绿色
  glVertex3f(-1.0f,-1.0f,0.0f);//左
  glColor3f(0.0f,0.0f,1.0f);    // 设置当前色为蓝色
  glVertex3f(1.0f,-1.0f,0.0f);//右
 glEnd();//绘制结束
 
 glTranslatef(4.0f,0.0f,0.0f);      // 右移3单位
 glBegin(GL_QUADS);  //  绘制正方形
 glColor3f(0.5f,0.0f,1.0f); //这里的设置将影响到整个图形
  glVertex3f(-1.0f, 1.0f, 0.0f);     // 左上
  glVertex3f( 1.0f, 1.0f, 0.0f);     // 右上
  glVertex3f( 1.0f,-1.0f, 0.0f);     // 左下
  glVertex3f(-1.0f,-1.0f, 0.0f);     // 右下
 glEnd();        // 正方形绘制结束

 return TRUE;
}

 

 OpenGL第四天:实现动态图形

下面在实现对图形的动态展示:

首先说明一下原理:

就好像我们挥手一样,我们看到的是一条弧线,我们挥手的每个时刻,手的位置都是不同的,由于挥手的速度很快,加上视觉暂留,所以形成弧线,如果放慢速度,我们看到的就不是弧线了,利用这个原理,计算机动画就形成了,上面说过,显示器并不是静态的,而是以一定的频率在刷新(大概几十毫秒),这个频率足以让我们看不见闪烁,OPENGL利用每次刷屏的时候,改变图形的在显示屏的位置,就形成了动画。如果你查看DrawGLScreen(GLvoid)在程序里面的位置(不是申明或定义),就会发现我说的是完全正确的。

//先增加两个全局变量,分别表示两个图形的旋转度数

GLfloat		rtri;						// 用于三角形的角度
GLfloat		rquad;						// 用于四边形的角度

//然后在DrawGLScreen(GLvoid)里添加

 

 

int DrawGLScreen(GLvoid)
{
 glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象

 glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
 glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
 //顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
 glRotatef(rtri,0.0f,1.0f,0.0f);    // 绕Y轴旋转三角形
 glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
     glColor3f(1.0f,0.0f,0.0f);    // 设置当前色为红色
  glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
  glColor3f(0.0f,1.0f,0.0f);    // 设置当前色为红色
  glVertex3f(-1.0f,-1.0f,0.0f);//左
  glColor3f(0.0f,0.0f,1.0f);    // 设置当前色为红色
  glVertex3f(1.0f,-1.0f,0.0f);//右
 glEnd();//绘制结束
 
 glLoadIdentity();     // 重置模型观察矩阵

 glTranslatef(1.5f,0.0f,-6.0f);      // 右移3单位
 glRotatef(rquad,1.0f,0.0f,0.0f);   //  绕X轴旋转四边形

 

 glBegin(GL_QUADS);  //  绘制正方形
     glColor3f(0.5f,0.0f,1.0f); 
  glVertex3f(-1.0f, 1.0f, 0.0f);     // 左上
  glVertex3f( 1.0f, 1.0f, 0.0f);     // 右上
  glVertex3f( 1.0f,-1.0f, 0.0f);     // 左下
  glVertex3f(-1.0f,-1.0f, 0.0f);     // 右下
 glEnd();        // 正方形绘制结束

 


 rtri+=0.2f;      // 增加三角形的旋转变量
 rquad-=0.15f;      // 减少四边形的旋转变量

 return TRUE;
}

 

 

 

 OpenGL第五天:进入3D空间

OpenGL作为3D引擎,如果我们学习OPENGL不写3D程序的话....白学了(其他任何API均能实现二位空间)

其实3D程序也是很简单的,绘制3D图形,只要按照现实中的逻辑,我们学习数学时绘图一样,先画出三维坐标系,然后分别在X,Y,Z轴上画出顶点,连线即可。

下面参照NeHe的程序,列出代码:

 

int DrawGLScene(GLvoid)         

{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
 glLoadIdentity();        

 glTranslatef(-1.5f,0.0f,-6.0f);    

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

//绘制棱锥   

 glBegin(GL_TRIANGLES);       
     glColor3f(1.0f,0.0f,0.0f);      
     glVertex3f( 0.0f, 1.0f, 0.0f);     

     glColor3f(0.0f,1.0f,0.0f);    

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

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

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

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

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

   glColor3f(0.0f,1.0f,0.0f);      

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

glEnd();         

 glLoadIdentity();        

 glTranslatef(1.5f,0.0f,-7.0f);      

  glRotatef(rquad,1.0f,1.0f,1.0f);

//绘制立方体

glBegin(GL_QUADS);          

glColor3f(0.0f,1.0f,0.0f);     

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

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

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

    glVertex3f( 1.0f, 1.0f, 1.0f);    

  glColor3f(1.0f,0.5f,0.0f);  

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

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

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

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

   glColor3f(1.0f,0.0f,0.0f);    

  glVertex3f( 1.0f, 1.0f, 1.0f);     

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

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

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

glColor3f(1.0f,1.0f,0.0f);       

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

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

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

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

 glColor3f(0.0f,0.0f,1.0f);    

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

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

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

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

glColor3f(1.0f,0.0f,1.0f);    

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

 glVertex3f( 1.0f, 1.0f, 1.0f);    

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

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

   glEnd();         

 rtri+=0.2f;        

  rquad-=0.15f;        

 return TRUE;          

}

 

OpenGL第六天:3D贴图
opengl的3D贴图称为纹理,CF里花哨的图景我想就是利用了纹理(当然CF里烟、雾、雪花...opengl均能实现),下面实现简单纹理:
 首先增加几个全局变量:
GLfloat xrot;    // x轴的旋转步进
GLfloat yrot;    //  y轴的旋转步进
GLfloat zrot;    // z轴的旋转步进
 
 GLuint texture[1];   //   存储一个纹理
 
下面是核心部分,为程序载入纹理:

AUX_RGBImageRec *LoadBMP(WCHAR *Filename)    // 载入位图资源

{  HANDLE  File;       //文件句柄

 if (!Filename)          // 检查文件名是否正确  {   return NULL;         //不存在的话返回失败  }

   WCHAR *strFilePathTemp;//存储文件路径  int FILEPATHSIZE=2048;//文件路径长度  strFilePathTemp=(WCHAR *)calloc(FILEPATHSIZE,sizeof(WCHAR));//初始化文件路径  File=CreateFile(strFilePathTemp, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开位图文件,读取位图信息      

 if (File)           //检查文件是否存在  {   CloseHandle(File);       // 信息已经获取,关闭文件设备句柄   return auxDIBImageLoadW(Filename);    // 成功返回文件信息  }

 return NULL;          // 紧接着上面的判断,失败的话返回NULL }

//为程序载入纹理

int LoadGLTextures()     {  int Status=FALSE;         //状态标记

 AUX_RGBImageRec *TextureImage[1];     //创建一个纹理存储空间存储纹理信息

 memset(TextureImage,0,sizeof(void *)*1);            // 清空存储空间

 if (TextureImage[0]=LoadBMP(L"zjw.bmp"))//载入图片纹理,图片放在可执行文件的同一目录下,调试状态下放在debug同一目录下,“zjw.bmp”为文件名

 {   Status=TRUE;         // 如果载入成功设置状态位为真

  glGenTextures(1, &texture[0]);     //创建纹理

     glBindTexture(GL_TEXTURE_2D, texture[0]);//使用位图资源生成纹理   glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);//生成纹理   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//线性滤波处理   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//线性滤波处理  }   //纹理生成成功后要释放资源  if (TextureImage[0])           {   if (TextureImage[0]->data)        {    free(TextureImage[0]->data);        

}

  free(TextureImage[0]);      

 }

 return Status;         

}

 

//然后在处理初始化opengl环境时要载入纹理

int InitGL(GLvoid) {

 if (!LoadGLTextures())         {   return FALSE;         }  glEnable(GL_TEXTURE_2D);       //启用纹理映射  glShadeModel(GL_SMOOTH);//启用阴影平滑

 glClearColor(0.0f,0.0f,0.0f,0.0f);//设置清屏颜色(类似于win32c程序的背景色)

 glClearDepth(1.0f);//设置深度缓存  glEnable(GL_DEPTH_TEST);//启用深度测试  glDepthFunc(GL_LEQUAL);//选择深度测试类型  glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//修正透视(如果有必要的话)  return TRUE; }

//修改 DrawGLScreen(GLvoid)内容,绘制具有纹理的立方体

 

int DrawGLScreen(GLvoid) {  glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象

 glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)  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);   // 立方体的正前方

解释下glTexCoord2f(int x,int y)函数,这个函数的作用是每当我们绘制一个点时,就将纹理(位图)的响应的点对应上,

glTexCoord2f 的第一个参数是X坐标。 0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。 glTexCoord2f 的第二个参数是Y坐标。 0.0f 是纹理的底部。 0.5f 是纹理的中点, 1.0f 是纹理的顶部。比如下面第一行,我们是(逆时针)绘制立方体的正前方的左下角,“”对应X=0,“”对应Y=0。

  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);   // 后方   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);   // 上方   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);   // 下方   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);   // 右方   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);   // 左方   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.6f;   yrot+=0.4f;   zrot+=0.3f;

  return TRUE; }

 

 

//效果如下

 

 

OpenGL第七天:光照和键盘控制

 

连载中......................吐舌头

 

 

 

 

 

 

 

 

 

阅读更多
版权声明:技术博客自由转载,自由使用。 https://blog.csdn.net/dai_jing/article/details/8283212
个人分类: opengl
想对作者说点什么? 我来说一句

双缓冲绘图解决闪屏问题

2014年08月26日 23.2MB 下载

没有更多推荐了,返回首页

不良信息举报

我们一起写opengl程序

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭