本文使用glfw库!

一、先摆效果图

二、Unity中的欧拉角

欧拉角有如下两种解释:

1、维基百科上的欧拉角。当认为顺序是YXZ(Yaw->Pitch->Roll)时,是传统的欧拉变换,每次变换都是以物体自己的局部坐标轴进行变换的,可以规避万向节死锁问题。本文后面会给出实现camera的代码。

2、当认为顺序是ZXY(Roll->Pitch->Yaw)的时候,是以惯性坐标系的轴进行变换的。
其中X、Y、Z角分别表示:绕X轴旋转X度,绕Y轴旋转Y度,绕Z轴旋转Z度。

这两种不同的表示,有时候也可以叫做内旋外旋

内旋按照旋转后物体的坐标Y轴旋转;
       外旋按照世界坐标系中的Y轴旋转。

二、Roll、Pitch、Yaw

沿着机身右方轴(Unity中的+X)进行旋转,称为pitch,中文叫俯仰。
       沿着机头上方轴(Unity中的+Y)进行旋转,称为Yaw,中文叫偏航。
       沿着机头前方轴(Unity中的+Z)进行旋转,称为Roll,中文叫桶滚。

三、Unity动画演示欧拉角变换和万向锁

(以下出自AndrewFan的文章)

1、欧拉角变换(外旋)

物体的初始状态图(RGB分别对应X轴、Y轴和Z轴):

       假设我们设定一组欧拉旋转(90,90,90),让unity执行这组欧拉旋转。如下图:
       其实Unity引擎是将这一组欧拉变换分成三组去处理的,每次都是相当于从初始状态重新执行累计欧拉角的旋转。

2、验证欧拉角旋转

① 先执行Z轴旋转90度。


       ② 再执行X轴旋转90度。


       ③ 最后执行Y轴旋转90度。

四、万向节死锁

本质就是在某一步旋转角度a之后(a%90==0),当前的旋转轴和最开始的世界坐标系恰好重合,但不是完全一模一样。这样会导致本来这样转能做出的旋转方式,现在做出的却是另外一种旋转方式了。

正常情况
由于Unity中欧拉旋转的顺规的定义,围绕Z轴的进动最先执行,所以就是说,当先沿着Z轴进行进动时,无论此时的XY是什么值,围绕Z轴的旋转方式始终是桶滚。
然而X、Y轴就不同了。先说X轴,如果Z轴也是保持为0,那么围绕X轴进动,最终的影响是预期的俯仰变化。如下图:

异常情况
然而当Z轴为90度时,围绕X轴的旋转方式变成了偏航。
原因分析:因为Z轴旋转后,物体局部坐标系Y轴的和世界坐标系的X轴重合了,这会导致你怎么转也转不出俯仰这种方式。
结果如下图:

欧拉变换是(90,180,180)时,先绕Z轴旋转180度,再绕X轴旋转90度,最后绕Y轴旋转180度。下图为蜜汁动画,明明是先转Z轴的,不过自己可以拿手机比划一下。

先把手机平放在桌面上,短边平行于你的身子,长边垂直于身子,屏幕朝向天花板。现在将手机绕着短边转90度,现在手机立起来了;紧接着绕长边旋转,你会发现旋转方式只会是滚筒,而本应该是偏航的方式消失了。
原因分析:因为X轴旋转后物体局部坐标系的Z轴和世界坐标系下的Y轴重合了,这会导致你怎么转也转不出偏航这种方式。

结果如下图:

四、万向减少节死锁的方法

1、当然是在openGL里面用内旋的方式实现CS的摄像机(就是每次旋转都是相对物体自己的局部坐标系进行旋转变换),但是这涉及到计算视点的算法,该算法就是计算3D中绕任意轴旋转的旋转矩阵。

2、四元数

本来以为万向节死锁可以用内旋的方式解决,听师兄一言才知道是我太天真了。只有用四元数的时候,万向节死锁的情况才会被避免啦!

五、求解旋转矩阵的算法

1总体思路

把v向量沿n向量的方向旋转θ。思路是将v向量分解成垂直于n向量和平行于n向量,对于和n向量平行的向量部分,旋转不起作用。因此将3D旋转的问题简化为2D空间上自行构造出第三个向量,该向量与之前v向量分解出来垂直于n的向量和n都垂直,即这三个向量之间线性无关,将前两者作为基向量,要求的向量可用基向量线性表示。

