围绕 x 和 z 轴的 OpenGL 旋转错误。你周围看起来还好吗?
OpenGL rotation wrong around x and z-axis. Around y it looks okay?
我完全被 OpenGL 中的旋转所困(再次)。我不确定它到底是什么,但我有一些理论。
首先让我分享一些代码:
我在这里渲染了三个实体,它们只是在 blender 中创建的 Ico-Spheres 的 .obj 模型。
val testEntity: Test = new Test(new Vector3(-3.0f,0,0), new Vector3(0,0,0), 0.75f)
val testEntity2: Test = new Test(new Vector3(0,0,0), new Vector3(0,0,0), 0.75f)
val testEntity3: Test = new Test(new Vector3(3.0f,0,0), new Vector3(0,0,0), 0.75f)
override def render(): Unit =
{
camera.update()
testEntity.addRotation(new Vector3(1,0,0))
testEntity.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
testEntity2.addRotation(new Vector3(0,1,0))
testEntity2.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
testEntity3.addRotation(new Vector3(0,0,1))
testEntity3.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
}
如您所见,我所做的就是将三个球体并排放置,并尝试围绕一个轴旋转每个球体。
addRotation(new Vector3(1,0,0)) 只是将旋转添加到实体的方向(开始时为 (0,0,0)),因此在这种情况下它在每帧的 x 轴。
理论#1 - 模型矩阵的计算错误
这就是我计算每帧每个实体的模型矩阵的方式
protected def calculateModelMatrix(position: Vector3, orientation: Vector3, scale: Float): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.scale(new Vector3(scale, scale, scale))
matrix.rotate(new Vector3(1,0,0), Math.toRadians(orientation.x).toFloat)
matrix.rotate(new Vector3(0,1,0), Math.toRadians(orientation.y).toFloat)
matrix.rotate(new Vector3(0,0,1), Math.toRadians(orientation.z).toFloat)
matrix.translate(position)
matrix
}
旋转应该总是发生在原点,但恒等矩阵会处理这个问题。
首先我们缩放,然后我们进行旋转,然后将实体平移到正确的位置。
Matrix4-class的rotate
-方法如下所示:
def rotate(axis: Vector3, angle: Float): Unit =
{
if(angle == 0) return
axis match
{
case Vector3(1,0,0) => rotateX(angle)
case Vector3(0,1,0) => rotateY(angle)
case Vector3(0,0,1) => rotateZ(angle)
case _ => //println("Rotation not yet implemented!")
}
}
private def rotateX(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m11 = cos
this.m12 = -sin
this.m21 = sin
this.m22 = cos
}
private def rotateY(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m02 = sin
this.m20 = -sin
this.m22 = cos
}
private def rotateZ(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m01 = -sin
this.m10 = sin
this.m12 = cos
}
对于辅助方法,我严格遵守数学(或至少尝试这样做)(参考:http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/ and https://en.wikipedia.org/wiki/Rotation_matrix)
理论#2 - 投影and/or视图矩阵的计算是错误的
对于我调用的每一帧 camera.update()
。它应该根据相机的位置更新我的视图和投影矩阵:
def update(): Unit =
{
updateViewMatrix()
updateViewProjectionMatrix()
}
private def calculateViewProjectionMatrix(): Matrix4 =
{
Matrix4.multiply(this.projectionMatrix, this.viewMatrix)
}
private def updateViewProjectionMatrix(): Unit =
{
this.viewProjectionMatrix = calculateViewProjectionMatrix()
}
private def calculateProjectionMatrix(): Matrix4 =
{
val aspectRatio: Float = 1024 / 768 // TODO get this from somewhere
// val yScale: Float = ((1.0f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio).toFloat
val yScale: Float = (1.0f / Math.tan(Math.toRadians(FOV / 2f))).toFloat
val xScale: Float = yScale / aspectRatio
val frustumLength = FAR_PLANE - NEAR_PLANE
val matrix: Matrix4 = Matrix4.Zero
matrix.m00 = xScale
matrix.m11 = yScale
matrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustumLength)
matrix.m23 = -1.0f
matrix.m32 = -((2.0f * NEAR_PLANE * FAR_PLANE) / frustumLength)
matrix
}
private def calculateViewMatrix(): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.rotate(new Vector3(1,0,0), Math.toRadians(this.pitch).toFloat)
matrix.rotate(new Vector3(0,1,0), Math.toRadians(this.yaw).toFloat)
// matrix.rotate(new Vector3(0,0,1), Math.toRadians(this.roll).toFloat)
matrix.translate(new Vector3(-this.position.x, -this.position.y, -this.position.z))
matrix
}
结果如下:
基本上绝对垃圾!围绕 y 轴(在中间)的旋转是旋转时唯一不平移的旋转。看起来还是有点 compressed/mashed(或者这只是我?!)还有那个绕 z 旋转的?它完全关闭了!
现在我也尝试从 https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/util/vector/Matrix4f.java
复制 rotate
在代码中看起来像这样:
def rotate(axis: Vector3, angle: Float): Unit =
{
val cosAngle: Float = Math.cos(angle).toFloat
val sinAngle: Float = Math.sin(angle).toFloat
val oneMinusCosAngle: Float = 1.0f - cosAngle
val xy: Float = axis.x * axis.y
val xz: Float = axis.x * axis.z
val yz: Float = axis.y * axis.z
val xs: Float = axis.x * sinAngle
val ys: Float = axis.y * sinAngle
val zs: Float = axis.z * sinAngle
val f00: Float = axis.x * axis.x * oneMinusCosAngle + cosAngle
val f01: Float = xy * oneMinusCosAngle + zs
val f02: Float = xz * oneMinusCosAngle - ys
val f10: Float = xy * oneMinusCosAngle - zs
val f11: Float = axis.y * axis.y * oneMinusCosAngle + cosAngle
val f12: Float = yz * oneMinusCosAngle + xs
val f20: Float = xz * oneMinusCosAngle + ys
val f21: Float = yz * oneMinusCosAngle - xs
val f22: Float = axis.z * axis.z * oneMinusCosAngle + cosAngle
val t00: Float = this.m00 * f00 + this.m10 * f01 + this.m20 * f02
val t01: Float = this.m01 * f00 + this.m11 * f01 + this.m21 * f02
val t02: Float = this.m02 * f00 + this.m12 * f01 + this.m22 * f02
val t03: Float = this.m03 * f00 + this.m13 * f01 + this.m23 * f02
val t10: Float = this.m00 * f10 + this.m10 * f11 + this.m20 * f12
val t11: Float = this.m01 * f10 + this.m11 * f11 + this.m21 * f12
val t12: Float = this.m02 * f10 + this.m12 * f11 + this.m22 * f12
val t13: Float = this.m03 * f10 + this.m13 * f11 + this.m23 * f12
this.m20 = this.m00 * f20 + this.m10 * f21 + this.m20 * f22
this.m21 = this.m01 * f20 + this.m11 * f21 + this.m21 * f22
this.m22 = this.m02 * f20 + this.m12 * f21 + this.m22 * f22
this.m23 = this.m03 * f20 + this.m13 * f21 + this.m23 * f22
this.m00 = t00
this.m01 = t01
this.m02 = t02
this.m03 = t03
this.m10 = t10
this.m11 = t11
this.m12 = t12
this.m13 = t13
}
这是结果:
它看起来比我自己的实现更好,但实体在旋转时仍然在 x 轴和 z 轴上平移。
我不明白为什么会这样,我调试了很长时间,没有任何效果。我做的事情肯定有问题。
后来我想切换到 Quaternions
但我想,如果基本旋转不起作用,那么现在实施四元数并不是一个好主意。
所以,根据@BDL 的说法,有两个错误,我(有点)修复了两个错误:
- 边旋转边翻译-Bug
问题是 .obj 文件没有以原点为中心,所以我在 Blender 的帮助下解决了这个问题。当它居中时,不再发生翻译。愚蠢的错误!
- 实际的旋转错误
lwjgl 实现中没有错误。在我修复 1.
之后一切正常。但我也想修复我自己的实现,所以再次感谢 BDL,我知道我必须乘以我的矩阵。
对于 X 和 Y 这已经有效,对于 Z 不太好,但基本上是我所做的:
protected def calculateModelMatrix(position: Vector3, orientation: Vector3, scale: Float): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.scale(new Vector3(scale, scale, scale))
matrix.rotate(new Vector3(1,0,0), Math.toRadians(orientation.x).toFloat)
val rotXYMatrix: Matrix4 = Matrix4.multiply(Matrix4.rotate(new Vector3(0,1,0), Math.toRadians(orientation.y).toFloat), matrix)
val rotXYZMatrix: Matrix4 = Matrix4.multiply(Matrix4.rotate(new Vector3(0,0,1), Math.toRadians(orientation.z).toFloat), rotXYMatrix)
rotXYZMatrix.translate(position)
rotXYZMatrix
}
不确定为什么它对 z 不起作用,但这是(半)工作结果:
编辑: 现在也修复了:
错误出现在:
private def rotateZ(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m01 = -sin
this.m10 = sin
this.m12 = cos
}
当然不是this.m12 = cos
而是this.m11 = cos
。现在按预期工作!
我完全被 OpenGL 中的旋转所困(再次)。我不确定它到底是什么,但我有一些理论。
首先让我分享一些代码:
我在这里渲染了三个实体,它们只是在 blender 中创建的 Ico-Spheres 的 .obj 模型。
val testEntity: Test = new Test(new Vector3(-3.0f,0,0), new Vector3(0,0,0), 0.75f)
val testEntity2: Test = new Test(new Vector3(0,0,0), new Vector3(0,0,0), 0.75f)
val testEntity3: Test = new Test(new Vector3(3.0f,0,0), new Vector3(0,0,0), 0.75f)
override def render(): Unit =
{
camera.update()
testEntity.addRotation(new Vector3(1,0,0))
testEntity.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
testEntity2.addRotation(new Vector3(0,1,0))
testEntity2.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
testEntity3.addRotation(new Vector3(0,0,1))
testEntity3.render(camera.getViewProjectionMatrix, camera.getViewMatrix)
}
如您所见,我所做的就是将三个球体并排放置,并尝试围绕一个轴旋转每个球体。
addRotation(new Vector3(1,0,0)) 只是将旋转添加到实体的方向(开始时为 (0,0,0)),因此在这种情况下它在每帧的 x 轴。
理论#1 - 模型矩阵的计算错误
这就是我计算每帧每个实体的模型矩阵的方式
protected def calculateModelMatrix(position: Vector3, orientation: Vector3, scale: Float): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.scale(new Vector3(scale, scale, scale))
matrix.rotate(new Vector3(1,0,0), Math.toRadians(orientation.x).toFloat)
matrix.rotate(new Vector3(0,1,0), Math.toRadians(orientation.y).toFloat)
matrix.rotate(new Vector3(0,0,1), Math.toRadians(orientation.z).toFloat)
matrix.translate(position)
matrix
}
旋转应该总是发生在原点,但恒等矩阵会处理这个问题。 首先我们缩放,然后我们进行旋转,然后将实体平移到正确的位置。
Matrix4-class的rotate
-方法如下所示:
def rotate(axis: Vector3, angle: Float): Unit =
{
if(angle == 0) return
axis match
{
case Vector3(1,0,0) => rotateX(angle)
case Vector3(0,1,0) => rotateY(angle)
case Vector3(0,0,1) => rotateZ(angle)
case _ => //println("Rotation not yet implemented!")
}
}
private def rotateX(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m11 = cos
this.m12 = -sin
this.m21 = sin
this.m22 = cos
}
private def rotateY(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m02 = sin
this.m20 = -sin
this.m22 = cos
}
private def rotateZ(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m01 = -sin
this.m10 = sin
this.m12 = cos
}
对于辅助方法,我严格遵守数学(或至少尝试这样做)(参考:http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/ and https://en.wikipedia.org/wiki/Rotation_matrix)
理论#2 - 投影and/or视图矩阵的计算是错误的
对于我调用的每一帧 camera.update()
。它应该根据相机的位置更新我的视图和投影矩阵:
def update(): Unit =
{
updateViewMatrix()
updateViewProjectionMatrix()
}
private def calculateViewProjectionMatrix(): Matrix4 =
{
Matrix4.multiply(this.projectionMatrix, this.viewMatrix)
}
private def updateViewProjectionMatrix(): Unit =
{
this.viewProjectionMatrix = calculateViewProjectionMatrix()
}
private def calculateProjectionMatrix(): Matrix4 =
{
val aspectRatio: Float = 1024 / 768 // TODO get this from somewhere
// val yScale: Float = ((1.0f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio).toFloat
val yScale: Float = (1.0f / Math.tan(Math.toRadians(FOV / 2f))).toFloat
val xScale: Float = yScale / aspectRatio
val frustumLength = FAR_PLANE - NEAR_PLANE
val matrix: Matrix4 = Matrix4.Zero
matrix.m00 = xScale
matrix.m11 = yScale
matrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustumLength)
matrix.m23 = -1.0f
matrix.m32 = -((2.0f * NEAR_PLANE * FAR_PLANE) / frustumLength)
matrix
}
private def calculateViewMatrix(): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.rotate(new Vector3(1,0,0), Math.toRadians(this.pitch).toFloat)
matrix.rotate(new Vector3(0,1,0), Math.toRadians(this.yaw).toFloat)
// matrix.rotate(new Vector3(0,0,1), Math.toRadians(this.roll).toFloat)
matrix.translate(new Vector3(-this.position.x, -this.position.y, -this.position.z))
matrix
}
结果如下:
基本上绝对垃圾!围绕 y 轴(在中间)的旋转是旋转时唯一不平移的旋转。看起来还是有点 compressed/mashed(或者这只是我?!)还有那个绕 z 旋转的?它完全关闭了!
现在我也尝试从 https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/util/vector/Matrix4f.java
复制rotate
在代码中看起来像这样:
def rotate(axis: Vector3, angle: Float): Unit =
{
val cosAngle: Float = Math.cos(angle).toFloat
val sinAngle: Float = Math.sin(angle).toFloat
val oneMinusCosAngle: Float = 1.0f - cosAngle
val xy: Float = axis.x * axis.y
val xz: Float = axis.x * axis.z
val yz: Float = axis.y * axis.z
val xs: Float = axis.x * sinAngle
val ys: Float = axis.y * sinAngle
val zs: Float = axis.z * sinAngle
val f00: Float = axis.x * axis.x * oneMinusCosAngle + cosAngle
val f01: Float = xy * oneMinusCosAngle + zs
val f02: Float = xz * oneMinusCosAngle - ys
val f10: Float = xy * oneMinusCosAngle - zs
val f11: Float = axis.y * axis.y * oneMinusCosAngle + cosAngle
val f12: Float = yz * oneMinusCosAngle + xs
val f20: Float = xz * oneMinusCosAngle + ys
val f21: Float = yz * oneMinusCosAngle - xs
val f22: Float = axis.z * axis.z * oneMinusCosAngle + cosAngle
val t00: Float = this.m00 * f00 + this.m10 * f01 + this.m20 * f02
val t01: Float = this.m01 * f00 + this.m11 * f01 + this.m21 * f02
val t02: Float = this.m02 * f00 + this.m12 * f01 + this.m22 * f02
val t03: Float = this.m03 * f00 + this.m13 * f01 + this.m23 * f02
val t10: Float = this.m00 * f10 + this.m10 * f11 + this.m20 * f12
val t11: Float = this.m01 * f10 + this.m11 * f11 + this.m21 * f12
val t12: Float = this.m02 * f10 + this.m12 * f11 + this.m22 * f12
val t13: Float = this.m03 * f10 + this.m13 * f11 + this.m23 * f12
this.m20 = this.m00 * f20 + this.m10 * f21 + this.m20 * f22
this.m21 = this.m01 * f20 + this.m11 * f21 + this.m21 * f22
this.m22 = this.m02 * f20 + this.m12 * f21 + this.m22 * f22
this.m23 = this.m03 * f20 + this.m13 * f21 + this.m23 * f22
this.m00 = t00
this.m01 = t01
this.m02 = t02
this.m03 = t03
this.m10 = t10
this.m11 = t11
this.m12 = t12
this.m13 = t13
}
这是结果:
它看起来比我自己的实现更好,但实体在旋转时仍然在 x 轴和 z 轴上平移。
我不明白为什么会这样,我调试了很长时间,没有任何效果。我做的事情肯定有问题。
后来我想切换到 Quaternions
但我想,如果基本旋转不起作用,那么现在实施四元数并不是一个好主意。
所以,根据@BDL 的说法,有两个错误,我(有点)修复了两个错误:
- 边旋转边翻译-Bug
问题是 .obj 文件没有以原点为中心,所以我在 Blender 的帮助下解决了这个问题。当它居中时,不再发生翻译。愚蠢的错误!
- 实际的旋转错误
lwjgl 实现中没有错误。在我修复 1.
之后一切正常。但我也想修复我自己的实现,所以再次感谢 BDL,我知道我必须乘以我的矩阵。
对于 X 和 Y 这已经有效,对于 Z 不太好,但基本上是我所做的:
protected def calculateModelMatrix(position: Vector3, orientation: Vector3, scale: Float): Matrix4 =
{
val matrix: Matrix4 = Matrix4.Identity
matrix.scale(new Vector3(scale, scale, scale))
matrix.rotate(new Vector3(1,0,0), Math.toRadians(orientation.x).toFloat)
val rotXYMatrix: Matrix4 = Matrix4.multiply(Matrix4.rotate(new Vector3(0,1,0), Math.toRadians(orientation.y).toFloat), matrix)
val rotXYZMatrix: Matrix4 = Matrix4.multiply(Matrix4.rotate(new Vector3(0,0,1), Math.toRadians(orientation.z).toFloat), rotXYMatrix)
rotXYZMatrix.translate(position)
rotXYZMatrix
}
不确定为什么它对 z 不起作用,但这是(半)工作结果:
编辑: 现在也修复了:
错误出现在:
private def rotateZ(angle: Float): Unit =
{
val cos: Float = Math.cos(angle).toFloat
val sin: Float = Math.sin(angle).toFloat
this.m00 = cos
this.m01 = -sin
this.m10 = sin
this.m12 = cos
}
当然不是this.m12 = cos
而是this.m11 = cos
。现在按预期工作!