NeHe的opengl框架delphi版

将CKER翻译的NeHe的VC 的OPENGL框架转成了Delphi版,希望对现象我一样初学OPENGL的兄弟有所帮助,不知为什么,
我的Delphi环境下无法直接运行,但是在别的机器上好像没问题我的机器只能编译后运行EXE文件。
感谢NeHe提供的这么好的框架,感谢CKER翻译的VC的资料Program Project1;
Uses opengl, windows, Messages;
Const WND_TITLE = 'OPenGl 基本框架';
//标题Var
//===========================================================================
// 每一个OpenGL都被连接到一个着色描述表上。着色描述表将所有的OpenGL调用命令连
// 接到Device Context(设备描述表)上,将OpenGL的着色描述表定义为hRC ,要让程序能
// 够绘制窗口的话,还需要创建一个设备描述表,Windows的设备描述表被定义为 hDC,
// DC将窗口连接到GDI(Graphics Device Interface图形设备接口)。而RC将OpenGL连接
// 到DC。
//=========================================================================== h_RC : HGLRC;
// Rendering Context(着色描述表)。 h_DC : HDC;
// Device Context(设备描述表) h_Wnd : HWND;
// 窗口句柄 h_Instance : HINST;
// 程序Instance(实例)。 keys : Array[0..255] Of Boolean;
// 用于键盘例程的数组 {$R *.res}
//==============================================================================
//重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定没有使用全屏模式)。
//甚至无法改变窗口的大小时(例如在全屏模式下),它至少仍将运行一次————————
//在程序开始时设置透视图。OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
//==============================================================================Procedure glResizeWnd(Width, Height: Integer);
// 重置并初始化GL窗口大小Begin If (Height = 0) Then
// 防止高度为0,产生除0异常 Height := 1; glViewport(0, 0, Width, Height);
// 重置当前的视口(Viewport)
//下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实
//外观的场景。此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是
//我们在场景中所能绘制深度的起点和终点。
//glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。
//投影矩阵负责为我们的场景增加透视。
//glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。
//调用 glLoadIdentity()之后我们为场景设置透视图。 glMatrixMode(GL_PROJECTION);
// 选择投影矩阵 glLoadIdentity();
// 重置投影矩阵 gluPerspective(45.0, Width / Height, 0.1, 100.0);
// 计算窗口的外观比例
//glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。
//模型观察矩阵中存放了我们的物体讯息。 glMatrixMode(GL_MODELVIEW);
// 选择模型观察矩阵 glLoadIdentity();
// 重置模型观察矩阵
//如果现在还不能理解这些术语的含义,请别着急。
//只要知道如果想获得一个精彩的透视场景的话,必须这么做。End;

//==============================================================================
// 对OpenGL进行所有的设置。将设置清除屏幕所用的颜色,打开深度缓存,
// 启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
// 此过程将有返回值。但此处的初始化没那么复杂,现在还用不着担心这个返回值。
//==============================================================================Procedure glInit();Begin
//设置清除屏幕时所用的颜色。如果对色彩的工作原理不清楚的话,快速解释一下。
//色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
//glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。
//最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
//当它用来清除屏幕的时候,不用关心第四个数字。现在让它为0.0f。
//通过混合三种原色(红、绿、蓝),可以得到不同的色彩
//因此,使用glClearColor(0.0f,0.0f,1.0f,0.0f),您蓝色来清除屏幕。
//如果用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,将使用中红色来清除屏幕。
//不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,应该将所有的颜色设成最亮(1.0f)。
//要黑色背景的话,该将所有的颜色设为最暗(0.0f)。 glClearColor(0.0, 0.0, 0.0, 0.0);
// 黑色背景
//阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。 glShadeModel(GL_SMOOTH);
// 启用阴影平滑
//接下来必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。
//深度缓存不断的对物体进入屏幕内部有多深进行跟踪。本程序其实没有真正使用深度缓存,
//但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
//这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。 glClearDepth(1.0);
// 设置深度缓存 glEnable(GL_DEPTH_TEST);
// 启用深度测试 glDepthFunc(GL_LESS);
// 所作深度测试的类型
//接着告诉OpenGL我们希望进行最好的透视修正。
//这会十分轻微的影响性能。但使得透视图看起来好一点。 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
// 真正精细的透视修正End;

