Рендеринг куба, составленного из треугольников, дает странные углы при повороте

#java #math #3d #geometry #rendering

#java #математика #3D #геометрия #рендеринг

Вопрос:

Я настроил простой 3D-рендерер на Java, который управляет проецированием 3D-куба на заданную 2D-панель. Теперь я на том этапе, когда я хотел бы повернуть этот куб, чтобы продемонстрировать 3D. Однако, как только я добавляю к нему поворот, углы, которые образуют треугольники, больше не «синхронизируются», и в итоге получаются странные перекрывающиеся вершины.

Две вершины куба

Это визуализируется с использованием только двух вершин, чтобы четко показать проблему. Я не уверен, как это исправить.

Пока это мой код.

 meshCube = new mesh();

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

    // SOUTH
    meshCube.tris.add(new triangle(new vector(0.0f,0.0f,0.0f),   new vector(0.0f,1.0f,0.0f),   new vector(1.0f,1.0f,0.0f)));
    meshCube.tris.add(new triangle(new vector(0.0f,0.0f,0.0f),   new vector(1.0f,1.0f,0.0f),   new vector(1.0f,0.0f,0.0f)));

    // EAST
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,0.0f),   new vector(1.0f,1.0f,0.0f),   new vector(1.0f,1.0f,1.0f)));
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,0.0f),   new vector(1.0f,1.0f,1.0f),   new vector(1.0f,0.0f,1.0f)));
    /*
    // NORTH
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,1.0f),   new vector(1.0f,1.0f,1.0f),   new vector(0.0f,1.0f,1.0f)));
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,1.0f),   new vector(0.0f,1.0f,1.0f),   new vector(0.0f,0.0f,1.0f)));

    // WEST
    meshCube.tris.add(new triangle(new vector(0.0f,0.0f,1.0f),   new vector(0.0f,1.0f,1.0f),   new vector(0.0f,1.0f,0.0f)));
    meshCube.tris.add(new triangle(new vector(0.0f,0.0f,1.0f),   new vector(0.0f,1.0f,0.0f),   new vector(0.0f,0.0f,0.0f)));

    // TOP
    meshCube.tris.add(new triangle(new vector(0.0f,1.0f,0.0f),   new vector(0.0f,1.0f,1.0f),   new vector(1.0f,1.0f,1.0f)));
    meshCube.tris.add(new triangle(new vector(0.0f,1.0f,0.0f),   new vector(1.0f,1.0f,1.0f),   new vector(1.0f,1.0f,0.0f)));

    // BOTTOM
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,1.0f),   new vector(0.0f,0.0f,1.0f),   new vector(0.0f,0.0f,0.0f)));
    meshCube.tris.add(new triangle(new vector(1.0f,0.0f,1.0f),   new vector(0.0f,0.0f,0.0f),   new vector(1.0f,0.0f,0.0f)));*/
    JPanel renderPanel = new JPanel() {
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.BLACK);
            g2.fillRect(0, 0, getWidth(), getHeight());
            g2.setColor(Color.WHITE);
            for (triangle t : meshCube.tris) {

                triangle triRotatedZ = new triangle(t.p[0],t.p[1],t.p[2]);

                long finish = System.nanoTime();
                long timeElapsed = finish - start;

                fTheta  = 0.1f * 1.0f; //This should really be fTheta  = 1.0f * timeElapsed; but doesn't seem to work that way.
                System.out.println(fTheta);

                mat4x4 matRotZ = new mat4x4();
                mat4x4 matRotX = new mat4x4();

                matRotZ.m[0][0] = (float)Math.cos(fTheta);
                matRotZ.m[0][1] = (float)Math.sin(fTheta);
                matRotZ.m[1][0] = (float)-Math.sin(fTheta);
                matRotZ.m[1][1] = (float)Math.cos(fTheta);
                matRotZ.m[2][2] = 1;
                matRotZ.m[3][3] = 1;

                matRotX.m[0][0] = 1;
                matRotX.m[1][1] = (float)Math.cos((fTheta*0.5f));
                matRotX.m[1][2] = (float)Math.sin((fTheta*0.5f));
                matRotX.m[2][1] = (float)-Math.sin((fTheta*0.5f));
                matRotX.m[2][2] = (float)Math.cos((fTheta*0.5f));
                matRotX.m[3][3] = 1;

                triRotatedZ.p[0] = multiplyMatrixVector(t.p[0],matRotZ);
                triRotatedZ.p[1] = multiplyMatrixVector(t.p[1],matRotZ);
                triRotatedZ.p[2] = multiplyMatrixVector(t.p[2],matRotZ);

                triangle triRotatedZX = new triangle(triRotatedZ.p[0],triRotatedZ.p[1],triRotatedZ.p[2]);

                triRotatedZX.p[0] = multiplyMatrixVector(triRotatedZ.p[0],matRotX);
                triRotatedZX.p[1] = multiplyMatrixVector(triRotatedZ.p[1],matRotX);
                triRotatedZX.p[2] = multiplyMatrixVector(triRotatedZ.p[2],matRotX);


                triangle triTranslated = new triangle(triRotatedZX.p[0],triRotatedZX.p[1],triRotatedZX.p[2]);

                triTranslated.p[0].z = triRotatedZX.p[0].z   3.0f;
                triTranslated.p[1].z = triRotatedZX.p[1].z   3.0f;
                triTranslated.p[2].z = triRotatedZX.p[2].z   3.0f;

                triangle triProjected = new triangle(triTranslated.p[0],triTranslated.p[1],triTranslated.p[2]);

                triProjected.p[0] = multiplyMatrixVector(triTranslated.p[0],proj);
                triProjected.p[1] = multiplyMatrixVector(triTranslated.p[1],proj);
                triProjected.p[2] = multiplyMatrixVector(triTranslated.p[2],proj);

                // Scale into view
                triProjected.p[0].x  = 1.0f;
                triProjected.p[0].y  = 1.0f;

                triProjected.p[1].x  = 1.0f;
                triProjected.p[1].y  = 1.0f;

                triProjected.p[2].x  = 1.0f;
                triProjected.p[2].y  = 1.0f;

                triProjected.p[0].x *= 0.5f * 500.0f;
                triProjected.p[0].y *= 0.5f * 500.0f;
                triProjected.p[1].x *= 0.5f * 500.0f;
                triProjected.p[1].y *= 0.5f * 500.0f;
                triProjected.p[2].x *= 0.5f * 500.0f;
                triProjected.p[2].y *= 0.5f * 500.0f;

                Path2D path = new Path2D.Double();
                path.moveTo(triProjected.p[0].x, triProjected.p[0].y);
                path.lineTo(triProjected.p[1].x, triProjected.p[1].y);
                path.lineTo(triProjected.p[2].x, triProjected.p[2].y);
                path.closePath();
                g2.draw(path);
            }

        }
    };
  