2、目标

vR(n,θ)=v′vR(n,\theta )=v^{^{'}}vR(n,θ)=v′,求出旋转矩阵R。

3、符号说明

vvv:初始向量
              v′v^{'}v′:真正在3D空间中旋转后的向量(要求的目标向量)
              v∣∣v_{||}v∣∣​:v向量分解的和n向量平行的向量
              nˉ\bar{n}nˉ:旋转轴(单位向量)
              θ\thetaθ:旋转角度
              v⊤v_{\top }v⊤​:v向量分解的和n向量垂直的向量
              www:同时垂直于v∣∣v_{||}v∣∣​和v⊤v_{\top }v⊤​的向量
              v⊤′v_{\top }^{'}v⊤′​:将v⊤v_{\top }v⊤​在2D空间中旋转θ\thetaθ后的向量

4、 详细推导

v∣∣=(v⋅nˉ)nˉv_{||}=(v\cdot \bar{n})\bar{n}v∣∣​=(v⋅nˉ)nˉ (ps:点乘的几何意义是投影)
v∣∣+v⊤=v⇒v⊤=v−(v⋅nˉ)nˉv_{||}+v_{\top }=v\Rightarrow v_{\top }=v-(v\cdot \bar{n})\bar{n}v∣∣​+v⊤​=v⇒v⊤​=v−(v⋅nˉ)nˉ (ps:可知v⊤v_{\top }v⊤​也是单位向量)
v∣∣+v⊤′=v′v_{||}+v_{\top }^{'}=v^{'}v∣∣​+v⊤′​=v′

w=nˉ×v⊤=nˉ×(v−v∣∣)=nˉ×(v−(v⋅nˉ)nˉ)=nˉ×vw=\bar{n}\times v_{\top }\\=\bar{n}\times (v-v_{||})\\=\bar{n}\times (v-(v\cdot \bar{n})\bar{n})\\=\bar{n}\times vw=nˉ×v⊤​=nˉ×(v−v∣∣​)=nˉ×(v−(v⋅nˉ)nˉ)=nˉ×v (ps:两个平行向量的叉积是零向量)

假设v向量是单位向量,则有w也是单位向量,因为两个相互垂直的单位向量叉积也是单位向量。
故有:∥w∥==∥v⊤′∥==∥v⊤∥\left \| w \right \|==\left \| v_{\top }^{'} \right \|==\left \| v_{\top } \right \|∥w∥==∥∥∥​v⊤′​∥∥∥​==∥v⊤​∥
立即推:v⊤′=cosθ⋅v⊤+sinθ⋅wv_{\top }^{'}=cos\theta \cdot v_{\top }+sin\theta \cdot wv⊤′​=cosθ⋅v⊤​+sinθ⋅w
⇒\Rightarrow⇒v′=v∣∣+v⊤′=v∣∣+cosθ⋅v⊤+sinθ⋅w=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×vv^{'}=v_{||}+v_{\top }^{'}\\=v_{||}+cos\theta \cdot v_{\top }+sin\theta \cdot w\\=(v\cdot \bar{n})\bar{n}+cos\theta \cdot (v-(v\cdot \bar{n})\bar{n})+sin\theta \cdot \bar{n}\times vv′=v∣∣​+v⊤′​=v∣∣​+cosθ⋅v⊤​+sinθ⋅w=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v

1、构造基向量一,令v1=[1,0,0]Tv_{1}=[1,0,0]^{T}v1​=[1,0,0]T,
v1′=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=([100]⋅[nxnynz])[nxnynz]+cosθ⋅([100]−([100]⋅[nxnynz])[nxnynz])+sinθ⋅[nxnynz]×[100]=[nx2nxnynxnz]+cosθ⋅[1−nx21−nxny1−nxnz]+sinθ⋅[0nz−ny]=[nx2+cosθ−cosθ⋅nx2)nxny+cosθ−cosθ⋅nxny+sinθ⋅nznxnz+cosθ−cosθ⋅nxnz−sinθ⋅ny]=[nx2⋅(1−cosθ)+cosθnxny⋅(1−cosθ)+nz⋅sinθnxnz⋅(1−cosθ)−ny⋅sinθ]v_{1}^{'}=(v\cdot \bar{n})\bar{n}+cos\theta \cdot (v-(v\cdot \bar{n})\bar{n})+sin\theta \cdot \bar{n}\times v\\=(\begin{bmatrix} 1\\ 0\\ 0 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix} )\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}+cos\theta \cdot (\begin{bmatrix} 1\\ 0\\ 0 \end{bmatrix}-(\begin{bmatrix} 1\\ 0\\ 0 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})+sin\theta \cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}\times \begin{bmatrix} 1\\ 0\\ 0 \end{bmatrix}\\=\begin{bmatrix} n_{x}^{2}\\ n_{x}n_{y}\\ n_{x}n_{z} \end{bmatrix}+cos\theta \cdot \begin{bmatrix} 1-n_{x}^{2}\\ 1-n_{x}n_{y}\\ 1-n_{x}n_{z} \end{bmatrix}+sin\theta \cdot \begin{bmatrix} 0\\ n_{z}\\ -n_{y} \end{bmatrix}\\=\begin{bmatrix} n_{x}^{2}+cos\theta -cos\theta \cdot n_{x}^{2})\\ n_{x}n_{y}+cos\theta -cos\theta \cdot n_{x}n_{y}+sin\theta \cdot n_{z}\\ n_{x}n_{z}+cos\theta -cos\theta \cdot n_{x}n_{z}-sin\theta \cdot n_{y} \end{bmatrix}\\=\begin{bmatrix} n_{x}^{2}\cdot (1-cos\theta )+cos\theta \\ n_{x}n_{y}\cdot (1-cos\theta )+n_{z}\cdot sin\theta \\ n_{x}n_{z}\cdot (1-cos\theta )-n_{y}\cdot sin\theta \end{bmatrix}v1′​=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=(⎣⎡​100​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​+cosθ⋅(⎣⎡​100​⎦⎤​−(⎣⎡​100​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​)+sinθ⋅⎣⎡​nx​ny​nz​​⎦⎤​×⎣⎡​100​⎦⎤​=⎣⎡​nx2​nx​ny​nx​nz​​⎦⎤​+cosθ⋅⎣⎡​1−nx2​1−nx​ny​1−nx​nz​​⎦⎤​+sinθ⋅⎣⎡​0nz​−ny​​⎦⎤​=⎣⎡​nx2​+cosθ−cosθ⋅nx2​)nx​ny​+cosθ−cosθ⋅nx​ny​+sinθ⋅nz​nx​nz​+cosθ−cosθ⋅nx​nz​−sinθ⋅ny​​⎦⎤​=⎣⎡​nx2​⋅(1−cosθ)+cosθnx​ny​⋅(1−cosθ)+nz​⋅sinθnx​nz​⋅(1−cosθ)−ny​⋅sinθ​⎦⎤​

2、构造基向量二,令v2=[0,1,0]Tv_{2}=[0,1,0]^{T}v2​=[0,1,0]T,
v2′=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=([010]⋅[nxnynz])[nxnynz]+cosθ⋅([010]−([010]⋅[nxnynz])[nxnynz])+sinθ⋅[nxnynz]×[010]=[nxnyny2nynz]+cosθ⋅[−nxny1−ny2−nynz]+sinθ⋅[−nz0nx]=[nxny−cosθ⋅nxny−sinθ⋅nzny2+cosθ−cosθ⋅ny2nynz−cosθ⋅nynz+sinθ⋅nx]=[nxny⋅(1−cosθ)−nz⋅sinθny2⋅(1−cosθ)+cosθnynz⋅(1−cosθ)+nx⋅sinθ]v_{2}^{'}=(v\cdot \bar{n})\bar{n}+cos\theta \cdot (v-(v\cdot \bar{n})\bar{n})+sin\theta \cdot \bar{n}\times v\\=(\begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix} )\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}+cos\theta \cdot (\begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix}-(\begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})+sin\theta \cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}\times \begin{bmatrix} 0\\ 1\\ 0 \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{y}\\ n_{y}^{2}\\ n_{y}n_{z} \end{bmatrix}+cos\theta \cdot \begin{bmatrix} -n_{x}n_{y}\\ 1-n_{y}^{2}\\ -n_{y}n_{z} \end{bmatrix}+sin\theta \cdot \begin{bmatrix} -n_{z}\\ 0\\ n_{x} \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{y}-cos\theta \cdot n_{x}n_{y}-sin\theta \cdot n_{z}\\ n_{y}^{2}+cos\theta -cos\theta \cdot n_{y}^{2}\\ n_{y}n_{z}-cos\theta \cdot n_{y}n_{z}+sin\theta \cdot n_{x} \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{y}\cdot (1-cos\theta )-n_{z} \cdot sin\theta\\ n_{y}^{2}\cdot (1-cos\theta )+cos\theta \\ n_{y}n_{z}\cdot (1-cos\theta )+ n_{x} \cdot sin\theta \end{bmatrix}v2′​=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=(⎣⎡​010​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​+cosθ⋅(⎣⎡​010​⎦⎤​−(⎣⎡​010​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​)+sinθ⋅⎣⎡​nx​ny​nz​​⎦⎤​×⎣⎡​010​⎦⎤​=⎣⎡​nx​ny​ny2​ny​nz​​⎦⎤​+cosθ⋅⎣⎡​−nx​ny​1−ny2​−ny​nz​​⎦⎤​+sinθ⋅⎣⎡​−nz​0nx​​⎦⎤​=⎣⎡​nx​ny​−cosθ⋅nx​ny​−sinθ⋅nz​ny2​+cosθ−cosθ⋅ny2​ny​nz​−cosθ⋅ny​nz​+sinθ⋅nx​​⎦⎤​=⎣⎡​nx​ny​⋅(1−cosθ)−nz​⋅sinθny2​⋅(1−cosθ)+cosθny​nz​⋅(1−cosθ)+nx​⋅sinθ​⎦⎤​

3、构造基向量三,令v3=[0,0,1]Tv_{3}=[0,0,1]^{T}v3​=[0,0,1]T,
v3′=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=([001]⋅[nxnynz])[nxnynz]+cosθ⋅([001]−([001]⋅[nxnynz])[nxnynz])+sinθ⋅[nxnynz]×[001]=[nxnznynznz2]+cosθ⋅[−nxnz−nynz1−nz2]+sinθ⋅[ny−nx0]=[nxnz−cosθ⋅nxnz+sinθ⋅nynynz−cosθ⋅nynz−sinθ⋅nxnz2+cosθ−cosθ⋅nz2]=[nxnz⋅(1−cosθ)+ny⋅sinθnynz⋅(1−cosθ)−nx⋅sinθnz2⋅(1−cosθ)+cosθ]v_{3}^{'}=(v\cdot \bar{n})\bar{n}+cos\theta \cdot (v-(v\cdot \bar{n})\bar{n})+sin\theta \cdot \bar{n}\times v\\=(\begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix} )\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}+cos\theta \cdot (\begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}-(\begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}\cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})\begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix})+sin\theta \cdot \begin{bmatrix} n_{x}\\ n_{y}\\ n_{z} \end{bmatrix}\times \begin{bmatrix} 0\\ 0\\ 1 \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{z}\\ n_{y}n_{z}\\ n_{z}^{2} \end{bmatrix}+cos\theta \cdot \begin{bmatrix} -n_{x}n_{z}\\ -n_{y}n_{z}\\ 1-n_{z}^{2} \end{bmatrix}+sin\theta \cdot \begin{bmatrix} n_{y}\\ -n_{x}\\ 0 \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{z}-cos\theta \cdot n_{x}n_{z}+sin\theta \cdot n_{y}\\ n_{y}n_{z}-cos\theta \cdot n_{y}n_{z}-sin\theta \cdot n_{x}\\ n_{z}^{2}+cos\theta -cos\theta \cdot n_{z}^{2} \end{bmatrix}\\=\begin{bmatrix} n_{x}n_{z}\cdot (1-cos\theta )+n_{y} \cdot sin\theta\\ n_{y}n_{z}\cdot (1-cos\theta )-n_{x} \cdot sin\theta\\ n_{z}^{2}\cdot (1-cos\theta )+cos\theta \end{bmatrix}v3′​=(v⋅nˉ)nˉ+cosθ⋅(v−(v⋅nˉ)nˉ)+sinθ⋅nˉ×v=(⎣⎡​001​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​+cosθ⋅(⎣⎡​001​⎦⎤​−(⎣⎡​001​⎦⎤​⋅⎣⎡​nx​ny​nz​​⎦⎤​)⎣⎡​nx​ny​nz​​⎦⎤​)+sinθ⋅⎣⎡​nx​ny​nz​​⎦⎤​×⎣⎡​001​⎦⎤​=⎣⎡​nx​nz​ny​nz​nz2​​⎦⎤​+cosθ⋅⎣⎡​−nx​nz​−ny​nz​1−nz2​​⎦⎤​+sinθ⋅⎣⎡​ny​−nx​0​⎦⎤​=⎣⎡​nx​nz​−cosθ⋅nx​nz​+sinθ⋅ny​ny​nz​−cosθ⋅ny​nz​−sinθ⋅nx​nz2​+cosθ−cosθ⋅nz2​​⎦⎤​=⎣⎡​nx​nz​⋅(1−cosθ)+ny​⋅sinθny​nz​⋅(1−cosθ)−nx​⋅sinθnz2​⋅(1−cosθ)+cosθ​⎦⎤​

4、用这些基向量构造矩阵,R(n,θ)=[v1′Tv2′Tv3′T]=[nx2⋅(1−cosθ)+cosθnxny⋅(1−cosθ)+nz⋅sinθnxnz⋅(1−cosθ)−ny⋅sinθnxny⋅(1−cosθ)−nz⋅sinθny2⋅(1−cosθ)+cosθnynz⋅(1−cosθ)+nx⋅sinθnxnz⋅(1−cosθ)+ny⋅sinθnynz⋅(1−cosθ)−nx⋅sinθnz2⋅(1−cosθ)+cosθ]R(n,\theta )=\begin{bmatrix} v_{1}^{'T}\\ v_{2}^{'T}\\ v_{3}^{'T} \end{bmatrix}=\begin{bmatrix} n_{x}^{2}\cdot (1-cos\theta )+cos\theta & n_{x}n_{y}\cdot (1-cos\theta )+n_{z} \cdot sin\theta &n_{x}n_{z}\cdot (1-cos\theta )-n_{y} \cdot sin\theta \\ n_{x}n_{y}\cdot (1-cos\theta )-n_{z}\cdot sin\theta & n_{y}^{2}\cdot (1-cos\theta )+cos\theta & n_{y}n_{z}\cdot (1-cos\theta )+n_{x} \cdot sin\theta\\ n_{x}n_{z}\cdot (1-cos\theta )+n_{y}\cdot sin\theta & n_{y}n_{z}\cdot (1-cos\theta )- n_{x} \cdot sin\theta & n_{z}^{2}\cdot (1-cos\theta )+cos\theta \end{bmatrix}R(n,θ)=⎣⎡​v1′T​v2′T​v3′T​​⎦⎤​=⎣⎡​nx2​⋅(1−cosθ)+cosθnx​ny​⋅(1−cosθ)−nz​⋅sinθnx​nz​⋅(1−cosθ)+ny​⋅sinθ​nx​ny​⋅(1−cosθ)+nz​⋅sinθny2​⋅(1−cosθ)+cosθny​nz​⋅(1−cosθ)−nx​⋅sinθ​nx​nz​⋅(1−cosθ)−ny​⋅sinθny​nz​⋅(1−cosθ)+nx​⋅sinθnz2​⋅(1−cosθ)+cosθ​⎦⎤​

六、代码实现CS摄像机

摄像机代码块:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>class nCamera
{protected:void RotateView(float angle, float x, float y, float z);
public:glm::mat4 ViewMatrix;nCamera();glm::vec3 mPos,mViewCenter,mUp;bool mbMoveLeft,mbMoveRight,mbMoveForward,mbMoveBackward;void Yaw(float angle);void Pitch(float angle);void Update(float deltaTime);
};
#include "ncamera.h"nCamera::nCamera() :mPos(-4.0f, 2.0f, -1.0f),mViewCenter(0.0f, 0.0f, -1.0f),mUp(0.0f, 1.0f, 0.0f)
{mbMoveLeft=false;mbMoveRight=false;mbMoveForward=false;mbMoveBackward=false;
}void nCamera::RotateView(float angle, float x, float y, float z)
{//算法实现glm::vec3 viewDirection=mViewCenter-mPos;glm::vec3 newDirection;float C=cosf(angle);float S=sinf(angle);glm::vec3 tempX(C+x*x*(1-C),x*y*(1-C)+z*S,x*z*(1-C)-y*S);newDirection.x = glm::dot(tempX,viewDirection);glm::vec3  tempY(x*y*(1-C)-z*S, C+y*y*(1-C), y*z*(1-C)+x*S);newDirection.y = glm::dot(tempY,viewDirection);glm::vec3  tempZ(x*z*(1 - C) +y*S,y*z*(1 - C)-x*S, C + z*z*(1-C));newDirection.z = glm::dot(tempZ,viewDirection);mViewCenter=newDirection+mPos;
}void nCamera::Yaw(float angle)
{RotateView(angle,mUp.x,mUp.y,mUp.z);
}void nCamera::Pitch(float angle)
{glm::vec3 viewDirection=mViewCenter-mPos;glm::normalize(viewDirection);glm::vec3 rightDirection=glm::cross(viewDirection,mUp);glm::normalize(rightDirection);RotateView(angle,rightDirection.x,rightDirection.y,rightDirection.z);
}void nCamera::Update(float deltaTime)
{float moveSpeed = 10.0f;float rotateSpeed = 1.0f;if (mbMoveLeft){glm::vec3 viewDirection = mViewCenter - mPos;glm::normalize(viewDirection);glm::vec3 rightDirection = glm::cross(viewDirection,mUp);glm::normalize(rightDirection);mPos = mPos + rightDirection*moveSpeed*deltaTime*-1.0f;mViewCenter = mViewCenter + rightDirection*moveSpeed*deltaTime*-1.0f;}if (mbMoveRight){glm::vec3 viewDirection = mViewCenter - mPos;glm::normalize(viewDirection);glm::vec3 rightDirection = glm::cross(viewDirection,mUp);glm::normalize(rightDirection);mPos = mPos + rightDirection*moveSpeed*deltaTime;mViewCenter = mViewCenter + rightDirection*moveSpeed*deltaTime;}if (mbMoveForward){glm::vec3 forwardDirection=mViewCenter-mPos;glm::normalize(forwardDirection);mPos = mPos + forwardDirection*moveSpeed*deltaTime;mViewCenter = mViewCenter + forwardDirection*moveSpeed*deltaTime;}if (mbMoveBackward){glm::vec3 backwardDirection= mPos - mViewCenter;glm::normalize(backwardDirection);mPos = mPos + backwardDirection*moveSpeed*deltaTime;mViewCenter = mViewCenter + backwardDirection*moveSpeed*deltaTime;}ViewMatrix=glm::lookAt(mPos,mViewCenter,mUp);
}

windows窗体交互代码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <string>
#include <vector>
#include <iostream>
#include "Shader.h"
#include "Camera.h"
#include "ncamera.h"
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
nCamera ncamera;
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
glm::dvec2 originalPos(0.0f,0.0f);
glm::dvec2 currentPos(0.0f,0.0f);
bool bRotateView = false;int main()
{//row_num表示网格行数,col_num表示网格列数int row_num=20,col_num=20;std::vector<float> p;float x=-20,z=-20;for(int i=0;i<row_num;i++) {x=-20;for(int j=0;j<col_num;j++) {p.push_back(x);p.push_back(0);p.push_back(z);x+=1;}z+=1;}std::vector<unsigned int> indicess;for(int i=1;i<row_num;i++) {for(int j=1;j<col_num;j++) {indicess.push_back((i-1)*col_num+j-1);indicess.push_back((i-1)*col_num+j);indicess.push_back(i*col_num+j-1);indicess.push_back(i*col_num+j-1);indicess.push_back((i-1)*col_num+j);indicess.push_back(i*col_num+j);}}glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endifGLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);//这个回调函数是重新设置窗口大小的glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//这个回调函数是设置鼠标光标移动事件的glfwSetCursorPosCallback(window, mouse_callback);//这个回调函数是设置鼠标滚轮事件的glfwSetScrollCallback(window, scroll_callback);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}    int nrAttributes;glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;unsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, p.size()*sizeof(float), &p[0], GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicess.size()*sizeof(unsigned int), &indicess[0], GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); Shader shader("normal.vs","normal.fs");shader.use();glEnable(GL_DEPTH_TEST);glm::mat4 Model,View,Projection;while (!glfwWindowShouldClose(window)){float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;processInput(window);glClearColor(0.2f, 0.3f, 0.6f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);             //每一帧绘制前要清除深度缓冲区,否则下一次刷新后会覆盖之前的Projection=glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);shader.use();shader.setMat4("projection", Projection);shader.setMat4("view", View);//每次render更新摄像机 ncamera.Update(deltaTime);//视图矩阵设置为CS摄像机的变量 View=ncamera.ViewMatrix;glBindVertexArray(VAO);Model=glm::mat4(1.0f);Model=glm::translate(Model, glm::vec3(0.0f, 0.0f, -2.0f)); shader.setMat4("model", Model);//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);      //线框模式 glDrawElements(GL_TRIANGLES,(row_num-1)*(col_num-1)*6, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window);glfwPollEvents();}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glfwTerminate();return 0;
}void processInput(GLFWwindow *window)
{if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)              //按下键盘W键ncamera.mbMoveForward=true;if (glfwGetKey(window, GLFW_KEY_W) == GLFW_RELEASE)               //松开键盘W键ncamera.mbMoveForward=false;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)                //按下键盘S键ncamera.mbMoveBackward=true;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_RELEASE)              //松开键盘S键ncamera.mbMoveBackward=false;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)               //按下键盘A键ncamera.mbMoveLeft=true;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_RELEASE)              //松开键盘A键ncamera.mbMoveLeft=false;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)               //按下键盘D键ncamera.mbMoveRight=true;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_RELEASE)             //松开键盘D键ncamera.mbMoveRight=false;if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)  //按下鼠标右键{//按下右键的时候记录当前位置 glfwGetCursorPos(window,&originalPos.x,&originalPos.y);//隐藏鼠标光标 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);//设置为可旋转,当鼠标光标移动时候回调,执行旋转操作 bRotateView = true;}if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_RELEASE)  //松开鼠标右键{//右键松开时候,鼠标光标再现 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); bRotateView = false;}
}void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if(bRotateView){//将当前的窗口坐标存到向量里面 glfwGetCursorPos(window,&currentPos.x,&currentPos.y);double deltaX = currentPos.x - originalPos.x;double deltaY = currentPos.y - originalPos.y;//将偏移量除以1000,因为捕捉到的坐标都比较大,都是上百的 float angleRotatedByRight = (float)deltaY / 1000.0f;float angleRotatedByUp = (float)deltaX / 1000.0f;ncamera.Yaw(-angleRotatedByUp);ncamera.Pitch(-angleRotatedByRight);//为了使鼠标右击按下拖动视角到松开右键时,鼠标光标位置仍在右击按下的位置glfwSetCursorPos(window,originalPos.x,originalPos.y);}
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{//按照鼠标滚轮的两种不同滚动方式判断 if(yoffset>0)ncamera.mbMoveForward=true;else ncamera.mbMoveBackward=true;ncamera.Update(deltaTime);
}

shader生成跳棋棋盘(mesh+简单的异或算法):

顶点着色器

#version 330 core
layout (location = 0) in vec3 position;uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
out vec2 coord;
out vec3 n_p;void main()
{vec3 new_pos=position;gl_Position = projection*view*model*vec4(new_pos, 1.0f);n_p=new_pos;
}

片段着色器

GLSL里面不存在隐式转换需要将int强转成bool。算法思路就是X和Z坐标里面只有一个是偶数的时候,将这个格子渲染成黑色;同奇同偶的时候,将这个格子渲染成白色。

#version 330 core
out vec4 FragColor;in vec3 n_p;void main()
{   int x=int(n_p.x),z=int(n_p.z);if(bool((x%2)^(z%2)))//if(x%2==1&&z%2==0)FragColor=vec4(0.0f,0.0f,0.0f,1.0f);else FragColor=vec4(1.0f,1.0f,1.0f,1.0f);}

参考文章: https://blog.csdn.net/AndrewFan/article/details/60981437

参考书籍: 3DMathPrimerForGraphicsAndGameDevelopment

CG的下一篇打算写粒子系统,至于四元数有空再学习,学明白了就会写博客。希望大家支持!

万向节死锁的理解与CS摄像机减少死锁的简单实现相关推荐

  1. 哲学家吃饭问题-对线程死锁的理解

    哲学家就餐问题-对线程死锁的理解 两个线程的死锁问题: 线程1 首先占有对象1,接着试图占有对象2 线程2 首先占有对象2,接着试图占有对象1 线程1 等待线程2释放对象2 与此同时,线程2等待线程1 ...

  2. MySQL笔记-死锁原理与分析及InnoDB中如何减少死锁

    根据InnoDB的加锁规则(Record Lock.Gap Lock.meta data lock)可以写出不会发生死锁的SQL语句,也能定位出产生死锁的原因. 死锁产生的原因: 产生回路:两个或两个 ...

  3. 什么是死锁(死锁的理解)

    多线程和锁 多线程和锁作为并发编程的两个重要概念,在提升了程序性能的同时,也带来了一些编码的复杂性.锁的出现就是为了保证在多线程的时候操作一组资源数据的一致性,我们在给资源加上锁之后,只有拥有了这个锁 ...

  4. 浅析java中的死锁_Java学习笔记五十五(死锁问题)

    多线程死锁问题. 我们知道,多线程可以改善系统的资源利用率,并且可以提高程序的运行效率.但是,多线程也带来了新的问题,即:死锁问题. 1.死锁的概念 死锁可以理解为多个线程为了争夺同一个资源,而出现互 ...

  5. sql server死锁_了解SQL Server中的死锁图的XML描述

    sql server死锁 介绍 (Introduction) 在我的前两篇文章" What is a SQL Server Deadlock and 什么是SQL Server死锁" ...

  6. mysql死锁检测算法_MySQL InnoDB如何应付死锁

    死锁是事务处理型数据库系统的一个经典问题,但是它们并不是很危险的, 除非它们如此地频繁以至于你根本处理不了几个事务. 当因死锁而产生了回滚时,你通常可以在你的应用程序中重新发出一个事务即可. Inno ...

  7. DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2

    本文介绍使用Windbg去验证<DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子>中的结论,调试对象是文中刚开始那个例子.(转载请指明出于breakso ...

  8. DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子

    有了前面两节的基础,我们现在切入正题:研究下DllMain为什么会因为不当操作导致死锁的问题.首先我们看一段比较经典的"DllMain中死锁"代码.(转载请指明出于breaksof ...

  9. 死锁是什么?死锁产生的条件?如何避免死锁?以及死锁的示例代码(Java代码)

    文章目录 一.什么是死锁? 二.产生死锁的条件? 三.产生死锁的示例代码(java) 四.如何避免死锁? 一.什么是死锁? 下面图片参考 JavaGuide中的内容: 死锁的概念: 死锁:指的是相互两 ...

最新文章

  1. 思维dp ---- K步最短路 D. Explorer Space
  2. 2021人工神经网络第二次作业要求
  3. vim中使用sed去除网上copy的源代码行号和空格
  4. #论文 《ImageNet Classification with Deep Convolutional Neural Networks》
  5. C 语言资源大全中文版
  6. g4900 win7 核显驱动_9代酷睿跑WIN7 你要的主板来了
  7. Ubuntu Server搭建FTP服务器(2) --本地用户FTP服务器架设
  8. QT之QHash简介
  9. 使用Okta的单点登录保护您的Vert.x服务器
  10. linux-LINUX试题
  11. 给用户配置hadoop权限
  12. 蓝桥杯题目常用API (JAVA)
  13. 我的世界Java种子大全_种子(世界生成)
  14. 高速局域网文件传输工具(速度可达20M) 的企业云盘
  15. python中shift函数_Python numpy.left_shift函数方法的使用
  16. 华为运营商级路由器配置示例 | 公网IPv6 over SRv6 TE Policy
  17. 基于C语言的信息管理系统和小游戏
  18. VBS 请求WebAPI接口_从零开始实现简单的webapi框架【Golang 入门系列十一】
  19. 基于心理旋转的视线诱导设施优化及应用案例分析
  20. 脚手架开发(2)-注册阶段

热门文章

  1. java开发工具还有那些?
  2. CNN结构的演变+几种典型的网络介绍+CNN设计准则
  3. 循环渐进NsDoor(七)
  4. 知识图谱技术发展详解(一)
  5. 前端----HTML/CSS 单边框
  6. 计算机软件工程考研考哪些专业,2022考研:软件工程考研考什么科目?
  7. CF221C Circling Round Treasures
  8. 第一课:Mstar-Non-OS方案(一)——搭建编译环境
  9. Shell文件查找之find命令(1)
  10. 实验室暑期CTF训练赛--第三、四周