//==============================================================================
//所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。
//以后的每个程序会在此处增加新的代码。
//==============================================================================Procedure glDraw();Begin glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT);
// 清除屏幕和深度缓存 glLoadIdentity();
// 重置当前的模型观察矩阵End;Function WndProc(hWnd: HWND;
// 窗口的句柄 Msg: UINT;
// 窗口的消息 wParam: WPARAM;
// 附加的消息内容 lParam: LPARAM
// 附加的消息内容 ): LRESULT; stdcall;Begin Result := 0; Case (Msg) Of
// 检查Windows消息 WM_ACTIVATE:
// 监视窗口激活消息 Begin End; WM_CREATE:
// 创建 Begin End; WM_CLOSE:
// 关闭 Begin PostQuitMessage(0);
// 发出退出消息 Result := 0 End; WM_KEYDOWN:
// 按键按下 Begin keys[wParam] := True;
// 如果是,设为TRUE Result := 0; End; WM_KEYUP:
// 按键松开 Begin keys[wParam] := False;
// 如果是,设为FALSE Result := 0; End; WM_SIZE:
//调整OpenGL窗口大小 Begin glResizeWnd(LOWORD(lParam), HIWORD(lParam));
//LoWord=Width,HiWord=Height Result := 0; End; WM_TIMER:
//timers Begin End; Else
//其余的让Windows自行处理。 Result := DefWindowProc(hWnd, Msg, wParam, lParam);
//向DefWindowProc传递所有未处理的消息。 End;End;