Код для умножения матриц:

 public static vector multiplyMatrixVector(vector in, mat4x4 m){
    vector out = new vector(0,0,0);
    out.x = in.x * m.m[0][0]   in.y * m.m[1][0]   in.z * m.m[2][0]   m.m[3][0];
    out.y = in.x * m.m[0][1]   in.y * m.m[1][1]   in.z * m.m[2][1]   m.m[3][1];
    out.z = in.x * m.m[0][2]   in.y * m.m[1][2]   in.z * m.m[2][2]   m.m[3][2];
    float w = in.x * m.m[0][3]   in.y * m.m[1][3]   in.z * m.m[2][3]   m.m[3][3];
    if(w!=0.0f) {
        out.x /= w;
        out.y /= w;
        out.z /= w;
        return out;
    }
    System.out.println("W is 0.");
    return out;
}
  

Комментарии:

1. Возможно, причина вашей проблемы не в этом, но вы не выполняете решающее разделение перспективы.

2. Я полагаю, что я нахожусь в своей функции multiplyMatrixVector. Я только начинаю с этого, так что, возможно, я ошибаюсь. Я добавил исходное сообщение.

3. multiplyMatrixVector должна быть функция общего назначения, которая делает именно то, что описывает ее название, а не что-то дополнительное. Вы должны выполнить разделение перспективы явно после проекции. Кроме того, w!=0.0f это очень плохая идея из-за числовой неточности.

4. @meowgoesthedog Я принял к сведению ваш комментарий для дальнейшей доработки (спасибо!), Однако, этот джентльмен использует точно такую же функцию, и его, кажется, работает просто отлично.

Ответ №1:

 for (triangle t : meshCube.tris) {
    …
    fTheta  = 0.1f * 1.0f;
  

У вас есть новый ракурс камеры для каждого треугольника. Переместите вычисление вашей матрицы за пределы цикла.

Комментарии:

1. Это было именно так. Мой герой <3