无矩阵3D投影在负距离场景下的坐标异常问题求助
无矩阵3D投影在负距离场景下的坐标异常问题求助
最近我在手写一个不用投影矩阵的3D透视渲染,纯靠乘除法来计算屏幕坐标,但遇到了一个卡了好久的问题:当顶点移动到相机后方(也就是相对相机的z坐标为负的时候),计算出来的屏幕坐标会直接镜像错乱,导致图形变形得一塌糊涂。
虽然我本来想忽略完全在相机后方的三角形,但现在遇到了部分顶点在相机后的情况——我需要正确处理这些顶点的坐标才能画出正常的边缘,总不能直接把半可见的多边形全丢了吧?
先把我的代码贴出来,方便大家帮我定位问题:
Main 类
package def_pack; import java.io.IOException; import javax.swing.JFrame; public class Main { public static void main(String[] args) throws IOException { JFrame f = new JFrame("3D Test"); f.setSize(1920, 1080); f.setResizable(false); f.setLocationRelativeTo(null); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GamePanel gamePanel = new GamePanel(); f.add(gamePanel); gamePanel.setSize(1920,1080); gamePanel.startThread(); f.setVisible(true); } }
GamePanel 类
package def_pack; import java.awt.Graphics; import java.awt.Graphics2D; import java.io.IOException; import javax.swing.JPanel; public class GamePanel extends JPanel implements Runnable{ public Thread thr; public scene s; GamePanel() throws IOException{ s = new scene(); } @Override public void run() { while (true) { try { Thread.sleep(1); } catch (InterruptedException e) {e.printStackTrace();} try { update(); repaint(); } catch (IOException e) {e.printStackTrace();} } } public void update() throws IOException { s.update(); } public void paintComponent(Graphics g) { Graphics2D g2d= (Graphics2D)g; g2d.clearRect(0, 0, 1920, 1080); try { s.draw(g2d); } catch (InterruptedException e) {e.printStackTrace();} } public void startThread() { thr = new Thread(this); thr.start(); } }
vertex3D 类
package def_pack; public class vertex3D { public double x,y,z; public double[] transformedCoords= {0,0,0}; vertex3D(double x, double y, double z){ this.x=x; this.y=-y; this.z=z; } public void update(double camX, double camY, double camZ) { // 计算相对于相机的坐标 this.transformedCoords[0]=this.x-camX; this.transformedCoords[1]=this.y-camY; this.transformedCoords[2]=this.z-camZ; } }
scene 类
package def_pack; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.geom.Area; import java.io.IOException; import java.util.ArrayList; public class scene { public double camX=0; public double camY=0; public double camZ=0; public vertex3D[] testQuad = {new vertex3D(-3,-2,2),new vertex3D(-3,-2,4),new vertex3D(-3,3,4),new vertex3D(-3,3,2)}; public int meshesAmount; public ArrayList<vertex3D[]> meshes = new ArrayList<>(); public Rectangle screen = new Rectangle(0,0,1920,1080); public int scrW=1920; public int scrH=1080; public int sizer=300; scene() throws IOException { meshes.add(testQuad); meshesAmount=meshes.size(); } public void update(){ // 相机慢慢向前移动,触发顶点进入相机后方的情况 camZ+=0.001; for (int i=0;i<meshesAmount;i++) { for (int j=0;j<meshes.get(i).length;j++) { meshes.get(i)[j].update(camX,camY,camZ); } } } public void draw(Graphics2D g2d) throws InterruptedException { for (int i=0;i<meshesAmount;i++) { // 只要有一个顶点在相机前就绘制这个多边形 boolean drawable=false; for (int j=0;j<meshes.get(i).length;j++) { if (meshes.get(i)[j].transformedCoords[2]>0) { drawable=true; } } if (drawable) { int nPoints=meshes.get(i).length; int xPoints[] = new int[nPoints]; int yPoints[]=new int[nPoints]; for (int j=0;j<meshes.get(i).length;j++) { // 核心的透视计算:用相对z坐标除x、y得到归一化坐标 double newX=meshes.get(i)[j].transformedCoords[0]/(meshes.get(i)[j].transformedCoords[2]); double newY=meshes.get(i)[j].transformedCoords[1]/(meshes.get(i)[j].transformedCoords[2]); // 缩放并平移到屏幕中心 newX*=sizer; newX/=scrW/(double)scrH; newY*=sizer; newX+=scrW/2; newY+=scrH/2; xPoints[j]=(int)(newX); yPoints[j]=(int)(newY); } g2d.setColor(Color.cyan); Polygon p = new Polygon(xPoints, yPoints, xPoints.length); Area polyArea = new Area(p); Area screenArea = new Area(screen); polyArea.intersect(screenArea); g2d.fill(polyArea); } } } }
我已经试过的办法(都没解决问题)
- 尝试给相机后的顶点z坐标取倒数取反:
if (behindCam[j]) { meshes.get(i)[j].transformedCoords[2]=-1/meshes.get(i)[j].transformedCoords[0]; }
结果是当z在0到-1之间时,计算出来的坐标直接爆炸,虽然z<-1后看起来正常,但中间这段完全没法用。
- 尝试直接把投影后的x、y取反:
newX*=-1; newY*=-1;
这只是简单的镜像,透视比例完全不对,图形还是畸形的。
- 尝试直接把z坐标取反:
meshes.get(i)[j].transformedCoords[2]=-meshes.get(i)[j].transformedCoords[2];
相当于把相机后的顶点强行拉到前面,透视效果假得离谱,完全不是我要的。
- 还试过把z坐标限制在0.0001避免负数,但这样透视的近大远小直接失效,看起来就像2D图形平移,完全没3D感了。
问题表现的几个阶段
- 初始状态:四边形正常显示,透视效果也对
- 顶点接近相机z=0时:图形开始出现明显变形
- 一半四边形在相机后时:直接镜像错乱,图形完全没法看
现在我需要的是:当顶点在相机后方时,能正确计算它的屏幕坐标(或者正确处理这种部分可见的多边形),而不是出现镜像或者坐标爆炸的情况。有没有大佬能帮我分析一下问题出在哪,或者给个可行的解决思路?
内容来源于stack exchange
相关产品推荐
相关产品推荐

