首页 > 解决方案 > 乘以投影矩阵后如何渲染三角形

问题描述

我正在尝试从头开始使用 java 制作 3d 游戏,但是在将每个顶点与投影矩阵相乘后渲染三角形时出现问题

我已经尝试使用投影顶点 x 和 y 但结果是所有顶点都在同一个 X 中,所以我尝试旋转三角形 X 或 Y 或 Z 轴但结果是相同的。

渲染结果(画在画中):

结果图像

我知道三角形与相机对齐,但我尝试通过更改其 X 或 Y 或 Z 坐标来移动重叠的顶点,但它没有用


import java.awt.Color;
import java.awt.Graphics;

import measurement.MatrixF;
import measurement.Vector3f;
import model.Mesh;
import model.Triangle;
import toolbox.GE;
import toolbox.Matrix;
import toolbox.Vector;

public class MeshRenderer {

    private int width, height;
    private float fNear, fFar;
    private float fov;
    private float fAspectRatio;
    private float fovRad;
    private float theta;
    private MatrixF projectionMatrix;
    private MatrixF rotXMatrix;
    private MatrixF rotYMatrix;
    private MatrixF rotZMatrix;
    private Vector3f globalTranslation;

    public MeshRenderer(float fNear, float fFar, float fov, int width, int height) {
        this.fNear = fNear;
        this.fFar = fFar;
        this.fov = fov;
        this.fAspectRatio = height / width;
        this.width = width;
        this.height = height;
        this.fovRad = (float) (1.0f / Math.tan(Math.toRadians(fov / 2)));

        projectionMatrix = new MatrixF(4, 4);
        rotXMatrix = new MatrixF(4, 4);
        rotYMatrix = new MatrixF(4, 4);
        rotZMatrix = new MatrixF(4, 4);

        projectionMatrix.m[0][0] = fAspectRatio * fovRad;
        projectionMatrix.m[1][1] = fovRad;
        projectionMatrix.m[2][2] = (-(fFar + fNear)) / (fFar - fNear);
        projectionMatrix.m[3][2] = (-2 * fFar * fNear) / (fFar - fNear);
        projectionMatrix.m[2][3] = -1.0f;   
        projectionMatrix.m[3][3] = 0.0f;

        rotXMatrix.m[0][0] = 1;
        rotXMatrix.m[1][1] = (float) Math.cos(theta);
        rotXMatrix.m[2][1] = (float) -Math.sin(theta);
        rotXMatrix.m[1][2] = (float) Math.sin(theta);
        rotXMatrix.m[2][2] = (float) Math.cos(theta);

        rotYMatrix.m[0][0] = (float) Math.cos(theta);
        rotYMatrix.m[2][0] = (float) Math.sin(theta);
        rotYMatrix.m[1][1] = (float) 1.0;
        rotYMatrix.m[0][2] = (float) -Math.sin(theta);
        rotYMatrix.m[2][2] = (float) Math.cos(theta);

        rotXMatrix.m[2][2] = 1;
        rotXMatrix.m[0][0] = (float) Math.cos(theta);
        rotXMatrix.m[1][0] = (float) -Math.sin(theta);
        rotXMatrix.m[0][1] = (float) Math.sin(theta);
        rotXMatrix.m[1][1] = (float) Math.cos(theta);

        //projectionMatrix = Matrix.transpose(projectionMatrix);
        globalTranslation = new Vector3f(0.0f, 0.0f, 0.0f);
    }

    public void renderMesh(Mesh mesh, Graphics g) {

        for(int i = 0; i < mesh.tris.length; i++) {
            Triangle tri =  new Triangle(mesh.tris[i].p[0], mesh.tris[i].p[1], mesh.tris[i].p[2]);
            Triangle translatedTri = tri;
            Triangle projectedTri = new Triangle();

            theta += 0.0001;
            this.calculateRotationMatrix(theta);

            translatedTri.p[0] = Matrix.multiplyMatrixVector(tri.p[0], rotYMatrix);
            translatedTri.p[1] = Matrix.multiplyMatrixVector(tri.p[1], rotYMatrix);
            translatedTri.p[2] = Matrix.multiplyMatrixVector(tri.p[2], rotYMatrix);

            translatedTri.p[0].z = tri.p[0].z + globalTranslation.z;
            translatedTri.p[1].z = tri.p[1].z + globalTranslation.z;
            translatedTri.p[2].z = tri.p[2].z + globalTranslation.z;

            projectedTri.p[0] = Matrix.multiplyMatrixVector(translatedTri.p[0], projectionMatrix);
            projectedTri.p[1] = Matrix.multiplyMatrixVector(translatedTri.p[1], projectionMatrix);
            projectedTri.p[2] = Matrix.multiplyMatrixVector(translatedTri.p[2], projectionMatrix);

            projectedTri.p[0].x += 1.0f; projectedTri.p[0].y += 1.0f;
            projectedTri.p[1].x += 1.0f; projectedTri.p[1].y += 1.0f;
            projectedTri.p[2].x += 1.0f; projectedTri.p[2].y += 1.0f;

            float scale = 0.5f;

            projectedTri.p[0].x *= scale * width;
            projectedTri.p[0].y *= scale * height;
            projectedTri.p[1].x *= scale * width; 
            projectedTri.p[1].y *= scale * height;
            projectedTri.p[2].x *= scale * width; 
            projectedTri.p[2].y *= scale * height;


            GE.drawTriangle(projectedTri.p[0].x, projectedTri.p[0].y, projectedTri.p[1].x, projectedTri.p[1].y, projectedTri.p[2].x, projectedTri.p[2].y, Color.WHITE, g);

            for(int j = 0; j < projectedTri.p.length; j++) {
                g.setColor(new Color(255, 0, (j * 50)));
                g.fillRect((int)projectedTri.p[j].x - 8, (int)projectedTri.p[j].y - 8, 16 - j, 16 - j);
            }

            translatedTri.p[0].z = tri.p[0].z - globalTranslation.z;
            translatedTri.p[1].z = tri.p[1].z - globalTranslation.z;
            translatedTri.p[2].z = tri.p[2].z - globalTranslation.z;
        }
    }

