OpenGL实现3D游戏编程【连载2】——了解并创建3D空间模型
1、本节实现的内容
上一节我们创建一个简单的窗口,本节我们需要了解一下细节内容,同时为了方便观看,我们需要显示一个世界坐标轴,建立一个直观的三维空间。
2、我们的眼睛设定(gluPerspective函数)
上一节课,我们创建了一个简单的opengl窗口,并显示了一个简单的3d模型正方体,这节我们就要开始了解更多的细节内容。嗯,上一节课中,使用了视角的设置函数perspective,第一个参数表示视场角的大小,符合人眼的条件一般设置为40°~70°之间这个函数。在我理解,有点像是你眼睛展开的角度,比如说10度就有点像你能观测的范围只有10度,就像眯着眼睛看世界,那么他看到的物体就范围比较窄,如果你设置的是60度,那么它光视角就会更大一些,看到的范围更多。这一点可以在我们后期,从三维坐标转换到屏幕的二维坐标中体会到,说当前的这个角度是60度,在坐标转换中一个三维坐标点A如果在当前视角的屏幕以外,那么,它A点与当前屏幕正中间点的视线夹角肯定就超过了60度,我们在后期讲解三维坐标转换时会讲到。
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar)
fovy:视角的大小,相当于眼睛张开的角度。当视角为0度时,这就相当于我们的眼睛闭起来了,那就神马也看不到了。所以选择一个合适的视角尤为重要,一般40度至70度最适合,当然具体情况具体分析。
aspect:宽和高的比例,这里我们直接可以取程序窗口的宽度和高度比。
zNear:观察点与近侧裁剪平面的距离,眼睛距离近处的距离,一般选择0.01。
zFar:观察点与远侧裁剪平面的距离,眼睛远处的裁面,一般为1000,超过这个距离将被裁剪看不到。
zNear和zFar说明了对于绘制的图形,它与视线原点的距离0.01到1000之间必须是在两者之间。
3、我们看向远方的视线(gluLookAt函数)
我们还用到了一个函数gluLookAt,这个函数形象的表述了我们在三维世界里头眼睛所在的位置和朝向的位置,还有一个视角的方向。
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz, GLdouble upx,GLdouble upy,GLdouble upz);
前两个非常的好理解,第一个参数就表示的是你眼睛的位置,第二个参数就表示你要看物体的位置坐标,当然,我们第二个参数可以不确定到具体的物体位置坐标,只要你向前看,视线上的任何一点都可以。第三个参数,要是你视线的正上方向量,根据设定角度不同,可以实现侧着头观察世界的效果。
4、通过我们的眼睛和视线探索世界
通过我们前面讲到的我们的眼睛设定(gluPerspective函数)和我们看向远方的视线(gluLookAt函数),我们就可以描述出我们观察时间的方式。还拿上节课的例子,我们通过gluPerspective将眼睛以45度的角度睁开,我的眼睛位于一个(10.0f,10.0f,10.0f)点的位置,视线朝着原点(0.0f,0.0f,0.0f)点的位置望去,这就是我们以下代码大致实现的内容。
//获取窗口大小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);
说到这里,后期当我们不停地改变眼睛的位置和视线的方向,就产生了视角移动的效果,可以产生人物移动观察世界的效果,类似第一人称射击游戏或RPG游戏人物视角的效果,是不是很有感觉,我们将在后期完善相关代码,随后再详细说。
5、窗口显示比例的锁定
当我们创建了视角以后,我们发现一个问题:当我们鼠标拖动窗口改变大小时,我们刚刚显示的物体竟然变形状了,而且立方体的位置不再显示在窗口的中心。试想以下,我们如果在玩游戏,出现人物如果会随着程序窗口大小的改变而不断地被拉长或压扁,那将是个多么糟糕的事情。因此我们需要另一个函数,来保证所有的物体保持横宽比例。
OpenGL中的glViewport 函数用于定义视口(Viewport),也就是确定窗口中显示的区域。它的定义如下:
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
x:视口的左下角X坐标。
y:视口的左下角Y坐标。
width:视口的宽度。
height:视口的高度。
glViewport函数的作用是将正投影坐标(OpenGL的默认坐标系)映射到屏幕上实际显示的区域。它将正投影坐标系中的点映射到屏幕上指定大小的矩形区域内。这里我们将的glViewport(0,0,tempClientRect.right,tempClientRect.bottom)就时将整个程序窗口平面设定为显示区域,那么配合着gluPerspective中设定的平面横纵比为(float)tempClientRect.right/(float)tempClientRect.bottom,我们就可以保持整个世界的横纵比例保持一致,不至于出现拉长、压扁的情况了。
//获取窗口大小RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);//重置视窗设置glViewport(0,0,tempClientRect.right,tempClientRect.bottom);glMatrixMode(GL_PROJECTION);glLoadIdentity();
在使用OpenGL进行绘图时,我们通常需要先通过glViewport来设置视口,将整个窗口或窗口的一部分作为渲染区域。这样,我们可以指定绘制的内容在窗口的哪个位置显示出来。通常情况下,我们将glViewport放置到窗口改变的消息处理函数中即可,但如果后期需要显示多个glViewport视口,就需要改变glViewport的设置位置。
大家还记不记得魔兽争霸3游戏界面里的人物选中头像界面,在二维平面相框中显示出来三维的头像,就可以个用glViewport实现出来,我们将在后面的程序中实现这一个功能,敬请期待。
6、我们需要一个三维坐标轴
接下来,我们看到的立方体是在一个黑色窗口里面,仿佛悬浮在空中一样,没有参照物,我们要创建一个可以看见的三维坐标轴方便观察。大家需要明确的一点是openGL中世界坐标系(World Coordinates)是右手坐标系,在二维屏幕上,屏幕水平方向是x 轴方向,向右为正,屏幕竖起方向是Y轴方向,向上为正,垂直于屏幕的方向是Z轴方向,从屏幕里往外为正。
我们首先去生成一个坐标轴,我们需要使用的画线操作。OpenGL的绘图的函数很多,OpenGL的绘图必须在在glBegin()和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: 连续填充四边形串
具体的使用方法上图一目了然。这个图我已经收藏了,用的时候超方便。
有了这些方法,我们就可以采用其中的GL_LINES (多组双顶点线段)来画我们的三维坐标轴了。这里我们用红色的线表示x轴,绿色的线表示y轴,蓝色线表示z轴。箭头方向的表示对应轴线的正值方向,另外一边就是负值方向。
//显示坐标轴if(true){//设置线的宽度glLineWidth(3.0f);//显示红色的X坐标轴glColor3f(1.0f,0.0f,0.0f);glBegin(GL_LINES);glVertex3f(-10.0f,0.0f,0.0f);glVertex3f(+10.0f,0.0f,0.0f);glEnd();//显示绿色的Y坐标轴glColor3f(0.0f,1.0f,0.0f);glBegin(GL_LINES);glVertex3f(0.0f,-10.0f,0.0f);glVertex3f(0.0f,+10.0f,0.0f);glEnd();//显示蓝色的Z坐标轴glColor3f(0.0f,0.0f,1.0f); glBegin(GL_LINES);glVertex3f(0.0f,0.0f,-10.0f);glVertex3f(0.0f,0.0f,+10.0f);glEnd();}
添加以上代码后,我们就可以看到一个简陋的坐标轴了。
7、添加坐标轴的方向箭头
我们画出了坐标轴,但是没有方向箭头,总感觉少点什么。我么还是把方向箭头显示出来,这样我们就可以明确的感知到各个方向的正方向和负方向的区别了。
这里我们箭头使用了前面正方体类似的系统函数,系统还提供了类似四面体、正八面体、正十二面体、正二十面体、球体、圆环体、茶壶等多个基础函数,方便用户使用。
//显示一个椎体glutSolidCone(0.5,1.0,30,30);
当然,系统提供的这些函数画出的立体图形只有默认的方向,如果用户想调整立体图形的方向和位置,就要用到转换矩阵相关函数。我们这里简单介绍一下:
void glTranslatef(GLfloat x,GLfloat y,GLfloat z);
函数功能:简单的理解,就是物体的位置移动。参数x,y,z分别指定沿x,y,z轴方向的平移分量。其作用就是将你绘点坐标的原点在当前原点的基础上平移一个(x,y,z)向量。
void glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
函数功能:简单的理解,就物体的旋转。先解释一下旋转方向,做(0,0,0)到(x,y,z)的向量,方向满足右手定则,用右手握住这条向量,大拇指指向向量的正方向,四指环绕的方向就是旋转的方向。以点(0,0,0)到点(x,y,z)为轴,旋转angle角度。
接下来,我们就可以使用glTranslatef和glRotatef函数对圆锥箭头进行位置和角度的调整。在Opengl中存在大量的位置和角度的调整,后期还会详细了解以上操作函数。
//显示坐标轴箭头glPushMatrix();glColor3f(1,0,0);glTranslatef(10,0,0);glRotatef(90,0.0f,1.0f,0.0f);glutSolidCone(0.5,1.0,30,30);glPopMatrix();//显示坐标轴箭头glPushMatrix();glColor3f(0,1,0);glTranslatef(0,10,0);glRotatef(-90,1.0f,0.0f,0.0f);glutSolidCone(0.5,1.0,30,30);glPopMatrix();//显示坐标轴箭头glPushMatrix();glColor3f(0,0,1);glTranslatef(0,0,10);glRotatef(0,1.0f,0.0f,0.0f);glutSolidCone(0.5,1.0,30,30);glPopMatrix();
相关文章:

