Processing(Java)中3D投影轴反转问题求助
3D相机旋转时立方体投影翻转问题求助
背景介绍
我数学基础一般,最近尝试将线性代数知识和编程技能结合,用Processing(Java)开发一款小型3D游戏。目前已经实现了立方体的三维旋转功能,但在添加相机视角时遇到了问题。
问题描述
参考维基百科的3D投影矩阵实现相机视角后,旋转相机时,投影的立方体出现横竖翻转现象(视频0:14处可见该问题)。调整显示面参数e的取值后,问题仍未解决,希望了解问题原因及修复方案。
相关代码
import java.util.Arrays; PVector[] points; PVector cam, cam_angle, point; float angle = 0.0; void setup() { //fullScreen(); size(1000,400); noCursor(); cam = point = new PVector(0, 0, -1000); cam_angle = new PVector(0,0,0); points = new PVector[] { new PVector(-100, -100, -100), new PVector(-100, -100, 100), new PVector(-100, 100, -100), new PVector(-100, 100, 100), new PVector( 100, -100, -100), new PVector( 100, -100, 100), new PVector( 100, 100, -100), new PVector( 100, 100, 100), }; } PVector Rotate2d(PVector p, float a) { float[][] m2 = { {cos(a), -sin(a)}, {sin(a), cos(a)} }; float[][] rotated = matmul(m2, new float[][] { { p.x }, { p.y } }); return new PVector(rotated[0][0], rotated[1][0]); } PVector Rotate3d(PVector p, float[][] m2) { float[][] rotated = matmul(m2, new float[][] { { p.x }, { p.y }, { p.z } }); return new PVector(rotated[0][0], rotated[1][0], rotated[2][0]); } PVector Rotate3d_x(PVector p, float a) { return Rotate3d(p, new float[][] { {1, 0, 0}, {0, cos(a), -sin(a)}, {0, sin(a), cos(a)} }); }; PVector Rotate3d_y(PVector p, float a) { return Rotate3d(p, new float[][] { {cos(a), 0, sin(a)}, {0, 1, 0}, {-sin(a), 0, cos(a)} }); } PVector Rotate3d_z(PVector p, float a) { return Rotate3d(p, new float[][] { {cos(a), -sin(a), 0}, {sin(a), cos(a), 0}, {0, 0, 1} }); } PVector Rotate3d(PVector p, PVector a) { return Rotate3d_z( Rotate3d_y(Rotate3d_x(p, a.x), a.y), a.z ); } PVector applyPerspective(PVector p) { PVector c = cam; PVector co = cam_angle; PVector e = new PVector(0, 0, 100); // c = camera position // co = camera orientation / camera rotation // e = displays surface pos relative to camera pinhole c // dx, dy, dz float[][] dxyz = matmul( matmul(new float[][]{ {1, 0, 0}, {0, cos(co.x), sin(co.x)}, {0, -sin(co.x), cos(co.x)} }, new float[][]{ {cos(co.y), 0, -sin(co.y)}, {0, 1, 0}, {sin(co.y), 0, cos(co.y)} }), matmul(new float[][]{ {cos(co.z), sin(co.z), 0}, {-sin(co.z), cos(co.z), 0}, {0, 0, 1} }, new float[][]{ {p.x - c.x}, {p.y - c.y}, {p.z - c.z}, })); PVector d = new PVector(dxyz[0][0], dxyz[1][0], dxyz[2][0]); return new PVector((e.z/d.z)*d.x+e.x, (e.z/d.z)*d.y+e.y); } // Matrixmultiplikation float[][] matmul(float[][] m1, float[][] m2) { int cols_m1 = m1.length, rows_m1 = m1[0].length; int cols_m2 = m2.length, rows_m2 = m2[0].length; try { if (rows_m1 != cols_m2) throw new Exception("Rows of m1 must match Columns of m2!"); } catch(Exception e) { println(e); } float[][] res = new float[cols_m2][rows_m2]; for (int c=0; c < cols_m1; c++) { for (int r2=0; r2 < rows_m2; r2++) { float sum = 0; float[] buf = new float[rows_m1]; // Multiply rows of m1 with columns of m2 and store in buf for (int r=0; r < rows_m1; r++) { buf[r] = m1[c][r]* m2[r][r2]; } // Add up all entries into sum for (float entry : buf) { sum += entry; } res[c][r2] = sum; } } return res; } void draw() { cam_angle = new PVector(0.01*(mouseY-width/2), 0.01*(mouseX-height/2), 0); background(255); translate(width/2, height/2); strokeWeight(1); fill(0); PVector[] points_projected = new PVector[points.length]; for (int i=0; i < points.length; i++) { points_projected[i] = applyPerspective(points[i]); } for (int i=0; i < points_projected.length; i++) { for (int a=0; a < points_projected.length; a++) { // Alle Punkte verbinden line(points_projected[i].x, points_projected[i].y, points_projected[a].x, points_projected[a].y); } } } void keyPressed() { if (key == 'w') { cam.add(Rotate3d(new PVector(0, 0, -20), cam_angle)); } if (key == 'a') { cam.add(Rotate3d(new PVector(20, 0, 0), cam_angle)); } if (key == 's') { cam.add(Rotate3d(new PVector(0, 0, 20), cam_angle)); } if (key == 'd') { cam.add(Rotate3d(new PVector(-20, 0, 0), cam_angle)); } }
已尝试的操作
调整显示面参数e的取值:
// e = displays surface position relative to camera pinhole c PVector e = new PVector(0, 0, 1000);
但问题未解决。
问题原因及修复方案
1. 鼠标角度映射维度错误
原因:draw()中cam_angle的计算将mouseY与width/2做差,mouseX与height/2做差,属于维度不匹配(高度轴对应高度值,宽度轴对应宽度值),导致Y轴旋转方向异常,引发翻转。
修复:修改cam_angle的计算逻辑:
cam_angle = new PVector(0.01*(mouseY - height/2), 0.01*(mouseX - width/2), 0);
2. 相机旋转矩阵符号与顺序错误
原因:applyPerspective中的X轴、Y轴旋转矩阵符号不符合右手坐标系的相机空间转换规则,且矩阵乘法顺序颠倒,导致坐标旋转后出现翻转。
修复:替换为正确的旋转矩阵,并调整乘法顺序:
float[][] dxyz = matmul( matmul(new float[][]{ {cos(co.z), -sin(co.z), 0}, {sin(co.z), cos(co.z), 0}, {0, 0, 1} }, new float[][]{ {cos(co.y), 0, sin(co.y)}, {0, 1, 0}, {-sin(co.y), 0, cos(co.y)} }), matmul(new float[][]{ {1, 0, 0}, {0, cos(co.x), -sin(co.x)}, {0, sin(co.x), cos(co.x)} }, new float[][]{ {p.x - c.x}, {p.y - c.y}, {p.z - c.z}, }) );
该顺序对应先对平移后的点应用X轴旋转,再Y轴、Z轴旋转,符合世界坐标到相机坐标的转换逻辑,同时矩阵符号匹配右手坐标系,避免翻转。
3. 额外验证:旋转函数一致性
确保Rotate3d函数的旋转顺序(X→Y→Z)与相机转换中的矩阵顺序一致,避免因旋转顺序冲突导致的异常。
内容的提问来源于stack exchange,提问作者mardari
相关产品推荐
相关产品推荐