//==============================================================================
// 只在程序退出之前调用。作用是依次释放着色描述表,设备描述表和窗口句柄。
// 加入了许多错误检查。如果程序无法销毁窗口的任意部分,都会弹出带相应错误消息的
// 讯息窗口,
//==============================================================================Procedure glKillWnd(Fullscreen: Boolean);Begin
//在KillGLWindow()中所作的第一件事是检查是否处于全屏模式。
//如果是,要切换回桌面。本应在禁用全屏模式前先销毁窗口,
//但在某些显卡上这么做可能会使得桌面崩溃。所以还是先禁用全屏模式。
//这将防止桌面出现崩溃,并在Nvidia和3dfx显卡上都工作的很好! If Fullscreen Then
// 处于全屏模式吗? Begin
// 使用ChangeDisplaySettings(NULL,0)回到原始桌面。
// 将NULL作为第一个参数,
// 0作为第二个参数传递强制Windows使用当前存放在注册表中的值
// (缺省的分辨率、色彩深度、刷新频率,等等)来有效的恢复我原始桌面。
// 换回桌面后,还要使得鼠标指针重新可见。 ChangeDisplaySettings(devmode(Nil^), 0);
// 是的话,切换回桌面 ShowCursor(True);
//显示鼠标 End;
//是否拥有着色描述表(hRC)。 If h_RC > 0 Then
//看我们能否释放它(将 hRC从hDC分开)。 If (Not wglMakeCurrent(h_DC, 0)) Then MessageBox(0, 'DC和RC无法被释放!', '错误', MB_OK Or MB_ICONERROR);
// 能否删除着色描述表 If (Not wglDeleteContext(h_RC)) Then Begin MessageBox(0, '删除着色描述表失败!', '错误', MB_OK Or MB_ICONERROR); h_RC := 0; End;
//是否存在设备描述表,如果有尝试释放它。 If ((h_DC > 0) And (ReleaseDC(h_Wnd, h_DC) = 0)) Then Begin MessageBox(0, '释放设备描述表失败!', '错误', MB_OK Or MB_ICONERROR); h_DC := 0; End;
//是否存在窗口句柄,调用 DestroyWindow( hWnd )来尝试销毁窗口 If ((h_Wnd <> 0) And (Not DestroyWindow(h_Wnd))) Then Begin MessageBox(0, '无法销毁窗体!', '错误', MB_OK Or MB_ICONERROR); h_Wnd := 0; End;
// 注销窗口类
//这允许我们正常销毁窗口,接着在打开其他窗口时,
//不会收到诸如"Windows Class already registered"(窗口类已注册)的错误消息。 If (Not UnRegisterClass('OpenGL', hInstance)) Then Begin MessageBox(0, '无法注销窗口类!', '错误', MB_OK Or MB_ICONERROR); hInstance := 0; End;End;
//==============================================================================
// 创建OpenGL窗口,
// 带有5个参数:窗口的标题栏,窗口的宽度,窗口的高度,色彩位数(16/24/32),
// 全屏标志(TRUE --全屏模式, FALSE--窗口模式 )。
// 返回的布尔值 窗口是否成功创建。
//==============================================================================Function glCreateWnd(Width, Height: Integer; Fullscreen: Boolean; PixelDepth: Integer): Boolean;Var wndClass : TWndClass;
// 窗口类 dwStyle : DWORD;
// 窗口风格 dwExStyle : DWORD;
// 扩展窗口风格 PixelFormat : GLuint;
// 象素格式 h_Instance : HINST;
// 当前实例 dmScreenSettings : DEVMODE;
// 设备模式 pfd : TPIXELFORMATDESCRIPTOR;
//格式描述符Begin h_Instance := GetModuleHandle(Nil);
// 取得窗口的实例 ZeroMemory(@wndClass, SizeOf(wndClass));
// 初始化内存 With wndClass Do
// 设置窗口类 Begin style := CS_HREDRAW Or
// 如果长度变化, CS_VREDRAW Or
// 如果高度变化,就是只要变化就强制重画 CS_OWNDC;
//CS_OWNDC为窗口创建一个私有的DC。这意味着DC不能在程序间共享。 lpfnWndProc := @WndProc;
// WndProc处理消息
// cbClsExtra := 0;
// 无额外窗口数据
// cbWndExtra := 0;
// 无额外窗口数据 hInstance := h_Instance;
// 设置实例
//hIcon := LoadIcon(0, IDI_WINLOGO);
// 装入缺省图标 hCursor := LoadCursor(0, IDC_ARROW);
//载入鼠标指针
//hbrBackground := 0;
// GL不需要背景
//lpszMenuName := '';
// 不需要菜单 lpszClassName := 'OpenGL';
//设定类名 End; If (RegisterClass(wndClass) = 0) Then
// 注册窗体类 Begin MessageBox(0, '注册窗体类失败!', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit End;
// 如果需要全屏的话 If Fullscreen Then Begin ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
// 确保内存分配 With dmScreenSettings Do Begin
// 设置屏幕设置的参数 dmSize := SizeOf(dmScreenSettings);
// Devmode 结构的大小 dmPelsWidth := Width;
// 所选屏幕宽度 dmPelsHeight := Height;
// 所选屏幕高度 dmBitsPerPel := PixelDepth;
// 每象素所选的色彩深度 dmFields := DM_PELSWIDTH
// 设置初始标志为dmPelsWidth Or DM_PELSHEIGHT
// dmPelsHeight 和 Or DM_BITSPERPEL;
// dmBitsPerPel End;
// 转换为全屏模式 ,
//切换成与dmScreenSettings所匹配模式。
//CDS_FULLSCREEN 移去了状态条。
//并保证在来回切换时,没有移动或改变您在桌面上的窗口。
// 转换为全屏模式 If (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) = DISP_CHANGE_FAILED) Then
//转换失败 Begin MessageBox(0, '不能转换为全屏模式!', '错误', MB_OK Or MB_ICONERROR); Fullscreen := False; End; End; If (Fullscreen) Then
// 仍在全屏模式下 Begin dwStyle := WS_POPUP Or
// 没有边框 WS_CLIPCHILDREN
// 要让OpenGL正常运行,这两个属性是必须的。 Or WS_CLIPSIBLINGS;
//他们阻止别的窗体在我们的窗体内/上绘图。 dwExStyle := WS_EX_APPWINDOW;
// 窗体可见时处于最前面 ShowCursor(False);
// 不显示鼠标 End Else
//否则 Begin dwStyle := WS_OVERLAPPEDWINDOW Or
//带标题栏、可变大小的边框、菜单和最大/小化按钮 WS_CLIPCHILDREN Or
// 要让OpenGL正常运行,这两个属性是必须的。 WS_CLIPSIBLINGS;
//他们阻止别的窗体在我们的窗体内/上绘图。 dwExStyle := WS_EX_APPWINDOW Or
// 增强窗体的3D感观