OpenGL实现3D游戏编程【连载2】——了解并创建3D空间模型
1、本节实现的内容 上一节我们创建一个简单的窗口,本节我们需要了解一下细节内容,同时为了方便观看,我们需要显示一个世界坐标轴,建立一个直观的三维空间。 2、我们的眼睛设定(gluPerspective函数) 上一…...

Java-文件操作和IO
文件介绍 文件本身有多重含义,狭义的文件,特指硬盘上的文件(以及保存文件的目录),广义的文件:计算机上的很多硬件设备,软件资源,在操作系统中,都会被视为是"文件" 文件除了有数据内容之外,还有一部分信息,例如文件名,文件类型,文件大小,这些信息可以称作文件的元信…...

AI智能化赋能电商经济,守护消费净土,基于轻量级YOLOv8n开发构建公共生活景下的超大规模500余种商品商标logo智能化检测识别分析系统
在数字经济浪潮的推动下,全力发展新质生产力已成为当今社会发展的主旋律。各行各业正经历着前所未有的变革,其中,电商行业作为互联网经济的重要组成部分,更是以惊人的速度重塑着商业格局与消费模式。AI智能化技术的深度融合&#…...

C语言菜鸟入门·数据结构·链表超详细解析
目录 1. 单链表 1.1 什么是单链表 1.1.1 不带头节点的单链表 1.1.2 带头结点的单链表 1.2 单链表的插入 1.2.1 按位序插入 (1)带头结点 (2)不带头结点 1.2.2 指定结点的后插操作 1.2.3 指定结点的前插操作 1.3 …...

