我的Go矩阵函数(用于OpenGL的透视/截锥和观察矩阵)有什么问题?

huangapple go评论97阅读模式
英文:

What's wrong with my Go matrix functions (Perspective/Frustum & Lookat matrices) for OpenGL?

问题

首先,这是一个非常简单的“新手标准”顶点着色器:

in vec3 aPos;
uniform mat4 uMatModel; uniform mat4 uMatView; uniform mat4 uMatProj;
    
void main () {
    gl_Position = uMatProj * uMatView * uMatModel * vec4(aPos, 1.0);
}

现在我正在渲染一个简单的六面立方体。在36个顶点坐标中没有应用旋转。标准的教程风格,范围在-0.5到+0.5之间。我会省略顶点数组,但请放心,它就是这么简单。

  • uMatModel 目前只是一个单位矩阵,没有缩放/平移/旋转
  • uMatView 是一个LookAt矩阵(下面是Go代码),使用pos={ 0.1, 0.1, -3.0 },target={ 0.1, 0.1, 0.1 },up={ 0, 1, 0 }(记住立方体的顶点坐标在所有维度上都在-0.5和0.5之间,所以0.1应该是“几乎居中”的)
  • uMatProj 是一个透视矩阵(下面是Go代码),使用fov=45 aspect=winwidth/winheight near=0.1 far=100

理论上,“相机”应该在立方体的“后面”约2-3个单位,正对着它。但实际上,我得到的结果是...

我的Go矩阵函数(用于OpenGL的透视/截锥和观察矩阵)有什么问题?

我想知道旋转是从哪里来的...我甚至还没有实现旋转。

所以总结一下,我尝试自己在Go中实现所需的矩阵函数,根据数学计算。但是我一定在某个地方出错了。有人能在下面的代码中发现任何矩阵理论上的问题吗?

type Mat4x4 [4][4]float64

func (me *Mat4x4) Identity () {
    me[0][0], me[0][1], me[0][2], me[0][3] = 1, 0, 0, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = 0, 1, 0, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = 0, 0, 1, 0
    me[3][0], me[3][1], me[3][2], me[3][3] = 0, 0, 0, 1
}

func (me *Mat4x4) Frustum (left, right, bottom, top, near, far float64) {
    me[0][0], me[0][1], me[0][2], me[0][3] = (near * 2) / (right - left), 0, 0, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = 0, (near * 2) / (top - bottom), 0, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1
    me[3][0], me[3][1], me[3][2], me[3][3] = 0, 0, -(far * near * 2) / (far - near), 0
}

func (me *Mat4x4) Perspective (fovY, aspect, near, far float64) {
    var top = near * math.Tan(fovY * math.Pi / 360)
    var right = top * aspect
    me.Frustum(aspect * -top, right, -top, top, near, far)
}

func (me *Mat4x4) LookAt (eyePos, lookTarget, worldUp *Vec3) {
    var vz = eyePos.Sub(lookTarget)
    vz.Normalize()
    var vx = worldUp.Cross(&vz)
    vx.Normalize()
    var vy = vz.Cross(&vx)
    vy.Normalize()
    me[0][0], me[0][1], me[0][2], me[0][3] = vx.X, vy.X, vz.X, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = vx.Y, vy.Y, vz.Y, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = vx.Z, vy.Z, vz.Z, 0
    me[3][0], me[3][1], me[3][2], me[3][3] = -((vx.X * eyePos.X) + (vx.Y * eyePos.Y) + (vx.Z * eyePos.Z)), -((vy.X * eyePos.X) + (vy.Y * eyePos.Y) + (vy.Z * eyePos.Z)), -((vz.X * eyePos.X) + (vz.Y * eyePos.Y) + (vz.Z * eyePos.Z)), 1
}

注意,这里的Vec3是同一个包中的自定义类型,我没有在这里包含它。目前我假设Vec3函数是正确的(也更容易验证),并怀疑我在矩阵结构中某处搞错了LookAt和/或Perspective算法。

英文:

First off, here's the very simplistic "newbie standard" vertex shader:

in vec3 aPos;
uniform mat4 uMatModel; uniform mat4 uMatView; uniform mat4 uMatProj;
    
void main () {
    gl_Position = uMatProj * uMatView * uMatModel * vec4(aPos, 1.0);
}

