【问题标题】:OpenGL Java Matrix4f implementationOpenGL Java Matrix4f 实现
【发布时间】:2016-07-09 13:46:38
【问题描述】:

我正在尝试编写一个 4*4 浮点矩阵类来创建一个包含模型、视图和投影矩阵的 3D 空间。在当前状态下,当我尝试旋转视图矩阵时,它似乎也应用了平移,并且空间被扭曲(好像被挤压了)。投影、视图和模型矩阵的乘法是在顶点着色器中完成的。

Edit5:转换函数的非工作状态如下:

public class Mat4f {
public float m00, m10, m20, m30,
             m01, m11, m21, m31,
             m02, m12, m22, m32,
             m03, m13, m23, m33;

public Mat4f() {
    loadIdentity();
}

public Mat4f loadIdentity() {
    m00 = 1.0f; m10 = 0.0f; m20 = 0.0f; m30 = 0.0f;
    m01 = 0.0f; m11 = 1.0f; m21 = 0.0f; m31 = 0.0f;
    m02 = 0.0f; m12 = 0.0f; m22 = 1.0f; m32 = 0.0f;
    m03 = 0.0f; m13 = 0.0f; m23 = 0.0f; m33 = 1.0f;
    return this;
}

public Mat4f store(FloatBuffer buffer) {
    buffer.put(m00);
    buffer.put(m01);
    buffer.put(m02);
    buffer.put(m03);

    buffer.put(m10);
    buffer.put(m11);
    buffer.put(m12);
    buffer.put(m13);

    buffer.put(m20);
    buffer.put(m21);
    buffer.put(m22);
    buffer.put(m23);

    buffer.put(m30);
    buffer.put(m31);
    buffer.put(m32);
    buffer.put(m33);

    buffer.flip();

    return this;
}

public Mat4f loadPerspective(float fov, float ratio, float near, float far) {
    m11 = (float) (1.0f / (Math.tan(fov / 2.0f)));
    m00 = m11 / ratio;
    m22 = -(far + near) / (far - near);
    m23 = -1.0f;
    m32 = -2.0f * far * near / (far - near);
    m33 = 0.0f;
    return this;
}

public Mat4f translate(float x, float y, float z) {
    m30 = x;
    m31 = y;
    m32 = z;
    return this;
}

public Mat4f scale(float x, float y, float z) {
    m00 = x;
    m11 = y;
    m22 = z;
    return this;
}

public Mat4f rotateX(float x) {
    m11 = (float) Math.cos(x);
    m12 = (float) Math.sin(x);
    m21 = (float) -(Math.sin(x));
    m22 = (float) Math.cos(x);
    return this;
}

public Mat4f rotateY(float y) {
    m00 = (float) Math.cos(y);
    m02 = (float) -(Math.sin(y));
    m20 = (float) Math.sin(y);
    m22 = (float) Math.cos(y);
    return this;
}

public Mat4f rotateZ(float z) {
    m00 = (float) Math.cos(z);
    m01 = (float) Math.sin(z);
    m10 = (float) -(Math.sin(z));
    m11 = (float) Math.cos(z);
    return this;
}
}

正确的做法如下:

public Mat4f translate(float x, float y, float z, Mat4f dest) {
    dest.m00 = m00;
    dest.m01 = m01;
    dest.m02 = m02;
    dest.m03 = m03;
    dest.m10 = m10;
    dest.m11 = m11;
    dest.m12 = m12;
    dest.m13 = m13;
    dest.m20 = m20;
    dest.m21 = m21;
    dest.m22 = m22;
    dest.m23 = m23;
    dest.m30 = m00 * x + m10 * y + m20 * z + m30;
    dest.m31 = m01 * x + m11 * y + m21 * z + m31;
    dest.m32 = m02 * x + m12 * y + m22 * z + m32;
    dest.m33 = m03 * x + m13 * y + m23 * z + m33;
    return this;
}

public Mat4f translate(float x, float y, float z) {
    return translate(x, y, z, this);
}

