OpenGL Java Matrix4f实现
我正在尝试编写一个4*4浮点矩阵类来创建一个包含模型、视图和投影矩阵的三维空间。在当前状态下,当我尝试旋转视图矩阵时,它似乎也应用了平移,空间变得扭曲(好像被压缩了)。投影、视图和模型矩阵相乘在顶点着色器中完成 Edit5:转换函数的非功能状态如下所示:OpenGL Java Matrix4f实现,opengl,lwjgl,linear-algebra,Opengl,Lwjgl,Linear Algebra,我正在尝试编写一个4*4浮点矩阵类来创建一个包含模型、视图和投影矩阵的三维空间。在当前状态下,当我尝试旋转视图矩阵时,它似乎也应用了平移,空间变得扭曲(好像被压缩了)。投影、视图和模型矩阵相乘在顶点着色器中完成 Edit5:转换函数的非功能状态如下所示: public class Mat4f { public float m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32,
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作为模型矩阵时,模型的方向不再跟随摄影机旋转。投影矩阵、平移和缩放效果良好
编辑3:编辑代码,应用的旋转不是一个完整的圆旋转,模型左右摆动
Edit4:我尝试使用矩阵乘法进行旋转您的矩阵类没有实现变换的组合。每个矩阵方法只覆盖某些元素 例如,查看以下操作序列:
translate(1,1,1);
rotateX(45);
translate(1,1,1);
这种序列在数学上应该由矩阵乘法T*R*T表示(其中T和R是具有各自参数的基本平移/旋转矩阵)
但是,您的代码所做的只是创建T(因为最初,矩阵是恒等式),覆盖旋转的某些部分,最后再次覆盖平移部分
要修复代码,您需要实现适当的矩阵多重编译方法,并在需要对矩阵应用进一步转换时实际使用它。为基本变换矩阵创建一些方法可能会有所帮助
但是,在我看来,这仍然是通常的行主矩阵与列主矩阵存储布局顺序
mXY成员的布局建议您使用数学约定,其中m12是第二行第三列。然后,您的store
方法将矩阵放入具有列主布局的缓冲区中。此类缓冲区可直接由传统GL的矩阵函数使用(如glLoadMatrix
或glMultMatrix
)。这也是矩阵制服的“标准”布局,其中transpose
参数设置为GL\u FALSE
但是,看看您的translate
方法:
public Mat4f translate(float x, float y, float z) {
m30 = x;
m31 = y;
m32 = z;
return this;
}
这会将转换向量设置为最后一行,而不是最后一列。(所有其他功能似乎也使用转置布局)
现在,传统GL使用
矩阵*向量
约定(与D3D更喜欢的向量*矩阵
相反)。在这种情况下,矩阵将被转换为GL所期望的矩阵。如果您使用着色器,则取决于您使用的乘法顺序,但它必须符合您对矩阵使用的约定,并且M*v==v^T*M^T
问题确实是我没有考虑早期的转换。但我最终没有使用矩阵乘法。我从JOML复制了相关部分,您可以访问:。
我将编辑问题以反映问题是什么,以及课堂的最终状态是什么。感谢JOML贡献者和Derhass的帮助 非常感谢您抽出时间回答我的问题!loadPerspective方法、转换和缩放工作正常,因此不会出现排序问题。如果我将store函数更改为row major(我认为这与在平移的情况下将索引更改为m03、m13和m23相同),那么即使我更改顶点着色器中的乘法顺序,所有内容都是漆黑的。对象的位置、方向和缩放场保持其绝对值,而不是增量,每次翻译矩阵之前,我都会调用单位矩阵。我唯一做不到的就是旋转。我试过只在轮换中转置索引,但也不起作用。我也尝试过向函数中添加依赖值(参见编辑),但它总是产生同样的错误行为。@GergelyKovács:这与绝对值和增量无关。看看你的
渲染器::updateCamera()
:它设置了一次标识,然后重复覆盖矩阵中的一些字段,创建了一个完全奇怪的转换,它与你认为放在那里的indivudal转换无关。只有旋转不起作用,其他转换就可以了。该方法每次调用时都将矩阵设置为单位矩阵。@GergelyKovács:好的,您的最新代码方向正确。不过,操作顺序似乎是倒退的。那么这个代码到底有什么问题呢?