C# Unity 面向对象补全计划 七大原则 之 依赖倒置原则 (DIP)难度:☆☆ 总结:多抽象,多接口,少耦合
本文仅作学习笔记与交流,不作任何商业用途,作者能力有限,如有不足还请斧正 本系列作为七大原则和设计模式的进阶知识,看不懂没关系 请看专栏:http://t.csdnimg.cn/mIitr,查漏补缺 1.依赖倒置原则 (DIP) 这…...

大模型面试问题
综合基础 1、讲讲制作一个LLM的流程以及各阶段的作用 2、发现模型性能不好,如何从各个阶段去排查问题 查看各阶段中是否有对应训练数据,然后再向下排查。预训练 1、Transfomer模型介绍一下 2、讲讲 Q、K、V 3、Transfomer模型中Encoder输出给Decoder的…...

keeplive配置详解与haproxy配置详解
一、keepalive相关知识 1.1 keepalive介绍 keepalive即LVS集群当中的高可用架构,只是针对调度器的高可用。是高可用的HA架构。 keepalive就是基于VRRP协议来实现LVS高可用的方案。 1、组播地址 224.0.0.18,根据组播地址进行通信,主备之间发…...

vivado里的LUT、LUTRAM、FF、BRAM、DSP、IO、BUFG、MMCM资源介绍
vivado里的LUT、LUTRAM、FF、BRAM、DSP、IO、BUFG、MMCM资源介绍 提示:以下是本篇文章正文内容,写文章实属不易,希望能帮助到各位,转载请附上链接。 vivado实现电路用到的资源类型 LUT(Look-Up Table)&am…...