public Mat4f scale(float x, float y, float z, Mat4f dest) {
    dest.m00 = m00 * x;
    dest.m01 = m01 * x;
    dest.m02 = m02 * x;
    dest.m03 = m03 * x;
    dest.m10 = m10 * y;
    dest.m11 = m11 * y;
    dest.m12 = m12 * y;
    dest.m13 = m13 * y;
    dest.m20 = m20 * z;
    dest.m21 = m21 * z;
    dest.m22 = m22 * z;
    dest.m23 = m23 * z;
    dest.m30 = m30;
    dest.m31 = m31;
    dest.m32 = m32;
    dest.m33 = m33;
    return this;
}

public Mat4f scale(float x, float y, float z) {
    return scale(x, y, z, this);
}

public Mat4f rotateX(float x, Mat4f dest) {
    float cos = (float) Math.cos(x);
    float sin = (float) Math.sin(x);
    float rm11 = cos;
    float rm12 = sin;
    float rm21 = -sin;
    float rm22 = cos;

    float nm10 = m10 * rm11 + m20 * rm12;
    float nm11 = m11 * rm11 + m21 * rm12;
    float nm12 = m12 * rm11 + m22 * rm12;
    float nm13 = m13 * rm11 + m23 * rm12;

    dest. m20 = m10 * rm21 + m20 * rm22;
    dest.m21 = m11 * rm21 + m21 * rm22;
    dest.m22 = m12 * rm21 + m22 * rm22;
    dest. m23 = m13 * rm21 + m23 * rm22;

    dest.m10 = nm10;
    dest.m11 = nm11;
    dest.m12 = nm12;
    dest.m13 = nm13;

    return this;
}

public Mat4f rotateX(float x) {
    return rotateX(x, this);
}

public Mat4f rotateY(float y, Mat4f dest) {
    float cos = (float) Math.cos(y);
    float sin = (float) Math.sin(y);
    float rm00 = cos;
    float rm02 = -sin;
    float rm20 = sin;
    float rm22 = cos;

    float nm00 = m00 * rm00 + m20 * rm02;
    float nm01 = m01 * rm00 + m21 * rm02;
    float nm02 = m02 * rm00 + m22 * rm02;
    float nm03 = m03 * rm00 + m23 * rm02;

    dest.m20 = m00 * rm20 + m20 * rm22;
    dest.m21 = m01 * rm20 + m21 * rm22;
    dest.m22 = m02 * rm20 + m22 * rm22;
    dest.m23 = m03 * rm20 + m23 * rm22;

    dest.m00 = nm00;
    dest.m01 = nm01;
    dest.m02 = nm02;
    dest.m03 = nm03;

    return this;
}

public Mat4f rotateY(float y) {
    return rotateY(y, this);
}

public Mat4f rotateZ(float z, Mat4f dest) {
    float cos = (float) Math.cos(z);
    float sin = (float) Math.sin(z);
    float rm00 = cos;
    float rm01 = sin;
    float rm10 = -sin;
    float rm11 = cos;

    float nm00 = m00 * rm00 + m10 * rm01;
    float nm01 = m01 * rm00 + m11 * rm01;
    float nm02 = m02 * rm00 + m12 * rm01;
    float nm03 = m03 * rm00 + m13 * rm01;

    dest.m10 = m00 * rm10 + m10 * rm11;
    dest.m11 = m01 * rm10 + m11 * rm11;
    dest.m12 = m02 * rm10 + m12 * rm11;
    dest.m13 = m03 * rm10 + m13 * rm11;

    dest.m00 = nm00;
    dest.m01 = nm01;
    dest.m02 = nm02;
    dest.m03 = nm03;

    return this;
}

public Mat4f rotateZ(float z) {
    return rotateZ(z, this);
}

为了修改矩阵,我使用了以下转换顺序:

public void transform() {
    mMat.loadIdentity();
    mMat.translate(position.x, position.y, position.z);
    mMat.rotateX((float) Math.toRadians(orientation.x));
    mMat.rotateY((float) Math.toRadians(orientation.y));
    mMat.rotateZ((float) Math.toRadians(orientation.z));
    mMat.scale(scale.x, scale.y, scale.z);
}