//创建一个窗体 h_Wnd := CreateWindowEx(dwExStyle,
// 扩展窗体风格 'OpenGL',
// 类名 WND_TITLE,
// 标题 dwStyle,
// 窗体属性 0, 0,
// 窗体位置 Width, Height,
// 窗体大小 0,
// 没有父窗体 0,
// 没有菜单 h_Instance,
// 实例句柄 Nil);
// 不向WM_CREATE传递任何东东 If h_Wnd = 0 Then
// 创建失败,销毁窗体 Begin glKillWnd(Fullscreen); MessageBox(0, '不能创建窗体!', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
// 描述象素格式
//选择了通过RGBA(红、绿、蓝、alpha通道)支持OpenGL和双缓存的格式。
//试图找到匹配选定的色彩深度(16位、24位、32位)的象素格式。
//最后设置16位Z-缓存。
//其余的参数要么未使用要么不重要
//(stencil buffer模板缓存和accumulation buffer聚集缓存除外)。 With pfd Do Begin nSize := SizeOf(TPIXELFORMATDESCRIPTOR);
// 格式描述符大小 nVersion := 1;
// 版本号 dwFlags := PFD_DRAW_TO_WINDOW
// 格式必须支持窗口 Or PFD_SUPPORT_OPENGL
// 格式必须支持OpenGL Or PFD_DOUBLEBUFFER;
// 必须支持双缓冲 iPixelType := PFD_TYPE_RGBA;
// 申请 RGBA 格式 cColorBits := PixelDepth;
// 选定色彩深度 cRedBits := 0;
// 忽略的色彩位 cRedShift := 0;
// 忽略的色彩位 cGreenBits := 0;
// 忽略的色彩位 cGreenShift := 0;
// 忽略的色彩位 cBlueBits := 0;
// 忽略的色彩位 cBlueShift := 0;
// 忽略的色彩位 cAlphaBits := 0;
// 无Alpha缓存 cAlphaShift := 0;
// 忽略Shift Bit cAccumBits := 0;
// 无聚集缓存 cAccumRedBits := 0;
// 忽略聚集位 cAccumGreenBits := 0;
// 忽略聚集位 cAccumBlueBits := 0;
// 忽略聚集位 cAccumAlphaBits := 0;
// 忽略聚集位 cDepthBits := 16;
// 16位 Z-缓存 (深度缓存) cStencilBits := 0;
// 无模板缓存 cAuxBuffers := 0;
// 无辅助缓存 iLayerType := PFD_MAIN_PLANE;
// 主绘图层 bReserved := 0;
// 保留 dwLayerMask := 0;
// 忽略层遮罩 dwVisibleMask := 0;
// 忽略层遮罩 dwDamageMask := 0;
// 忽略层遮罩 End;
//得到设备场景描述 h_DC := GetDC(h_Wnd); If (h_DC = 0) Then Begin glKillWnd(Fullscreen);
// 创建失败,销毁窗体 MessageBox(0, '不能得到设备场景!', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
//找到相应的象素格式 PixelFormat := ChoosePixelFormat(h_DC, @pfd); If (PixelFormat = 0) Then Begin glKillWnd(Fullscreen); MessageBox(0, '找不到合适的格式', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
//设置像素格式. If (Not SetPixelFormat(h_DC, PixelFormat, @pfd)) Then Begin glKillWnd(Fullscreen); MessageBox(0, '无法创建渲染格式', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
//取得着色描述表 h_RC := wglCreateContext(h_DC); If (h_RC = 0) Then Begin glKillWnd(Fullscreen); MessageBox(0, '无法创建OpenGL 绘制描述表', '错误', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
//已经取得了设备描述表和着色描述表。
//激活着色描述表 If (Not wglMakeCurrent(h_DC, h_RC)) Then Begin glKillWnd(Fullscreen); MessageBox(0, 'Unable to activate OpenGL rendering context', 'Error', MB_OK Or MB_ICONERROR); Result := False; Exit; End;
//OpenGL窗口已经创建完成
// 显示窗体,置于最前 ShowWindow(h_Wnd, SW_SHOW);
// 显示窗口 SetForegroundWindow(h_Wnd);
// 略略提高优先级 SetFocus(h_Wnd);
// 设置键盘的焦点至此窗口 glResizeWnd(Width, Height);
// 设置透视 GL 屏幕 glInit();
// 初始化新建的GL窗口 Result := True;End;Function WinMain(hInstance: HINST;
//实例 hPrevInstance: HINST;
// 前一个实例 lpCmdLine: PChar;
// 命令行参数 nCmdShow: Integer
// 窗口显示状态 ): Integer; stdcall;Var msg : TMsg;
// Windowsx消息结构 finished : Boolean;
// 用来退出循环的Bool 变量Begin finished := False;
//应用程序初始化
//glCreateWnd,创建窗体为800*600 If Not glCreateWnd(800, 600, false, 32) Then Begin Result := 0; Exit; End; While Not finished Do Begin
//检查一个线程消息队列,将所选的范围保存到消息纪录中
//BOOL PeekMessage(
// LPMSG lpMsg,
// 消息记录的指针
// HWND hWnd,
// 窗口句柄
// UINT wMsgFilterMin,
// 第一个消息
// UINT wMsgFilterMax,
// 最后一个消息
// UINT wRemoveMsg
// 标志 Value Meaning
// ); PM_NOREMOVE 处理后保留在消息队列中
// PM_REMOVE 处理后从消息队列中清除
//要做的第一件事是检查是否有消息在等待。
//使用PeekMessage()可以在不锁住我们的程序的前提下对消息进行检查。
//许多程序使用GetMessage(),也可以很好的工作。
//但使用GetMessage(),程序在收到paint消息或其他别的什么窗口消息之前不会做任何事。 If (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) Then
//检查是否有消息
// wMsgFilterMin,wMsgFilterMax 这两个参数都为0,返回所有可用的消息 Begin If (msg.message = WM_QUIT) Then
// 如果是退出消息 finished := True
//改变循环条件,退出 Else Begin
// 否则处理消息
// 翻译消息,然后发送消息,使得WndProc() 或 Windows能够处理他们。 TranslateMessage(msg);
//翻译消息 DispatchMessage(msg);
//发送消息 End; End Else
//如果没有消息,绘制我们的OpenGL场景。 Begin glDraw();
// 重画屏幕 SwapBuffers(h_DC);
// 交换缓存 (双缓存) If (keys[VK_ESCAPE]) Then
// 如果按下了ESC键 finished := True End; End; glKillWnd(FALSE);
// 释放窗体 Result := msg.wParam;
// 退出程序End;Begin WinMain(hInstance, hPrevInst, CmdLine, CmdShow);End. 这段代码的功能是设置一个 OpenGL窗口。它可以只是一个窗口或是全屏幕的、可以任意 大小、任意色彩深度。此处的代码很稳定且很强大,您可以在您所有的OpenGL项目中使用。我会尽量在学习NeHe教程的同时将他翻译成Delphi版的。

