OpenGL实现3D游戏编程【连载1】——初探3D世界
1、前言
在我学习C++的过程中,研究了一下OpenGL编程,打开了3D世界的编程世界,3D世界的效果还是相当不错。而且OpenGL能够支持跨平台兼容,是不错的学习方向,于是就自己学习了网上的很多教程,并将所有学到的知识运用到自己编程中去,现在正好有时间,就将自己编程中的一些点点滴滴汇总出来,以供大家参考。
2、设计的目标
那么我们还是从游戏的角度出发,去了解一下游戏中的功能都是怎么实现的。这一切还是要从自己玩游戏开始说起,此前就玩过一下3D游戏,当时就被游戏里的一些画面深深的吸引了,同时,游戏里头还有很多,很有趣的设定,比如,玩家的视角是怎么移动的?崎岖不平的地图是怎样制作的?人物和物体、地面的碰撞是怎样检测的?鼠标是怎样选中眼前的物体的?魔法技能是怎样释放的?不用加载进度条的无缝世界地图是怎么实现的?带着这些疑问,我们走进了一个OpenGL世界的3D世界。
3、程序设计的思路
为了实现以上游戏丰富多彩的内容,我们需要对相应的游戏内容实现进行探索,从一开始的准备工作,游戏库文件的准备,第一个游戏窗口的创建,运用OpenGL画出基本的3D物体,文字的显示,视角的变化,贴图纹理的使用,点在三维世界的位置转换,三维世界顶点在二维屏幕的投影位置,鼠标怎样选中物体,贝塞尔曲线和曲面的应用等等一系列的问题进行研究,逐步模仿实现游戏世界的内容。虽然有很多内容网上都只言片语的提到过,但很少有系统来说的,这里我就我自己编程中遇到的问题和解决的办法汇总出来,仅供参考。
4、准备工作
我们这里还是选取VC6.0来创建工程,毕竟他对系统的要求不高,实现一些功能来说,简单容易上手,源码文件体积小,也更好分享一些。这里我们首先要准备一下OpenGL的运行库,除了VC6.0自带的运行库外,我们需要在网上下载一个很重要的OpenGL支持运行库,这里我已经准备好放在附件里了,大家可以直接下载使用。
后期我们在建立名称为World的工程文件夹后,需要对以上运行库进行以下处理,以便后期使用,具体步骤如下:
第一、把以上的运行库的GL文件夹直接放在工程的World目录下;
第二、把所有DLL后缀的文件直接放在工程的World目录下;
第三、把所有LIB后缀的文件拷贝到VC的安装路径(通常为Microsoft Visual Studio\VC98\Lib)下,如果提示文件重复是否需要覆盖,全部选择覆盖即可。
那么我们现在急需一个工程,并创建第一个窗口。
5、创建一个窗口
我们想制作一个游戏,首先必须得创建一个WINDOWS的窗口,我们先用VC创建一个最简单的窗口。就好比在学各种编程语言的时候都会创建一个基本的hello world示例程序一样,这样可以给我们的编程从感觉上带来简单容易上手的良好效果。
6、创建窗口的代码
这里我们没有使用网上众多采用的DOS窗口建立的运行环境,主要是那样的窗口给游戏设计带来的体验感较差,这里使用WINDOWS窗口更符合设计的需求。我们先建立一个World.cpp的源文件,将以下程序保存到文件,再用VC编译运行后就能产生一个简单的程序窗口。
这里窗口代码运行有错误提示(unresolved external symbol _main)的,请参照消灭星星游戏程序设计【连载一】——游戏窗口的创建中第6部分的解决办法。
//加载系统头文件#include "Windows.h"#include "Stdio.h"#include "Imm.h"#include "Math.h"#include "Time.h"#include "Winuser.h"#include "FStream.h"#include "IOStream.h"#include "MMSystem.h"//加载OPENGL头文件#include "gl/glu.h"#include "gl/glut.h"#include "gl/glaux.h"//加载链接库#pragma comment(lib,"glut.lib")#pragma comment(lib,"glaux.lib")#pragma comment(lib,"glu32.lib")#pragma comment(lib,"glut32.lib")#pragma comment(lib,"opengl32.lib")//加载链接库#pragma comment(lib,"winmm.lib")#pragma comment(lib,"user32.lib")#pragma comment(lib,"msimg32.lib")#pragma comment(lib,"imm32.lib")//加载自定义头文件#include "Library\Library.h"//#include "Game\Game.h"//#include "Interface\Interface.h"//加载自定义头文件#include "Library\Library.cpp"//#include "Game\Game.cpp"//#include "Interface\Interface.cpp"//全局句柄HWND hWnd=NULL;HINSTANCE hInstance=NULL;//消息处理模块LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{HDC hDC;switch (message){case WM_CREATE:return 0;case WM_PAINT:PAINTSTRUCT PS; hDC=BeginPaint(hWnd,&PS);ReleaseDC(hWnd,hDC); return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hWnd,message,wParam,lParam);}//主函数int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{MSG message;HWND hWnd;CHAR szAppName[]="World";//设置程序的样式WNDCLASS WC;WC.style = CS_HREDRAW|CS_VREDRAW;WC.lpfnWndProc = WndProc;WC.cbClsExtra = 0;WC.cbWndExtra = 0;WC.hInstance = hInstance;WC.hIcon = LoadIcon(hInstance,IDI_APPLICATION);WC.hCursor = LoadCursor(hInstance,IDC_ARROW);WC.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);WC.lpszMenuName = NULL;WC.lpszClassName = szAppName;if(!RegisterClass(&WC)){return 0;}//创建窗口hWnd=CreateWindow(szAppName,szAppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);//赋值全局变量::hWnd=hWnd;::hInstance=hInstance;//显示更新窗口ShowWindow(hWnd,iCmdShow);UpdateWindow(hWnd);//消息循环while(GetMessage(&message,NULL,0,0)){TranslateMessage(&message);DispatchMessage(&message);}return message.wParam;}
7、3D屏幕的初始化
我们看到刚刚窗口跟平时建立的窗口没有什么区别,但是这样的窗口,目前现在还不能显示3d的物体,现在还只能像我们平时的应用程序显示文字和图片,但这并不是我们需要的,我们需要的是一个能够显示3d效果的窗口。我们需要给现实设备HDC进行初始化设置,我们通过一个SetPixelFormat(HDC hDC)函数进行设置操作,以便告诉系统我们要进行OpenGL的3d显示,在以上初始化完屏幕后,我们就可以显示3d物体了。
这里要注意:在以下添加代码前,必须完成本节第4部分的准备工作,将所有的库文件准备完毕,否则会提示出错。
//初始化OPENGL设置bool SetPixelFormat(HDC hDC)
{HGLRC hRC;GLuint pixelformat;static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be{sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor1, // Version NumberPFD_DRAW_TO_WINDOW | // Format Must Support WindowPFD_SUPPORT_OPENGL | // Format Must Support OpenGLPFD_DOUBLEBUFFER, // Must Support Double BufferingPFD_TYPE_RGBA, // Request An RGBA Format16, // Select Our Color Depth0, 0, 0, 0, 0, 0, // Color Bits Ignored0, // No Alpha Buffer0, // Shift Bit Ignored0, // No Accumulation Buffer0, 0, 0, 0, // Accumulation Bits Ignored16, // 16Bit Z-Buffer (Depth Buffer)0, // No Stencil Buffer0, // No Auxiliary BufferPFD_MAIN_PLANE, // Main Drawing Layer0, // Reserved0, 0, 0 // Layer Masks Ignored};if(!(pixelformat=ChoosePixelFormat(hDC,&pfd))){MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);return false;}if(!SetPixelFormat(hDC,pixelformat,&pfd)){MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);return false;}if(!(hRC=wglCreateContext(hDC))){MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);return false;}if(!(wglMakeCurrent(hDC,hRC))){MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);return false;}//初始化OPENGL窗口if(true){glShadeModel(GL_SMOOTH);glClearColor(0.0f,0.0f,0.0f,1.0f);glClearDepth(1.0f);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL);glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);}return true;}
随后,我们只需要在程序初始化消息WndProc函数中对屏幕进行初始化,既可以完成3d窗口的设置,开启我们的3d编程之旅。
case WM_CREATE:hDC=GetDC(hWnd);SetPixelFormat(hDC);ReleaseDC(hWnd,hDC);return 0;
8、显示第一个3D物体
设置好以上显示模式后,我们就行可以开始显示3D物体了。我们需要在消息处理函数WndProc中对WM_PAINT的处理添加我们的显示内容。
case WM_PAINT:PAINTSTRUCT PS; hDC=BeginPaint(hWnd,&PS);......//在此处添加3D显示设置及内容......ReleaseDC(hWnd,hDC); return 0;
接下来,我们在以上待添加3D显示设置及内容的地方添加显示代码如下:
//显示3d世界内容if(true){//获取窗口大小RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);//初始化3D视角glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(45,(float)tempClientRect.right/(float)tempClientRect.bottom,0.01f,1000.0f);glMatrixMode(GL_MODELVIEW);glLoadIdentity();//设置用户眼睛视角,展示壮观的三维世界从这里开始gluLookAt(10.0f,10.0f,10.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);//进行初始化设置,清除屏幕glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//开始用户自定义绘图if(true){//设定颜色glColor3f(1.0f,0.0f,0.0f);//显示立方体glutSolidCube(3.0f);}//结束用户自定义绘图并显示到屏幕上glFlush();SwapBuffers(hDC); }
小结
通过以上操作,我们已经生成了第一个Windows窗口下的OpenGL程序,在窗口的正中央显示了一个正方体,当然这个正方体是从45度视角向下观察的结果,我们后期还会给正方体各个面添加不同的颜色,这样立方体才能更加逼真。但作为OpengGL编程的第一节内容,能够实现一个3D物体,已经达到了我们的目的。随着后期内容的添加,我们将探索更多、更神奇的功能效果。
立方体的显示效果如下:
运行源码见附近。
相关文章:

OpenGL实现3D游戏编程【连载1】——初探3D世界
1、前言 在我学习C的过程中,研究了一下OpenGL编程,打开了3D世界的编程世界,3D世界的效果还是相当不错。而且OpenGL能够支持跨平台兼容,是不错的学习方向,于是就自己学习了网上的很多教程,并将所有学到的知…...

工程化实践:工程配置化设计
文内项目 Github:XIAOJUSURVEY 配置化是很灵活且很常见的使用,那XIAOJUSURVEY里有哪些地方应用到了呢? 基础模板 问卷模板 在创建问卷时,我们提供了多种问卷类型选择,例如普通问卷、投票、报名、NPS等。 为了实…...

浏览器事件循环详解
1. 浏览器的进程模型 1.1. 何为进程? 程序运行需要有它自己的专属内存空间,可以把这块内存空间简单的理解为进程。 每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意。 1.2. 何为线程?…...
Linux:线程管理(线程创建、线程退出、线程回收、线程分离、其它线程函数)
线程管理 (1)What(什么是线程管理) 对程序中线程的创建、调度、同步、退出、回收等操作进行有效的控制和协调 (2)Why(为什么要管理线程) 充分利用系统资源,提高程序的并发的性能和稳定性。但如果管理不当,…...

【JVM】常见面试题
🥰🥰🥰来都来了,不妨点个关注叭! 👉博客主页:欢迎各位大佬!👈 文章目录 1. JVM 中的内存区域划分2. JVM 的类加载机制2.1 加载(Loading)✨双亲委派模型2.2 验证(Verification)2.3 准…...

0805作业+梳理
一、作业: 代码: create.c #include<myhead.h> int main(int argc, const char *argv[]) {//创建一个有名管道文件if(mkfifo("./linux",0664)-1){perror("mkfifo linux error");return -1;}getchar();system("rm linux…...

Java高并发编程详解教程(对高并发更深一层的领悟和体会 电子版)
前言 第一部分主要阐述Thread的基础知识,详细介绍线程的API使用、线程安全、线程间数据通信以及如何保护共享资源等内容,它是深入学习多线程内容的基础。 在第二部分中之所以引人 ClassLoader,是因为 ClassLoader 与线程不无关系࿰…...
字符串中的第一个唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。 s 只包含小写字母 示例 1: 输入: s "leetcode" 输出: 0示例 2: 输入: s "loveleetcode" 输出: 2示例 3: 输…...

leetcode数论(3044. 出现频率最高的质数)
前言 经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。现阶段开始专项练习。 描述 给你一个大小为 m x n 、下标从 0 开始的二维矩阵 mat 。在每个单元格,你可以按以下方式生成数字: 最多有 8 条路径可以选择࿱…...

70.加载功能菜单功能设计
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:易道云信息技术研究院 上一个内容:69.搭建分析工具界面 以 69.搭建分析工具界面 它的代码为基础进行修改 效果图…...

在线Banner设计工具大比拼:谁更胜一筹
在数字营销的时代,一个吸引眼球的 Banner 广告是吸引潜在客户、提高品牌知名度的关键。为了帮助营销人员和设计师快速创建专业的 Banner 广告,市面上出现了多种易于使用的 Banner 设计工具。本文将介绍几个受欢迎的 Banner 设计工具,包括即时…...
C++ STL copy, move 用法
一:功能 正向(从前向后的顺序)拷贝/移动操作,将一个容器元素拷贝/移动到另一容器中。 二:用法 #include <iostream> #include <vector> #include <algorithm>int main() {std::vector<std::str…...

MoonBit 周报 Vol.52:增加类型别名的支持、错误类型声明方式说明、MoonBit AI 支持生成文档等!
weekly 2024-08-05 MoonBit更新 JSON字面量支持array spread。 let xs: Array[json.JsonValue] [1, 2, 3, 4] let _: json.JsonValue [1, ..xs]增加了类型别名的支持,主要是为了渐进式代码重构和迁移,而不是某种给类型简短名字的机制。例如…...

Android开发之事件分发
#来自ウルトラマンゼロ(哉阿斯) 1 Activity 构成 平常布局展示在ContentView中。 2 事件分发 事件分发的本质其实就是把事件(Touch)封装成 MotionEvent 类,然后传递给 View 的层级处理。 MotionEvent 事件类型主要有…...

PyTorch深度学习实战(2)——PyTorch快速入门
PyTorch的简洁设计使得它易于入门,在深入介绍PyTorch之前,本文先介绍一些PyTorch的基础知识,以便读者能够对PyTorch有一个大致的了解,并能够用PyTorch搭建一个简单的神经网络。 1 Tensor Tensor是PyTorch中最重要的数据结构&#…...
ServletConfig、ServletContext超详细讲解
文章目录 前言一、ServletConfig的使用1.ServletConfig定义2.ServletConfig的API3.ServletConfig的测试代码: 二、 ServletContext的使用1.ServletContext定义2.ServletContext如何用3. ServletContext其他重要API 总结 前言 ServletConfig接口代表了Servlet的配置信…...

【文献阅读】GraphAny: A Foundation Model for Node Classification on Any Graph
Abstract 可以执行任何新任务而无需特定训练的基础模型已经在视觉和语言应用中引发了机器学习的革命。然而,涉及图结构数据的应用仍然是基础模型面临的一个难题,因为每个图都有独特的特征和标签空间。传统的图机器学习模型,如图神经网络&…...

动态规划.
目录 (一)递归到动规的一般转化方法 (二)动规解题的一般思路 1. 将原问题分解为子问题 2. 确定状态 3. 确定一些初始状态(边界状态)的值 4. 确定状态转移方程 (三)能用动规解…...
PHP常用函数
字符串 strlen()获取字符串长度strpos()在字符串内查找一个字符或一段指定的文本,返回第一次出现的位置或falsestripos()同上,但不区分大小写strrpos()同上上,返回最后一…...
完全用python 实现消息中间件4
为了进一步完善这个消息中间件,我们可以添加以下功能: 消息确认:客户端可以发送一个确认消息,表明消息已经被正确接收。消息队列:使用一个队列来存储消息,而不是直接存储在字典中。多消费者支持࿱…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...