public void updateCamera() {
    Vec3f position = World.camera.getPosition();
    vMat.loadIdentity();
    vMat.rotateX((float) Math.toRadians(World.camera.getPitch()));
    vMat.rotateY((float) Math.toRadians(World.camera.getYaw()));
    vMat.translate(-position.x, -position.y, position.z);
}

编辑:透视投影很好,平移也很好,但是如果我将模型矩阵存储在 Mat4f 中,那么模型的旋转会跟随相机的旋转。

Edit2:当我使用 Mat4f 作为模型矩阵时,模型的方向不再跟随相机旋转。投影矩阵、平移和缩放效果很好。

Edit3:编辑代码,应用的旋转不是整圈旋转,模型左右摆动。

Edit4:我尝试使用矩阵乘法进行旋转

【问题讨论】:

    标签: opengl lwjgl linear-algebra


    【解决方案1】:

    您的矩阵类没有实现转换的组合。您的每个矩阵方法只是覆盖某些元素。

    例如,看下面的操作序列:

    translate(1,1,1);
    rotateX(45);
    translate(1,1,1);
    

    这样的序列在数学上应该由矩阵乘法 T * R * T 表示(其中 T 和 R 是具有各自参数的基本平移/旋转矩阵)。

    但是,您的代码所做的只是创建 T(因为最初,矩阵是恒等式),覆盖一些旋转部分,最后再次覆盖平移部分。

    要修复您的代码,您需要实现一个适当的矩阵乘法方法,并在您想对矩阵进行进一步转换时实际使用它。为基本转换矩阵创建一些方法可能会有所帮助。

    但是,在我看来,这仍然像通常的行主与列主矩阵存储布局顺序。

    您的 mXY 成员布局建议您使用数学约定,其中 m12 是第二行第三列。然后,您的 store 方法确实将矩阵放入具有列主要布局的缓冲区中。传统 GL 的矩阵函数(如 glLoadMatrixglMultMatrix)可以直接使用这样的缓冲区。这也是矩阵制服的“标准”布局,其中transpose 参数设置为GL_FALSE

    但是,查看您的 translate 方法:

    public Mat4f translate(float x, float y, float z) {
        m30 = x;
        m31 = y;
        m32 = z;
       return this;
    }
    

    这会将平移向量设置为最后一个,而不是最后一个。 (所有其他函数似乎也使用了转置布局)。

    现在旧版 GL 使用 matrix * vector 约定(与 D3D 更喜欢的 vector * matrix 相反)。在这种情况下,您的矩阵将转置为 GL 所期望的。如果您使用着色器,则取决于您使用的乘法顺序 - 但它必须与您用于矩阵的约定和M * v == v^T * M^T 相匹配。

    【讨论】:

    • 非常感谢您花时间回答我的问题! loadPerspective 方法、平移和缩放工作正常,所以它不可能是排序问题。如果我将 store 函数更改为 row major(我认为这与将我的索引更改为 m03、m13 和 m23 在翻译的情况下相同),那么即使我在顶点着色器。
    • 对象的位置、方向和比例字段保持它们的绝对值,而不是增量,我每次在转换矩阵之前都调用单位矩阵。我唯一无法工作的是旋转。我曾尝试仅在旋转中转换索引,但这也不起作用。我也尝试向函数添加依赖值(请参阅编辑),但它总是会产生相同类型的不当行为。
    • @GergelyKovács:这与绝对值与增量无关。例如,查看您的Renderer::updateCamera():它设置身份一次,然后反复覆盖矩阵中的某些字段,创建一个完全奇怪的转换,与您认为放置在那里的单个转换无关。
    • 只有旋转不起作用,其他转换都很好。该方法每次调用时都会将矩阵设置为单位矩阵。
    • @GergelyKovács:好的,您的最新代码朝着正确的方向发展。不过,操作顺序似乎倒退了。那么该代码到底有什么问题呢?
    【解决方案2】:

    问题确实是我没有考虑早期的转换。我最终没有使用矩阵乘法。我从 JOML 复制了相关部分,您可以通过以下方式访问:https://github.com/JOML-CI/JOML。 我将编辑问题以反映问题是什么,以及课程的最终状态是什么。感谢 JOML 贡献者和 Derhass 的帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-30
      • 2016-08-21
      • 2014-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多