利用上面的框架,我们可以很方便的进行我们的OPENGL编程(NeHe的教程也是围绕这个框架展开的,而且我看老外的源码,很多使用这个框架的)

下面我们在窗体上画一个三角和一个正方形

只要在Procedure glDraw();中添加以下代码即可

//==============================================================================
//所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。
//以后的每个程序会在此处增加新的代码。
//==============================================================================

Procedure glDraw();
Begin
   glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存

   //当您调用glLoadIdentity()之后,您实际上讲当前点移到了屏幕中心,X坐标轴从左至右,
   //Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。
   // 中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。
   // 移入屏幕深处是负值,移出屏幕则是正值。
   glLoadIdentity();                    // 重置当前的模型观察矩阵

   //glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。
   //根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
   //注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,
   //而是相对与当前所在的屏幕位置。
   glTranslatef(-1.5, 0.0, -6.0);       // 左移 1.5 单位,并移入屏幕 6.0

   //现在我们已经移到了屏幕的左半部分,
   //并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。
   //glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
   //通常您会需要画3个顶点,可以使用GL_TRIANGLES。
   //在绝大多数的显卡上,绘制三角形是相当快速的。
   //如果要画四个顶点,使用GL_QUADS的话会更方便。
   //但据我所知,绝大多数的显卡都使用三角形来为对象着色。
   //最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。

   //本节的简单示例中,我们只画一个三角形。
   //如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。
   //所有六点代码都应包含在glBegin(GL_TRIANGLES) 和 glEnd()之间。
   //在他们之间再不会有多余的点出现,
   //也就是说,(GL_TRIANGLES) 和 glEnd()之间的点都是以三点为一个集合的。
   //这同样适用于四边形。如果您知道实在绘制四边形的话,
   //您必须在第一个四点之后,再加上四点为一个集合的点组。
   //另一方面,多边形可以由任意个顶点,
   //(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之间有多少行代码。

    //   几何图元类型和说明
    //   类型                    说明
    //   GL_POINTS              单个顶点集
    //   GL_LINES               多组双顶点线段
    //   GL_POLYGON             单个简单填充凸多边形
    //   GL_TRAINGLES           多组独立填充三角形
    //   GL_QUADS               多组独立填充四边形
    //   GL_LINE_STRIP          不闭合折线
    //   GL_LINE_LOOP           闭合折线
    //   GL_TRAINGLE_STRIP      线型连续填充三角形串
    //   GL_TRAINGLE_FAN        扇形连续填充三角形串
    //   GL_QUAD_STRIP          连续填充四边形串

