如何使用JOML来模拟OpenGL中的模型视图矩阵,在3D投影到2D平面上?

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

how to use JOML to simulate OpenGL like Model, View Matrices on 3D projection to 2D plane?

问题

经过多年的学习OpenGL初级课程和线性代数课程,我最近终于理解了模型(Model)、视图(View)和投影(Projection)矩阵的作用。基本上,模型矩阵将3D模型的顶点坐标转换为3D世界中的顶点坐标(相对于3D世界的原点进行平移、旋转和缩放)。视图矩阵将3D世界的顶点坐标转换为相对于摄像机的顶点坐标(通常仅涉及相对于摄像机的世界平移和旋转),投影矩阵用于将摄像机视角中的顶点坐标投影到2D平面上(通常是屏幕)。

我试图在一个3D投影的2D平面上创建一个摄像机系统,而不使用OpenGL,而是使用JOML。JOML是一个Java数学库(主要用于线性代数数学),通常与轻量级Java游戏库3(LWJGL3)一起用于OpenGL。我已经能够在OpenGL中创建一个摄像机系统,使用了前面提到的3个矩阵,这在OpenGL中相当容易。但是当我在同样的矩阵基础上进行操作(以及一些额外的代码,使投影出现在屏幕上),我只能够在2D平面上进行投影。模型矩阵和视图矩阵似乎对模型在屏幕上的投影方式没有任何影响。

以下是我用来在屏幕上投影一个立方体的代码:

private float theta = 0;

@Override
public void render(Graphics g) {
    // ...(前面的代码与你提供的相同)

    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));
            // ...(以下代码与你提供的相同)
        }
    }

    // ...(后面的代码与你提供的相同)
}

@Override
public void update() {
    theta++;
}

在上面的代码中,立方体应该距离摄像机25个单位(因为立方体距离世界原点5个单位,而摄像机在相反方向上距离世界20个单位),并且在世界的右侧5个单位。但事实并非如此,正如我们在上面的图片中所看到的:

(图片已省略)

从图片中可以看出,立方体明显是居中且近距离看的。

我试图找到一个解决方案,可以在我的LWJGL3应用程序和3D投影应用程序中保持相同的“OpenGL”基础代码(更准确地说是基于JOML的代码),使用相同的模型、视图和投影矩阵在两个应用程序中产生相同的投影。

英文:

After years of taking beginner courses in OpenGL and linear algebra courses, I recently finally understood the point of the Model, View and Projection Matrices. Basically the Model Matrix converts the vertex coordinates of an 3D model into vertex coordinates in a 3D world (translating, rotating and scaling the model relative to the origin of the 3D world). The View Matrix converts the vertex coordinates of the 3D world into vertex coordinates relative to a camera (usually only translation and rotation of the world relative to the camera) and the Projection Matrix is used to compute/convert the vertex coordinate in a Camera view into a projection on a 2D plane (usually the screen).

I'm trying to create a camera system in a 3D Projection on a 2D Plane without OpenGL but by using JOML which is a Java Math (Mostly Linear Algebra math) Library for OpenGL often used with the LightWeight Java Game Library 3. I am able to create a camera system in OpenGL, which is quite easy with the 3 aforementioned matrices. But when I use the same exact matrices (and some extra code so that the projection appears on the screen) I can only do the Projection on a 2D Plane. The Model Matrix and View Matrix don't seem to have any effect on the way the model is projected on the screen.

Here is the code I'm using to project a cube on the screen:

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 &lt; cube3f.length; i++) {
		for(int j = 0; j &lt; 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&#39;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 &lt; 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++;
}

In the above code, the cube is supposed to be 25 units away from the camera (because the cube is 5 unit away from the origin of the world and the camera 20 units away from the world on opposite direction) and 5 units to the right of the world. but that is not the case as we can see on the following picture:

如何使用JOML来模拟OpenGL中的模型视图矩阵,在3D投影到2D平面上?

As we can see in the picture; the cube is clearly centered and seen up-close.

I'm trying to find a solution that would allow me to keep the same "OpenGL" base code (more accurately JOML base code) on both my LWJGL3 application and my 3D Projection application. That is using the same Model, View and Projection Matrices to produce the same projection on both applications.

答案1

得分: 2

你错过了透视除法部分。裁剪空间坐标是齐次坐标。你必须将齐次裁剪空间坐标转换为笛卡尔归一化设备坐标(所有分量范围在[-1, 1]之间),通过将xyz分量除以w分量:

tempvec = tempvec.mul(projectionMatrix);
cube4f[i][j] = new Vector4f(
    tempvec.x / tempvec.w,
    tempvec.y / tempvec.w,
    tempvec.z / tempvec.w,
    1.0f);

由于顶点是点而不是向量,顶点坐标的第四个分量必须为1,而不是0:

<s>Vector4f tempvec = new Vector4f(cube3f[i][j], 0.0f).mul(modelMatrix);</s>

Vector4f tempvec = new Vector4f(cube3f[i][j], 1.0f).mul(modelMatrix);
英文:

You missed the Perspective divide. The clip space coordinate is a Homogeneous coordinates. You have to transform the Homogeneous clip space coordinate to a Cartesian normalized device coordinate (all components are in range [-1, 1]) by dividing the x, y and z component by the w cpmponent:

tempvec = tempvec.mul(projectionMatrix);
cube4f[i][j] = new Vector4f(
    tempvec.x / tempvec.w,
    tempvec.y / tempvec.w,
    tempvec.z / tempvec.w,
    1.0f);

Since the vertices are points and not vectors, the 4th component of the vertex coordinate has to be 1 rather than 0:

<s>Vector4f tempvec = new Vector4f(cube3f[i][j], 0.0f).mul(modelMatrix);</s>

Vector4f tempvec = new Vector4f(cube3f[i][j], 1.0f).mul(modelMatrix);

huangapple
  • 本文由 发表于 2020年9月22日 23:27:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/64012912.html
匿名

发表评论

匿名网友

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

确定