背景
我认为我已经掌握了创建OpenGL环境的基础知识。我可以渲染简单的对象,可以处理照明等。我正在尝试创建一个测试环境,我可以像FPS游戏风格一样移动。我希望能够朝着我正在看的方向向前和向后“行走”。
我的其他方法如何完成
我已经开发了一个向前和向后的功能,但旋转相机时遇到了一些困难。我有一些似乎有用的变量。我有cam.lookat,这是我的相机看到的点,cam.position这是我的相机所在的点,距离是两者之间的计算(在我的情况下我保持不变)。
所以我可能会完全错误,但这是我到目前为止所做的:
我在初始加载时调用的setupviewport sub:
Private Sub SetupViewport()
Dim w As Integer = GLcontrol1.Width
Dim h As Integer = GLcontrol1.Height
Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width / CSng(GLcontrol1.Height), 0.1F, 2000.0F)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(0, w, h, 0, -1, 1)
GL.LoadMatrix(perspective1)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadIdentity()
GL.Viewport(0, 0, w, h)
GL.Enable(EnableCap.DepthTest)
GL.DepthFunc(DepthFunction.Less)
End Sub
以下是每帧更改调用的Paint事件:
Private Sub GlControl1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs)
GL.Clear(ClearBufferMask.ColorBufferBit)
GL.Clear(ClearBufferMask.DepthBufferBit)
GL.DepthMask(True)
GL.Enable(EnableCap.DepthTest)
GL.ClearDepth(1.0F)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadIdentity()
Dim lightColor0 As Single() = {light_intensity, light_intensity, light_intensity, 1.0F}
Dim lightPos0 As Single() = {cam.Position.X, cam.Position.Y, cam.Position.Z, 1.0F}
GL.Light(LightName.Light0, LightParameter.Diffuse, lightColor0)
GL.Light(LightName.Light0, LightParameter.Position, lightPos0)
GL.Enable(EnableCap.Light0)
Dim mat_specular As Single() = {1.0F, 1.0F, 1.0F, 1.0F}
Dim mat_shininess As Single() = {50.0F}
GL.Material(MaterialFace.Front, MaterialParameter.Specular, mat_specular)
GL.Material(MaterialFace.Front, MaterialParameter.Shininess, mat_shininess)
draw_extras()
GL.Flush()
GLcontrol1.SwapBuffers()
End Sub
我构建“前进和后退”子的方式如下:
Private Sub move_forward_and_back(ByVal forward As Boolean, ByVal delta As Single)
'change the distance between camera and lookat
'distance = old distance
Dim curdistance As Single = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
'curdistance = distance
Dim deltadistance As Single = 0.1
Dim newdistance As Single = deltadistance
'The formula to use for the new point is Xnew = +/-((X2-X1)/Dold)*Dnew+X2
'This formula results with two possible points because of the +/-
'Both points are calculated and then evaluated
Dim newcamx As Single = 0
Dim newcamy As Single = 0
Dim newcamz As Single = 0
Dim newlookatx As Single = 0
Dim newlookaty As Single = 0
Dim newlookatz As Single = 0
Dim newx1 As Single = (((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
Dim newy1 As Single = (((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
Dim newz1 As Single = (((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
Dim newdistance1 As Single = Math.Sqrt((newx1 - cam.lookat.X) ^ 2 + (newy1 - cam.lookat.Y) ^ 2 + (newz1 - cam.lookat.Z) ^ 2)
Dim newx2 As Single = (-((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
Dim newy2 As Single = (-((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
Dim newz2 As Single = (-((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
Dim newdistance2 As Single = Math.Sqrt((newx2 - cam.lookat.X) ^ 2 + (newy2 - cam.lookat.Y) ^ 2 + (newz2 - cam.lookat.Z) ^ 2)
'The one with the greater distance is considered the one to use for "moving away"
'The one with the less distance is considered the one to use for "moving towards"
If forward = True Then 'use one with less distance
If newdistance1 > newdistance2 Then
newcamx = newx2
newcamy = newy2
newcamz = newz2
Else
newcamx = newx1
newcamy = newy1
newcamz = newz1
End If
Else 'use one with greater distance
If newdistance1 < newdistance2 Then
newcamx = newx2
newcamy = newy2
newcamz = newz2
Else
newcamx = newx1
newcamy = newy1
newcamz = newz1
End If
End If
'newcamx, newcamy, and newcamz are calculated for where the camera needs to move to
'need to move lookat the same distance in the same direction
If forward = True Then
newdistance = curdistance + delta
Else
newdistance = curdistance - delta
End If
newx1 = (((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
newy1 = (((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
newz1 = (((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
newdistance1 = Math.Sqrt((newx1 - cam.lookat.X) ^ 2 + (newy1 - cam.lookat.Y) ^ 2 + (newz1 - cam.lookat.Z) ^ 2)
newx2 = (-((cam.Position.X - cam.lookat.X) / curdistance) * newdistance) + cam.Position.X
newy2 = (-((cam.Position.Y - cam.lookat.Y) / curdistance) * newdistance) + cam.Position.Y
newz2 = (-((cam.Position.Z - cam.lookat.Z) / curdistance) * newdistance) + cam.Position.Z
newdistance2 = Math.Sqrt((newx2 - cam.lookat.X) ^ 2 + (newy2 - cam.lookat.Y) ^ 2 + (newz2 - cam.lookat.Z) ^ 2)
If forward = True Then 'we want the one that is smaller
If newdistance1 < newdistance2 Then
newlookatx = newx1
newlookaty = newy1
newlookatz = newz1
Else
newlookatx = newx2
newlookaty = newy2
newlookatz = newz2
End If
Else
If newdistance1 < newdistance2 Then
newlookatx = newx1
newlookaty = newy1
newlookatz = newz1
Else
newlookatx = newx2
newlookaty = newy2
newlookatz = newz2
End If
End If
'now simply assign values
cam.Position.X = newcamx
cam.Position.Y = newcamy
cam.Position.Z = newcamz
cam.lookat.X = newlookatx
cam.lookat.Y = newlookaty
cam.lookat.Z = newlookatz
newdistance = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
End Sub
我只是根据恒定的距离计算相机位置点并知道相机的外观。这是作为计算而不是矩阵来完成的,这有望成为良好的实践。
现在所有这些工作都很顺利,但我提供它作为我如何处理我的环境的背景
问题
我正在尝试基于两个角度创建旋转相机功能。它应该保持距离和cam.position恒定,同时只改变cam.lookat点。到目前为止我所拥有的:
Private Sub rotate_camera(ByVal deltaangle1 As Single, ByVal deltaangle2 As Single)
Dim curdistance As Single = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
angle1 += deltaangle1
angle2 += deltaangle2
If angle1 >= 360 Then
angle1 = angle1 - 360
End If
If angle2 >= 360 Then
angle2 = angle2 - 360
End If
If angle1 < 0 Then
angle1 = angle1 + 360
End If
If angle2 < 0 Then
angle2 = angle2 + 360
End If
deltax = distance * Sin(deltaangle1 * (PI / 180)) * Cos(deltaangle2 * (PI / 180))
deltay = distance * Sin(deltaangle1 * (PI / 180)) * Sin(deltaangle2 * (PI / 180))
deltaz = distance * Cos(deltaangle1 * (PI / 180))
deltaz = Sqrt((distance ^ 2) - (deltax ^ 2) - (deltay) ^ 2) * zsign
'now simply assign values
cam.lookat.X = cam.lookat.X + deltax
cam.lookat.Y = cam.lookat.Y + deltay
cam.lookat.Z = cam.lookat.Z + deltaz
'distance = Sqrt((cam.Position.X - cam.lookat.X) ^ 2 + (cam.Position.Y - cam.lookat.Y) ^ 2 + (cam.Position.Z - cam.lookat.Z) ^ 2)
End Sub
所以我有三个问题:
我知道这里有很多东西要读,但我希望这些问题是基本的。我提供了足够的细节,因为如果我完全偏离左侧场地,我可以调整其他方法。
每个问题更新并在下面回答
所以感谢你们迄今为止提供的所有帮助!我认为我大多在那里。我仍然有这一行使用最终矩阵:
Dim perspective1 As Matrix4 = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3F, GLcontrol1.Width / CSng(GLcontrol1.Height), 0.1F, 2000.0F)
但我已经使用以下建议更改了cam.getviewmatrix函数:
Public Function GetViewMatrix() As Matrix4
Dim myforwardvector As Vector3
Dim rotational_matrix_y As Matrix3
Dim rotational_matrix_x As Matrix3
Dim rotational_matrix_z As Matrix3
Dim upvector As Vector3
If invert_z = False Then
upvector = Vector3.UnitZ
Else
upvector = -Vector3.UnitZ
End If
rotational_matrix_x.M11 = 1
rotational_matrix_x.M12 = 0
rotational_matrix_x.M13 = 0
rotational_matrix_x.M21 = 0
rotational_matrix_x.M22 = Cos(theida * (PI / 180))
rotational_matrix_x.M23 = -Sin(theida * (PI / 180))
rotational_matrix_x.M31 = 0
rotational_matrix_x.M32 = Sin(theida * (PI / 180))
rotational_matrix_x.M33 = Cos(theida * (PI / 180))
rotational_matrix_y.M11 = Cos(theida * (PI / 180))
rotational_matrix_y.M12 = 0
rotational_matrix_y.M13 = Sin(theida * (PI / 180))
rotational_matrix_y.M21 = 0
rotational_matrix_y.M22 = 1
rotational_matrix_y.M23 = 0
rotational_matrix_y.M31 = -Sin(theida * (PI / 180))
rotational_matrix_y.M32 = 0
rotational_matrix_y.M33 = Cos(theida * (PI / 180))
rotational_matrix_z.M11 = Cos(theida * (PI / 180))
rotational_matrix_z.M12 = -Sin(theida * (PI / 180))
rotational_matrix_z.M13 = 0
rotational_matrix_z.M21 = Sin(theida * (PI / 180))
rotational_matrix_z.M22 = Cos(theida * (PI / 180))
rotational_matrix_z.M23 = 0
rotational_matrix_z.M31 = 0
rotational_matrix_z.M32 = 0
rotational_matrix_z.M33 = 1
Dim rotational_matrix As Matrix3
rotational_matrix = Matrix3.Mult(Matrix3.Mult(rotational_matrix_x, rotational_matrix_y), rotational_matrix_z)
myforwardvector = multiply_matrix3_by_vector3(rotational_matrix, myforwardvector)
lookat = multiply_vector3_by_scalar(myforwardvector, distance)
Return Matrix4.LookAt(Position, lookat, upvector)
End Function
当我更改theida时,环境的加载有效,但我的环境没有任何变化。我调用我的SetupViewPort刷新矩阵并像平常一样重新绘制它。我在矩阵创建中遗漏了什么?
这是当前按住仅增加我的偏航的按钮时发生的事情(只是更改X矩阵)。请记住,一个球体位于(0,0,0):
基于第二个答案的改进
My new ViewMatrix function is as follows:
Dim view_matrix As Matrix4 = Matrix4.LookAt(Position, lookat, up_vector)
'transform to x axis first
view_matrix = view_matrix * Get_Transform_Matrix(-Position.X, 0, 0)
'rotate around x axis
view_matrix = view_matrix * Get_Rotational_Matrix("x", yaw)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.X, 0, 0)
'transform to y axis first
view_matrix = view_matrix * Get_Transform_Matrix(0, -Position.Y, 0)
'rotate around y axis
view_matrix = view_matrix * Get_Rotational_Matrix("y", pitch)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.Y, 0, 0)
'transform to z axis first
view_matrix = view_matrix * Get_Transform_Matrix(0, -Position.Z, 0)
'rotate around z axis
view_matrix = view_matrix * Get_Rotational_Matrix("z", roll)
'trnsform back
view_matrix = view_matrix * Get_Transform_Matrix(Position.Z, 0, 0)
Return view_matrix
我的Get_Rotational_Matrix函数,用于检索用于给定旋转的正确矩阵:
Public Function Get_Rotational_Matrix(ByVal matrix_name As String, ByVal angle As Single) As OpenTK.Matrix4
'yaw = x, pitch = y, z = roll
Dim rotational_matrix_ As Matrix4
Select Case matrix_name
Case "x"
rotational_matrix_.M11 = 1
rotational_matrix_.M12 = 0
rotational_matrix_.M13 = 0
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = 0
rotational_matrix_.M22 = Cos(angle * (PI / 180))
rotational_matrix_.M23 = -Sin(angle * (PI / 180))
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = 0
rotational_matrix_.M32 = Sin(angle * (PI / 180))
rotational_matrix_.M33 = Cos(angle * (PI / 180))
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
Case "y"
rotational_matrix_.M11 = Cos(angle * (PI / 180))
rotational_matrix_.M12 = 0
rotational_matrix_.M13 = Sin(angle * (PI / 180))
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = 0
rotational_matrix_.M22 = 1
rotational_matrix_.M23 = 0
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = -Sin(angle * (PI / 180))
rotational_matrix_.M32 = 0
rotational_matrix_.M33 = Cos(angle * (PI / 180))
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
Case "z"
rotational_matrix_.M11 = Cos(angle * (PI / 180))
rotational_matrix_.M12 = -Sin(angle * (PI / 180))
rotational_matrix_.M13 = 0
rotational_matrix_.M14 = 0
rotational_matrix_.M21 = Sin(angle * (PI / 180))
rotational_matrix_.M22 = Cos(angle * (PI / 180))
rotational_matrix_.M23 = 0
rotational_matrix_.M24 = 0
rotational_matrix_.M31 = 0
rotational_matrix_.M32 = 0
rotational_matrix_.M33 = 1
rotational_matrix_.M34 = 0
rotational_matrix_.M41 = 0
rotational_matrix_.M42 = 0
rotational_matrix_.M43 = 0
rotational_matrix_.M44 = 1
End Select
'
Return rotational_matrix_
End Function
这似乎很棒!最后一个问题,如何将旋转应用到up_vector以便我可以跟踪它?请注意我的新方法永远不会改变它。
答案 0 :(得分:0)
我认为您应该有一个私人会员,您可以在其中存储您的前向媒介。指向相机前方的方向。在开始时,您应该在z方向上查看,例如它可以是myForwardvector = vec3(0,0,1)
。
您还应将距离存储到您瞄准的点。 所以lookAt的位置应该变成:
vec3 position = myForwardVector * radius;
现在,当您想根据输入,鼠标等旋转前向矢量时...
其中theta是左/右(偏航)和Phi上/下(俯仰)旋转时的角度。卷不用于fps。
如果你想使用matrice,你可以从一个小例子开始,然后实现偏航运动。
这就是thoose matrices的样子:
使用中间的那个并用你的偏航角替换θ。
现在每次进行一次动作都会改变你的forwardVector:
myForwardVector = Rotationmatrice * myForwardVector;
myLookAtMatrice = glm::lookat(myCameraPos, myFowardVector * radius, upVector)
希望它有所帮助,
请注意,您将使用相同的方法更新upVector。
答案 1 :(得分:0)
@DraykoonD答案很好。但似乎你对几何学有点困惑。所以,这里有一些解释:
如果您的物品无法移动,您只需要步骤:相机和投影。
转换相机意味着翻译和/或旋转它。这两种运动通常都是由一个唯一的矩阵表示,通常称为“#34; lookAt&#34;矩阵。
如果使用矩阵,那么C= R*T
就是操作(lookAt C = Rotation * Translation,按此顺序,而不是T * R)
<强>翻译强>
虽然T
非常简单,但您可能希望用自己的代码替换矩阵运算。类似的东西:
camera.x = camera.x + deltaX
camera.y = camera.x + deltaY
camera.z = camera.x + deltaZ
请注意,deltas
不是必需的,因为用户可能会立刻在1/2/3轴上移动。
如果您想要在当前方向(目标方向)上移动相机stepSize
,而不是上述方法,那么在该方向上获取单位矢量并计算增量并使用他们和以前一样:
vx = target.x - camera.x
vy = target.y - camera.y
vz = target.x - camera.z
modulus = Sqrt(vx*vx + vy*vy + vz*vz)
' components of the unit vector
dx = vx / modulus
dy = vy / modulus
dz = vz / modulus
' stepSize is a signed value, forward or backwards
deltaX = dx * stepSize
deltaY = dy * stepSize
deltaZ = dz * stepSize
轮换强>
所有的拳头:事物围绕轴旋转一个角度,而不是围绕一个点。
再次阅读最后一句。这意味着你不能简单地给出两个角度并获得旋转,你也需要发生两次旋转的顺序,因为如果你的结果不一样改变顺序。
现在再读一遍。这意味着每个旋转需要一个轴。
人们感到困惑的是轮换需要一个起源。想想你用来绘制圆弧的指南针。
所以:轴,角度和原点。
确定。让我们解释FPS游戏中的旋转。我们将用于旋转相机位置的原点。再说一次,不要把这个称为“旋转点”#34;。
让我们说你的轴X +向右,Y +深入屏幕,Z +向上。这是一个权利处理系统:如果计算与轴对齐的两个单位向量的叉积,则得到第三个单位向量。交叉乘积作为矩阵乘法,不可交换;所以要注意顺序,否则你会在相反的方向上得到一个向量。
如果将此轴系统用作世界坐标系,则该轴系统易于理解。
左侧处理系统是相同的,但更改了一些符号,例如Z= Y x X
,而不是Z= X x Y
如果您的第一次旋转超过Z轴,那么将Wikipedia Rotation中显示的Rz
矩阵应用于当前X向量和Y向量,我们得到新的X&#39;,Y&#39;向量,Z&#39;与Z相同。如果首先相机目标为(0, d, 0)
,则将其后乘Rz
以获得新目标(tx, ty, 0)
&#34;预&#34;或&#34;发布&#34;矩阵乘法取决于矩阵是列还是行mayor顺序。通常在OpenGL中使用许多图形API列mayor顺序,这与Wikipedia链接相同。
现在假设用户想要一个下行轮换。所需的轴是我们在Z轴旋转之前获得的X'
。这是一个&#34;本地&#34;轴。如果我们应用Rx
矩阵,我们会得到局部旋转。对。但我们需要的是一个全球性的&#34;方向,以便对象定位良好。
当矩阵内容闪耀时:只需应用R2= Rx*Rz
而不是R2= Rx
。如果您将R2
应用于(0, d, 0)
,则会获得new2目标(t2x, t2y, t2z)
它很简单,如#34;堆叠&#34;转换:Cn= Cn-1 * ... *C5*C4*C3*C2*C1
其中Ck
是&#39; k&#39;的列 - 市长 - 顺序矩阵。转换(翻译或轮换或其他)。只要注意顺序;行 - 市长顺序反转它。
一旦你有了第一个&#34;相机矩阵&#34;以下矩阵只是堆叠的问题。
注意:翻译不能用3x3矩阵表示。但4x4确实如此。使用4x4矩阵可以堆叠任何变换。
现在用户想要再次旋转Z轴(左右)。嗯,让我看看......当我们进行旋转时,整个轴系统会发生变化。这意味着现在,在前两次旋转之后,Z-局部轴,其中&#34;向上摄像机&#34;向量是,与Z-global不同。但是用户确实需要Z-global轮换,而不是本地轮换(用户想要X-local但是Z-global)
我们之前已经看到每个Ck
矩阵代表一个局部变换。我们现在需要的是在本地系统中表示的Z-全局轴。那就是:Zv= R2 * (0,0,1)
好的,但我只知道Rx,Ry,Rz矩阵,现在Z-global与我的本地轴没有相同。真正。我们需要围绕任何轴旋转矩阵。 This link显示了它。
好的。但是......第一个相机矩阵&#34;是什么?建? This lookAt link显示了它。所需的参数(摄像机位置,目标和摄像机)取决于您最初想要的参数&#34;参见&#34;。
如果你对矩阵的推导感到好奇,我告诉你,变换矩阵行是用旧轴向量表示的新轴向量。并且您可以使用交叉产品获取向量并对其进行标准化。
不可即可。而不是使用堆叠矩阵(我知道它们在由于数值问题导致的许多变换后退化)我想得到&#34; camera.position&#34;,&#34; camera.target&#34;和#34; camera.up&#34; (重要的是:所有这些都在全局坐标中)并且我自己使用lookAt
矩阵,用于每次摄像机移动。然后你必须每次计算这三个向量。
翻译显示在这个答案的开头。您必须翻译camera.position和camera.target。 camera.up是一个向量,而不是一个点;因此,翻译一个向量是没有意义的。
对于旋转调用,我们使用相机的位置作为任何旋转的原点。这意味着在旋转目标变换之前,它将全局坐标转换为原点,然后旋转,然后撤消翻译,如下所示:
target.x = target.x - camera.position.x
target.y = target.y - camera.position.y
target.z = target.z - camera.position.z
target = RotateGeneric(axis, angle, target)
target.x = target.x + camera.position.x
target.y = target.y + camera.position.y
target.z = target.z + camera.position.z
我认为你有一个RotateGeneric
函数很好,它使用了我之前写过的链接中的矩阵。
要旋转上摄像头矢量,您可以使用相同的功能(没有翻译,它是一个矢量,对吧?)
您正在对全球系统进行所有计算。 Z轴旋转很简单。但是X-local-axis,在任何时候......我们如何得到它?嗯,有两种选择:
如果您还围绕Y轴旋转,则过程与X轴相同。
在每次移动时使用矩阵堆栈或lookAt
矩阵,得到的是轴系统(如X和Y轴)与OpenGL设备轴系统对齐。
现在剩下两个工作:投影和处理Z轴方向。
正如你所怀疑的那样,矩阵又来了。
我不会在这里写关于正字法和透视矩阵表示法,这不是问题的主题。
只需指出那些矩阵在适当的位置有一个-1
可以反转Z的符号,所以OpenGL左手设备系统很开心。那么,大多数人总是使用价值并不是绝对必要的;但如果设置了+1
,那么您应该更改深度比较功能,以告诉OpenGL使用右手系统。
堆叠投影矩阵&#34;&#34;相机矩阵M = P * C以及所有。