//glBegin之后的第一行设置了多边形的第一个顶点,
//glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。
//第一个点是上顶点,然后是左下顶点和右下顶点。
//glEnd()告诉OpenGL没有其他点了。
//这样将显示一个填充的三角形。

//译者:这里要注意的是存在两种不同的坐标变换方式,
//glTranslatef(x, y, z)中的x, y, z是相对与您当前所在点的位移,
//但glVertex(x,y,z)是相对于glTranslatef(x, y, z)移动后的新原点的位移。
//因而这里可以认为glTranslate移动的是坐标原点,glVertex中的点是相对最新的坐标原点的坐标值。

   glBegin(GL_TRIANGLES);               // 绘制三角形
   glVertex3f(0.0, 1.0, 0.0);           // 上顶点
   glVertex3f(-1.0, -1.0, 0.0);         // 左下
   glVertex3f(1.0, -1.0, 0.0);          // 右下
   glEnd();                             // 三角形绘制结束

   //在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。
   //为此要再次使用glTranslate。
   //这次右移,所以X坐标值为正值。
   //因为前面左移了1.5个单位,
   //这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。
   //总共要向右移3.0个单位。

   glTranslatef(3.0, 0.0, 0.0);         // 右移3单位

   //现在使用GL_QUADS绘制正方形。
   //与绘制三角形的代码相类似,画四边形也很简单。
   //唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。
   //并增加了一个点。
   //我们使用顺时针次序来画正方形-左上-右上-右下-左下。
   //采用顺时针绘制的是对象的后表面。这就是说我们所看见的是正方形的背面。
   //逆时针画出来的正方形才是正面朝着我们的。
   //现在来说并不重要,但以后您必须知道。
   glBegin(GL_QUADS);                   // 绘制正方形
   glVertex3f(-1.0, 1.0, 0.0);          // 左上
   glVertex3f(1.0, 1.0, 0.0);           // 右上
   glVertex3f(1.0, -1.0, 0.0);          // 左下
   glVertex3f(-1.0, -1.0, 0.0);         // 右下
   glEnd();                             // 正方形绘制结束

   // Markus Knauer 注:
   //在 ("OpenGL Programming Guide: The Official Guide to Learning OpenGL, Release 1",
   //J. Neider, T. Davis, M. Woo, Addison-Wesley, 1993)
   //《OpenGL编程指南:OpenGL学习的官方指南,第一版》
   //一书中清楚的解释了NeHe所指的在OpenGL中移动的单位概念:
   //"在OpenGL中真的有英寸和英里的区别吗?
   //答案是一句话:没有。透视和其他的变换都是无单位的。
   //如果您想要裁剪在1.0到20.0米,英寸、公里等等之间的平面,
   //在OpenGL中您无法做到。唯一的法则是您必须使用一致的度量单位。"
End;


你可以运行一下看看效果

在上节的内容上作些扩展,现在开始生成真正的3D对象,而不是象前两节中那样3D世界中的2D对象。我们给三角形增加一个左侧面,一个右侧面,一个后侧面来生成一个金字塔(四棱锥)。给正方形增加左、右、上、下及背面生成一个立方体。

我们混合金字塔上的颜色,创建一个平滑着色的对象。给立方体的每一面则来个不同的颜色。

Procedure glDraw();
Begin
   glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
   glLoadIdentity();                    // 重置当前的模型观察矩阵

   glTranslatef(-1.5, 0.0, -6.0);       // 左移 1.5 单位,并移入屏幕 6.0

   glRotatef(rtri, 0.0, 1.0, 0.0);      // 绕Y轴旋转三角形


   //下面的代码没有变化。在屏幕的左面画了一个彩色渐变三角形,并绕着Y轴从左向右旋转
   glBegin(GL_TRIANGLES);               // 绘制三角