Now what I'm rendering is a simple 6-faced cube. There is no rotation applied or inherent in the 36 vertex coordinates. Standard tutorial-style -0.5..+0.5 stuff. I'll spare you the vertex array here but rest assured, it's as simple as that.

  • uMatModel is simply the identity matrix for now, no scaling/translating/rotating as-yet
  • uMatView is a LookAt matrix (Go code below) called with pos={ 0.1, 0.1, -3.0 }, target={ 0.1, 0.1, 0.1 }, up={ 0, 1, 0 } (remember the cube vertex coords are all between -0.5 and 0.5 in all dimensions so the 0.1s should be "almost central")
  • uMatProj is a Perspective matrix (Go code below) called with fov=45 aspect=winwidth/winheight near=0.1 far=100

In theory the "camera" should be about 2-3 units "behind" the cube facing it straight. Instead, I get...

我的Go矩阵函数(用于OpenGL的透视/截锥和观察矩阵)有什么问题?

I wonder where the rotation is coming from... I do not even have rotations implemented yet.

So in summary I have tried to implement the required matrix functions myself in Go, working off the maths. But somewhere I must have bugged up. Can anyone spot any matrix-theoretical problems in my below code?

type Mat4x4 [4][4]float64

func (me *Mat4x4) Identity () {
    me[0][0], me[0][1], me[0][2], me[0][3] = 1, 0, 0, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = 0, 1, 0, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = 0, 0, 1, 0
    me[3][0], me[3][1], me[3][2], me[3][3] = 0, 0, 0, 1
}

func (me *Mat4x4) Frustum (left, right, bottom, top, near, far float64) {
    me[0][0], me[0][1], me[0][2], me[0][3] = (near * 2) / (right - left), 0, 0, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = 0, (near * 2) / (top - bottom), 0, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1
    me[3][0], me[3][1], me[3][2], me[3][3] = 0, 0, -(far * near * 2) / (far - near), 0
}

func (me *Mat4x4) Perspective (fovY, aspect, near, far float64) {
    var top = near * math.Tan(fovY * math.Pi / 360)
    var right = top * aspect
    me.Frustum(aspect * -top, right, -top, top, near, far)
}

func (me *Mat4x4) LookAt (eyePos, lookTarget, worldUp *Vec3) {
    var vz = eyePos.Sub(lookTarget)
    vz.Normalize()
    var vx = worldUp.Cross(&vz)
    vx.Normalize()
    var vy = vz.Cross(&vx)
    vy.Normalize()
    me[0][0], me[0][1], me[0][2], me[0][3] = vx.X, vy.X, vz.X, 0
    me[1][0], me[1][1], me[1][2], me[1][3] = vx.Y, vy.Y, vz.Y, 0
    me[2][0], me[2][1], me[2][2], me[2][3] = vx.Z, vy.Z, vz.Z, 0
    me[3][0], me[3][1], me[3][2], me[3][3] = -((vx.X * eyePos.X) + (vx.Y * eyePos.Y) + (vx.Z * eyePos.Z)), -((vy.X * eyePos.X) + (vy.Y * eyePos.Y) + (vy.Z * eyePos.Z)), -((vz.X * eyePos.X) + (vz.Y * eyePos.Y) + (vz.Z * eyePos.Z)), 1
}

Note, Vec3 here is a custom type in the same package, I have not included it here. For now I assume the Vec3 functions are correct (also a lot easier to verify) and suspect that I somehow messed up the LookAt and/or Perspective algorithms in the matrix struct.

答案1

得分: 1

这个(可能是)将角度转换为弧度的转换是否正确?

func (me *Mat4x4) Perspective (fovY, aspect, near, far float64) {
        var top = near * math.Tan(fovY * math.Pi / 360)
        var right = top * aspect
        me.Frustum(aspect * -top, right, -top, top, near, far)
}

也许应该改为:

        top := near * math.Tan(fovY * 2 * math.Pi / 360)
英文:

Is this (presumably) deg to rad conversion okay?

func (me *Mat4x4) Perspective (fovY, aspect, near, far float64) {
        var top = near * math.Tan(fovY * math.Pi / 360)
        var right = top * aspect
        me.Frustum(aspect * -top, right, -top, top, near, far)
}

Maybe there should be:

        top := near * math.Tan(fovY * 2 * math.Pi / 360)

huangapple
  • 本文由 发表于 2012年10月12日 03:29:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/12847094.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定