经过多年学习 OpenGL 和线性代数入门类(class),我最近终于理解了模型、 View 和投影矩阵的要点。基本上,模型矩阵将 3D 模型的顶点坐标转换为 3D 世界中的顶点坐标(相对于 3D 世界的原点平移、旋转和缩放模型)。 View 矩阵将 3D 世界的顶点坐标转换为相对于相机的顶点坐标(通常只是世界相对于相机的平移和旋转),投影矩阵用于将相机 View 中的顶点坐标计算/转换为2D 平面(通常是屏幕)上的投影。
我正在尝试在不使用 OpenGL 的情况下在 2D 平面上的 3D 投影中创建一个相机系统,但使用 JOML,这是一个用于 OpenGL 的 Java 数学(主要是线性代数数学)库,通常与 LightWeight Java Game Library 3 一起使用。我能够在 OpenGL 中创建一个相机系统,使用上述 3 个矩阵就非常容易了。但是,当我使用相同的精确矩阵(以及一些额外的代码以便投影出现在屏幕上)时,我只能在 2D 平面上进行投影。模型矩阵和 View 矩阵似乎对模型在屏幕上的投影方式没有任何影响。
这是我用来在屏幕上投影立方体的代码:
private float theta = 0;
@Override
public void render(Graphics g) {
Vector3f cube3f[][] = {
// SOUTH
{ new Vector3f(-0.5f, -0.5f, -0.5f), new Vector3f(-0.5f, 0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, -0.5f) },
{ new Vector3f(-0.5f, -0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, -0.5f), new Vector3f( 0.5f, -0.5f, -0.5f) },
// EAST
{ new Vector3f( 0.5f, -0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, 0.5f) },
{ new Vector3f( 0.5f, -0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, 0.5f), new Vector3f( 0.5f, -0.5f, 0.5f) },
// NORTH
{ new Vector3f( 0.5f, -0.5f, 0.5f), new Vector3f( 0.5f, 0.5f, 0.5f), new Vector3f(-0.5f, 0.5f, 0.5f) },
{ new Vector3f( 0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, 0.5f, 0.5f), new Vector3f(-0.5f, -0.5f, 0.5f) },
// WEST
{ new Vector3f(-0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, 0.5f, 0.5f), new Vector3f(-0.5f, 0.5f, -0.5f) },
{ new Vector3f(-0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, 0.5f, -0.5f), new Vector3f(-0.5f, -0.5f, -0.5f) },
// TOP
{ new Vector3f(-0.5f, 0.5f, -0.5f), new Vector3f(-0.5f, 0.5f, 0.5f), new Vector3f( 0.5f, 0.5f, 0.5f) },
{ new Vector3f(-0.5f, 0.5f, -0.5f), new Vector3f( 0.5f, 0.5f, 0.5f), new Vector3f( 0.5f, 0.5f, -0.5f) },
// BOTTOM
{ new Vector3f( 0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, -0.5f, -0.5f) },
{ new Vector3f( 0.5f, -0.5f, 0.5f), new Vector3f(-0.5f, -0.5f, -0.5f), new Vector3f( 0.5f, -0.5f, -0.5f) },
};
Vector4f cube4f[][] = new Vector4f[cube3f.length][cube3f[0].length];
for(int i = 0; i < cube3f.length; i++) {
for(int j = 0; j < cube3f[i].length; j++) {
Matrix4f modelMatrix = new Matrix4f()
.rotate((float)Math.toRadians(theta), new Vector3f(0.0f, 1.0f, 0))
.rotate((float)Math.toRadians(theta), new Vector3f(1.0f, 0, 0))
.translate(new Vector3f(0, 5, 5)); // this is supposed to move the cube up 5 units and away 5 units
Vector4f tempvec = new Vector4f(cube3f[i][j], 0.0f).mul(modelMatrix);
Matrix4f viewMatrix = new Matrix4f().translate(new Vector3f(theta, 0, -20)); //this is supposed to translate the camera back 20 units
tempvec = tempvec.mul(viewMatrix);
Matrix4f projectionMatrix = new Matrix4f().identity().setPerspective((float)Math.toRadians(70.0f), 1280.0f/720.0f, 0.1f, 1000.0f);
cube4f[i][j] = tempvec.mul(projectionMatrix);
//following code makes the projection appear inside the screen's borders
cube4f[i][j].x += 1.0f;
cube4f[i][j].y += 1.0f;
cube4f[i][j].x *= 0.5f * 1280.0f;
cube4f[i][j].y *= 0.5f * 720.0f;
}
}
Graphics2D g2d = (Graphics2D)g;
g2d.setBackground(new Color(32, 32, 32, 255));
g2d.clearRect(0, 0, 1280, 720);
g2d.setColor(Color.WHITE);
for(int i = 0; i < cube4f.length; i++) {
g2d.drawLine((int)cube4f[i][0].x, (int)cube4f[i][0].y, (int)cube4f[i][1].x, (int)cube4f[i][1].y);
g2d.drawLine((int)cube4f[i][1].x, (int)cube4f[i][1].y, (int)cube4f[i][2].x, (int)cube4f[i][2].y);
g2d.drawLine((int)cube4f[i][2].x, (int)cube4f[i][2].y, (int)cube4f[i][0].x, (int)cube4f[i][0].y);
}
}
@Override
public void update() {
theta++;
}
在上面的代码中,立方体应该距离相机 25 个单位(因为立方体距离世界原点 5 个单位,而相机在相反方向距离世界 20 个单位)和 5 个单位到世界的右边。但事实并非如此,如下图所示:
如图所示;立方体清晰居中,近距离观察。
我正在尝试找到一种解决方案,使我能够在 LWJGL3 应用程序和 3D 投影应用程序上保留相同的“OpenGL”基本代码(更准确地说是 JOML 基本代码)。即使用相同的模型、 View 和投影矩阵在两个应用程序上生成相同的投影。
最佳答案
您错过了Perspective divide 。剪辑空间坐标是 Homogeneous coordinates 。您必须将齐次剪切空间坐标转换为 Cartesian通过将 x
、y
和 z
分量除以 来标准化设备坐标(所有分量都在 [-1, 1] 范围内) >w
组件:
tempvec = tempvec.mul(projectionMatrix);
cube4f[i][j] = new Vector4f(
tempvec.x / tempvec.w,
tempvec.y / tempvec.w,
tempvec.z / tempvec.w,
1.0f);
由于顶点是点而不是 vector ,因此顶点坐标的第四个分量必须是 1 而不是 0:
Vector4f tempvec = new Vector4f(cube3f[i][j], 0.0f).mul(modelMatrix);
Vector4f tempvec = new Vector4f(cube3f[i][j], 1.0f).mul(modelMatrix);
关于java - 如何使用JOML在3D投影到2D平面上模拟OpenGL之类的模型、 View 矩阵?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64012912/