{

有些人可能早已在上节课中的代码上尝试自行创建3D对象了。但经常有人来信问我:"我的对象怎么不会绕着其自身的轴旋转?看起来总是在满屏乱转。"要让您的对象绕自身的轴旋转,您必须让对象的中心坐标总是(0.0f,0,0f,0,0f)。
下面的代码创建一个绕者其中心轴旋转的金字塔。金字塔的上顶点离中心一个单位,底面离中心也是一个单位。上顶点在底面的投影位于底面的中心。

注意所有的面-三角形都是逆时针次序绘制的。这点十分重要,在以后的课程中我会作出解释。现在,您只需明白要么都逆时针,要么都顺时针,但永远不要将两种次序混在一起,除非您有足够的理由必须这么做。

我们开始画金字塔的前侧面。因为所有的面都共享上顶点,我们将这点在所有的三角形中都设置为红色。底边上的两个顶点的颜色则是互斥的。前侧面的左下顶点是绿色的,右下顶点是蓝色的。这样相邻右侧面的左下顶点是蓝色的,右下顶点是绿色的。这样四边形的底面上的点的颜色都是间隔排列的。

}

   glColor3f(1.0, 0.0, 0.0);            // 红色
   glVertex3f(0.0, 1.0, 0.0);           // 三角形的上顶点 (前侧面)
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(-1.0, -1.0, 1.0);         // 三角形的左下顶点 (前侧面)
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(1.0, -1.0, 1.0);          // 三角形的右下顶点 (前侧面)

{

现在绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处。顶点则位于Y轴上的一单位处,且Z坐标正好处于底边的两顶点的Z坐标中心。右侧面从上顶点开始向外侧倾斜至底边上。
这次的左下顶点用蓝色绘制,以保持与前侧面的右下顶点的一致。蓝色将从这个角向金字塔的前侧面和右侧面扩展并与其他颜色混合。
还应注意到后面的三个侧面和前侧面处于同一个glBegin(GL_TRIANGLES) 和 glEnd()语句中间。因为我们是通过三角形来构造这个金字塔的。OpenGL知道每三个点构成一个三角形。当它画完一个三角形之后,如果还有余下的点出现,它就以为新的三角形要开始绘制了。OpenGL在这里并不会将四点画成一个四边形,而是假定新的三角形开始了。所以千万不要无意中增加任何多余的点。

}

   glColor3f(1.0, 0.0, 0.0);            // 红色
   glVertex3f(0.0, 1.0, 0.0);           // 三角形的上顶点 (右侧面)
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(1.0, -1.0, 1.0);          // 三角形的左下顶点 (右侧面)
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(1.0, -1.0, -1.0);         // 三角形的右下顶点 (右侧面)

{现在是后侧面。再次切换颜色。左下顶点又回到绿色,因为后侧面与右侧面共享这个角。}  

glColor3f(1.0, 0.0, 0.0);            // 红色
   glVertex3f(0.0, 1.0, 0.0);           // 三角形的上顶点 (后侧面)
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(1.0, -1.0, -1.0);         // 三角形的左下顶点 (后侧面)
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(-1.0, -1.0, -1.0);        // 三角形的右下顶点 (后侧面)

{最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。右下顶点是蓝色,与前侧面的左下顶点相同。
到这里金字塔就画完了}

   glColor3f(1.0, 0.0, 0.0);            // 红色
   glVertex3f(0.0, 1.0, 0.0);           // 三角形的上顶点 (左侧面)
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(-1.0, -1.0, -1.0);        // 三角形的左下顶点 (左侧面)
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(-1.0, -1.0, 1.0);         // 三角形的右下顶点 (左侧面)
   glEnd();                             //金字塔绘制结束

{因为金字塔只绕着Y轴旋转,我们永远都看不见底面,因而没有必要添加底面。您觉得有经验了,尝试增加底面(正方形),并将金字塔绕X轴旋转来看看您是否作对了。确保底面四个顶点的颜色与侧面的颜色相匹配。}

{译者:这是我自己加的底面}

glBegin(GL_QUADS);                   // 绘制底面
   glColor3f(0.6, 0.2, 2.0);            //设置当前色为紫色
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(-1.0, -1.0, 1.0);         // 左上
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(1.0, -1.0, 1.0);          // 右上
   glColor3f(0.0, 1.0, 0.0);            // 绿色
   glVertex3f(1.0, -1.0, -1.0);         // 左下
   glColor3f(0.0, 0.0, 1.0);            // 蓝色
   glVertex3f(-1.0, -1.0, -1.0);        // 右下
   glEnd();                             // 正方形绘制结束

   glLoadIdentity();                    // 重置模型观察矩阵
   glTranslatef(1.5, 0.0, -6.0);        // 右移1.5单位,并移入屏幕 6.0
   glRotatef(rquad, 1.0, 1.0, 1.0);     // 绕X轴旋转四边形 ( 新增 )