    private void calculateRotationMatrix(float theta) {
        rotXMatrix.m[0][0] = 1;
        rotXMatrix.m[1][1] = (float) Math.cos(theta);
        rotXMatrix.m[2][1] = (float) -Math.sin(theta);
        rotXMatrix.m[1][2] = (float) Math.sin(theta);
        rotXMatrix.m[2][2] = (float) Math.cos(theta);

        rotYMatrix.m[0][0] = (float) Math.cos(theta);
        rotYMatrix.m[2][0] = (float) Math.sin(theta);
        rotYMatrix.m[1][1] = (float) 1.0;
        rotYMatrix.m[0][2] = (float) -Math.sin(theta);
        rotYMatrix.m[2][2] = (float) Math.cos(theta);

        rotXMatrix.m[2][2] = 1;
        rotXMatrix.m[0][0] = (float) Math.cos(theta);
        rotXMatrix.m[1][0] = (float) -Math.sin(theta);
        rotXMatrix.m[0][1] = (float) Math.sin(theta);
        rotXMatrix.m[1][1] = (float) Math.cos(theta);
    }

    public Vector3f getTranslation() {
        return globalTranslation;
    }

    public float getfNear() {
        return fNear;
    }

    public float getfFar() {
        return fFar;
    }

    public float getFov() {
        return fov;
    }

    public float getfAspectRatio() {
        return fAspectRatio;
    }

    public float getFovRad() {
        return fovRad;
    }

}

矩阵 (4x4) 与 vector3 函数相乘以防万一:

        Vector3f o = new Vector3f(0, 0, 0);
        o.x = (i.x * m.m[0][0]) + (i.y * m.m[1][0]) + (i.z * m.m[2][0]) + m.m[3][0];
        o.y = (i.x * m.m[0][1]) + (i.y * m.m[1][1]) + (i.z * m.m[2][1]) + m.m[3][1];
        o.z = (i.x * m.m[0][2]) + (i.y * m.m[1][2]) + (i.z * m.m[2][2]) + m.m[3][2];
        float w = (i.x * m.m[0][3]) + (i.y * m.m[1][3]) + (i.z * m.m[2][3]) + m.m[3][3];

        if (w != 0.0f)
        {
            o.x /= w; o.y /= w; o.z /= w;
        }

        return o;
    }

标签: javamatrixgraphics3drender

解决方案


如果没有确切地看到这个类是如何被使用的,很难确切地说出问题是什么,但是FWIW我没有看到数学上有太多错误

  • 有几个地方你可能打算初始化rotZMatrix而不是重新初始化rotXMatrix,但代码实际上并没有使用任何一个。

  • 添加时globalTranslation,您正在用预旋转的 z 坐标覆盖旋转的 z 坐标,而您可能只想更新旋转的坐标。

  • MatrixF 是初始化为恒等式还是初始化为零尚不清楚——但如果是后者,您可能应该m[3][3]用 1.0 填充旋转矩阵的元素。

  • theta自然,一旦您有多个三角形,您可能希望在三角形循环之外取消增量和旋转计算。

我猜问题是你离开globalTranslation零并且网格靠近原点 - 因此转换后的几何图形位于近平面的错误一侧和视锥体之外。大多数图形引擎会剔除此类几何图形,因为转换后的结果将位于剪辑空间之外,并且在眼点周围和后面看起来越来越反常。

我建议尝试调整globalTranslation.z以确保0 < fNear < translatedTri.p[i].z < fFar所有翻译点。

(您也可以尝试用正交投影矩阵临时交换透视矩阵,以确定问题是在投影/均匀化数学中还是在其他地方。)


推荐阅读