window关闭端口占用
文章目录 一、打开命令行,输入命令,得到进程号二、找到其端口并杀死该端口总结 一、打开命令行,输入命令,得到进程号 winr打开命令行,输入命令 netstat -ano | findstr 端口号得到端口号 二、找到其端口并杀死该端…...

Java:类和对象
类和对象 类(Class)类的定义 对象(Object)对象的创建 构造方法(Constructor)构造方法的定义 继承(Inheritance)继承的示例 总结示例一设想一个场景:创建一个虚拟动物园一…...

Pandas数据分析案例之用户购买记录分析
文章目录 数据分析之用户购买记录分析第一部分:数据类型处理数据加载观察数据查看数据的数据类型数据中是否存储在缺失值将order_dt转换成时间类型查看数据的统计描述计算所有用户购买商品的平均数量计算所有用户购买商品的平均花费 在源数据中添加一列表示月份:ast…...

串口调试可能遇见的常见问题和排查方法
串口UART作为嵌入式应用和通讯领域中最常用的接口之一,接口协议虽然简单,但在实际应 用中不同设备之间的通讯也会存在各种小问题,下面对使用中各种常见的问题做下总结和梳 理,可作为调试参考。 01串口通信常见问题 串口通信乱码…...

运放学习提纲
目的:给初入硬件的朋友一个系统性学习运放的参考方向,避免像无头苍蝇那般 一:偏置电流 1.1. 为什么是输入偏置电流? 1.2. 什么是输入偏置电流? 1.3. 怎么搜索资料?怎么把 ADI 模型导 入Multisim &#…...

nvidia系列教程-AGX-Orin系统刷机及备份
目录 前言 一、准备工作 二、AGX Orin 系统刷机步骤 三、AGX Orin 系统备份 总结 前言 NVIDIA AGX Orin 是一款高性能的嵌入式计算平台,专为边缘计算和 AI 应用而设计。为了确保系统的稳定性和适应不同的应用场景,用户可能需要对 AGX Orin 进行系统刷…...

将 Mojo 与 Python 结合使用
Mojo 允许您访问整个 Python 生态系统,但环境可能会因 Python 的安装方式而异。花些时间准确了解 Python 中的模块和包的工作原理是值得的,因为有一些复杂情况需要注意。如果您以前在调用 Python 代码时遇到困难,这将帮助您入门。 Python 中的模块和包 让我们从 Python 开始…...

Unrecognized option: --add-opens=java.base/java.lang=ALL-UNNAMED
Unrecognized option: --add-opensjava.base/java.langALL-UNNAMED Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit. Disconnected from server 报错原因:这里我是启动一个SpringBoot项目的时候报这…...

js与ios、安卓原生方法互调。
注意方法名与参数需要与对方约束 1.js调用安卓原生方法 window.android.方法名(要传递的参数) 调用安卓方法并且传递参数过去:window.WebAppInterface.安卓方法("参数") window.安卓暴露的方法function(安卓传递过来的参数){} …...

C++——多态经典案例(二)制作饮品
案例:制作饮品的步骤是差不多一样的,假设都有四步,打开包装Open、煮水Boil、放杯子里面PutInCup、放佐料PutSomething、喝Drink 利用多态,制作茶和咖啡等饮品 分析:定义一个抽象类,纯虚函数包括Open、Boil…...