贴图可以极大的节省CPU时间。呵呵,但是这一节费了我比较多的时间 : (因为用到了opengl的辅助库,现在这个库的函数已经很少有人用了,但是我还是找到了,感谢zdcnow(磁效应),他给我提供的这个辅助库的delphi版本。在学习本节之前,请大家到网上下载glaux.dll、Glaux.pas文件,并加到项目中。好了,让我们继续OPENGL之路.首先我们需要加进SysUtils单元,因为我们这节要用到文件操作,我们还要将Glaux单元加进来。然后我们在第一课的基础上加上几个变量,xrot , yrot 和 zrot 。这些变量用来使立方体绕X、Y、Z轴旋转。texture[] 为一个纹理分配存储空间。如果您需要不止一个的纹理,应该将数字1改成您所需要的数字。}VAR h_RC : HGLRC;
// Rendering Context(着色描述表)。 h_DC : HDC;
// Device Context(设备描述表) h_Wnd : HWND;
// 窗口句柄 h_Instance : HINST;
// 程序Instance(实例)。 keys : Array[0..255] Of Boolean;
// 用于键盘例程的数组 xrot,
// X 旋转量 ( 新增 ) yrot,
// Y 旋转量 ( 新增 ) zrot : GLfloat;
// Z 旋转量 ( 新增 ) Texture : Array[0..1] Of GLuint;
// 存储一个纹理 ( 新增 ){然后引载入opengl32.dll中的两个过程,我们要用到他们}Procedure glGenTextures(n: GLsizei; Var textures: GLuint); stdcall; external opengl32;Procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;{接下来我们需要增加一个新的函数,用来再入图像,该函数的返回类型在Glaux.pas中定义如下:TAUX_RGBImageRec= record sizeX, sizeY: GLint; data: pointer; end; PTAUX_RGBImageRec= ^TAUX_RGBImageRec;具体含义会在后面介绍}Function LoadBmp(filename: pchar): PTAUX_RGBImageRec;Var BitmapFile : Thandle;
// 文件句柄Begin
//接下来检查文件名是否已提供 If Filename = '' Then
// 确保文件名已提供。 result := Nil;
// 如果没提供,返回 NULL
//接着检查文件是否存在。 BitmapFile := FileOpen(Filename, fmOpenWrite);
//尝试打开文件
//如果我们能打开文件的话,很显然文件是存在的。 If BitmapFile > 0 Then
// 文件存在么? Begin
//关闭文件。 FileClose(BitmapFile);
// 关闭句柄
//auxDIBImageLoad(Filename) 读取图象数据并将其返回。 result := auxDIBImageLoadA(filename);
//载入位图并返回指针 End Else
//如果我们不能打开文件,我们将返回NiL。 result := Nil;
// 如果载入失败,返回NiL。End;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NeHe OpenGL教程中文PDF [教程说明] 整个教程由nehe制作 课程内容由dancingwind(周炜)以及gamedev和csdn的志愿者翻译 最早的翻译应该是由CKER完成的(1~12章) 注:本教程经dancingwind授权发布于http://www.yakergong.com dancingwind获得Nehe授权 [dancingwind的权声明] 权与使用声明: 我是个对学习和生活充满激情的普通男孩,在网络上我以DancingWind为昵 称,我的联系方式是[email protected],如果你有任何问 题,都可以联系我。 引子 网络是一个共享的资源,但我在自己的学习生涯中浪费大量的时间去搜索 可用的资料,在现实生活中花费了大量的金钱和时间在书店中寻找资料, 于是我给自己起了个昵称DancingWind,其意义是想风一样从各个知识的站 点中吸取成长的养料。在飘荡了多年之后,我决定把自己收集的资料整理 为一个统一的资源库。 权声明 所有DancingWind发表的内容,大多都来自共享的资源,所以我没有资格把 它们据为己有,或声称自己为这些资源作出了一点贡献。故任何人都可以 复制,修改,重新发表,甚至以自己的名义发表,我都不会追究,但你在 做以上事情的时候必须保证内容的完整性,给后来的人一个完整的教程。 最后,任何人不能以这些资料的任何部分,谋取任何形式的报酬。 发展计划 在国外,很多资料都是很多人花费几年的时间慢慢积累起来的。如果任何 人有兴趣与别人共享你的知识,我很欢迎你与我联系,但你必须同意我上 面的声明。 感谢 感谢我的母亲一直以来对我的支持和在生活上的照顾。 感谢我深爱的女友田芹,一直以来默默的在精神上和生活中对我的支持, 她甚至把买衣服的钱都用来给我买书了,她真的是我见过的最好的女孩, 希望我能带给她幸福。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值