内网域森林之ProxyNotShell漏洞利用
点击星标,即时接收最新推文 本文选自《内网安全攻防:红队之路》 在渗透测试过程,如果目标环境存在Exchange服务器,我们也需要测试是否存在已知的远程命令执行漏洞,这里首先介绍ProxyNotShell。 ProxyNotShell和之前…...

SpringBoot基础 第一天
SpringBoot配置的文件名是固定的:application.yml application.properties YAML:以数据为中心 比Json xml更适合做配置文件 YAML语法: 1 字面量:普通值(字符串 布尔值 数字) (1) k: v (2) " "不会转义 会转义 2 对象,map(属性和值) (1)…...

【C/C++】C语言和C++实现Stack(栈)对比
我们初步了解了C,也用C语言实现过栈,就我们当前所更新过的有关C学习内容以栈为例子,来简单对比一下C语言和C。 1.C中栈的实现 栈的C语言实现在【数据结构】栈的概念、结构和实现详解-CSDN博客 ,下面是C实现的栈, 在St…...

mysql定时备份脚本
概述 整理Mysql数据库备份脚本,用在生产环境数据库定时备份。 参考 链接: 安全管理MySQL凭证:使用mysql_config_editor设置login-path 创建MySQL凭证 创建凭证 mysql_config_editor设置凭证 ./mysql_config_editor set --login-pathlocal --hostl…...

云原生 (1)
一、实验准备 1,准备一台rhel7的主机,并开启主机的图形。 2,关闭vmware DHCP功能。 3,配置好可用IP。 4,关闭火墙。 二、安装图形化kickstart自动安装脚本的工具 1. 基础配置 yum install system-config-kickstart ——安…...

gitlab-pages创建静态站点
配置.gitlab-ci.yml image: fe-image/node:2.8.2 # 表示使用有 nodejs 环境的 docker,python等也有其他的 docker。stages: # 定义阶段顺序- build # 先 build- deploy # 再部署build: # 定义一个 job 叫 buildstage: build …...

Python爬虫技术 案例集锦
让我们通过几个实际的案例来说明如何使用Python编写网络爬虫。这些案例将涵盖从简单的静态网页爬取到较为复杂的动态网站交互,并且还会涉及到数据清洗、存储和分析的过程。 案例 1: 简单的静态网页爬虫 假设我们需要从一个简单的静态新闻网站上抓取文章标题和链接…...

实战OpenCV之环境安装与配置
OpenCV是什么 OpenCV,英文全称为Open Source Computer Vision Library,是一个开源的计算机视觉和机器学习软件库。它设计用于提供一系列功能强大的算法,以帮助开发者处理图像和视频数据,实现各种视觉任务,包括…...

Android应用开发面试之Jetpack面试题分析汇总
Jetpack作为Android开发的一个重要框架,其相关问题在Android原生开发面试中也非常常见。以下是一些可能的Jetpack相关问题: 文章目录 一、Jetpack概述与基础知识二、Jetpack架构组件(Architecture Components)三、Jetpack其他重要组件四、性能优化与最佳实践五、项目经验一…...

【数据结构】栈的概念、结构和实现详解
本文来介绍一下数据结构中的栈,以及如何用C语言去实现。 1. 栈的概念及结构 栈:一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中元素遵循后进先出…...

LeetCode 每日一题 2024/7/29-2024/8/4
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 7/29 682. 棒球比赛7/30 2961. 双模幂运算7/31 3111. 覆盖所有点的最少矩形数目8/1 LCP 40. 心算挑战8/2 3128. 直角三角形8/3 3143. 正方形中的最多点数8/4 572. 另一棵树…...

Golang死锁vs操作系统死锁
目录 一、死锁 二、Golang死锁场景 2.1 重复上锁 2.2 不会减少的 WaitGroup 2.3 空select 2.4 channel 一、死锁 1.golang中死锁的触发条件: 死锁是当 Goroutine 被阻塞而无法解除阻塞时产生的一种状态。 2.操作系统死锁: 发生死锁时,线…...