如何让自定义绘制的3D立方体数学正确?解决渲染异常问题
Java Swing 3D立方体绘制问题排查与修复
存在的问题
- 立方体左右两侧显示尺寸不一致(理论应完全相同)
- 立方体位于相机后方时画面持续出现错乱(glitch)
精简代码
GameFrame.java
import javax.swing.JFrame; public class GameFrame extends JFrame{ public GameFrame(){ this.add(new GamePanel()); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); this.setResizable(false); this.pack(); this.setLocationRelativeTo(null); } public static void main(String[] args) { new GameFrame(); } // end of main } // end of class GameFrame
DimensionPoint.java
public class DimensionPoint { int x,y,z = 0; public DimensionPoint(int x, int y, int z){ this.x = x; this.y = y; this.z = z; } public int getXprojected(int length){ return length == -z ? x*length/(length+z+1) : x*length/(length+z); //Temporary, because you can't divide by zero } public int getYprojected(int length){ return length == -z ? x*length/(length+z+1): y*length/(length+z); } public int getZ(){ return z; } } // end of class Point
GamePanel.java
import java.awt.event.*; import javax.swing.*; import java.awt.*; public class GamePanel extends JPanel implements ActionListener{ static final int width = 800; static final int height = 800; DimensionPoint[] cube = new DimensionPoint[8]; int pos = 75; public GamePanel(){ this.setPreferredSize(new Dimension(width,height)); this.setBackground(Color.gray); this.setFocusable(true); setCube(100,400,10,200,cube); } public void paintComponent(Graphics g){ super.paintComponent(g); draw(g); } public void draw(Graphics g){ g.setColor(Color.black); if (cube[5].getZ() > -pos+9) { // This is also only temporary. When you change the size of the cube, it sometimes still glitches out, or dissapears before leaving the screen drawCube(g,cube); } } public void setCube(int anfangX, int anfangY, int anfangZ,int size, DimensionPoint[] p){ p[0] = new DimensionPoint(anfangX,anfangY-size,anfangZ); p[1] = new DimensionPoint(anfangX,anfangY,anfangZ); p[2] = new DimensionPoint(anfangX+size,anfangY-size,anfangZ); p[3] = new DimensionPoint(anfangX+size,anfangY,anfangZ); p[4] = new DimensionPoint(anfangX,anfangY-size,anfangZ+size/10); p[5] = new DimensionPoint(anfangX,anfangY,anfangZ+size/10); p[6] = new DimensionPoint(anfangX+size,anfangY-size,anfangZ+size/10); p[7] = new DimensionPoint(anfangX+size,anfangY,anfangZ+size/10); } public void drawCube(Graphics g,DimensionPoint[] p){ g.drawLine(p[0].getXprojected(pos),p[0].getYprojected(pos),p[1].getXprojected(pos),p[1].getYprojected(pos)); g.drawLine(p[0].getXprojected(pos),p[0].getYprojected(pos),p[2].getXprojected(pos),p[2].getYprojected(pos)); g.drawLine(p[0].getXprojected(pos),p[0].getYprojected(pos),p[4].getXprojected(pos),p[4].getYprojected(pos)); g.drawLine(p[1].getXprojected(pos),p[1].getYprojected(pos),p[3].getXprojected(pos),p[3].getYprojected(pos)); g.drawLine(p[1].getXprojected(pos),p[1].getYprojected(pos),p[5].getXprojected(pos),p[5].getYprojected(pos)); g.drawLine(p[2].getXprojected(pos),p[2].getYprojected(pos),p[6].getXprojected(pos),p[6].getYprojected(pos)); g.drawLine(p[2].getXprojected(pos),p[2].getYprojected(pos),p[3].getXprojected(pos),p[3].getYprojected(pos)); g.drawLine(p[3].getXprojected(pos),p[3].getYprojected(pos),p[7].getXprojected(pos),p[7].getYprojected(pos)); g.drawLine(p[4].getXprojected(pos),p[4].getYprojected(pos),p[5].getXprojected(pos),p[5].getYprojected(pos)); g.drawLine(p[4].getXprojected(pos),p[4].getYprojected(pos),p[6].getXprojected(pos),p[6].getYprojected(pos)); g.drawLine(p[7].getXprojected(pos),p[7].getYprojected(pos),p[6].getXprojected(pos),p[6].getYprojected(pos)); g.drawLine(p[7].getXprojected(pos),p[7].getYprojected(pos),p[5].getXprojected(pos),p[5].getYprojected(pos)); } @Override public void actionPerformed(ActionEvent e){ } } // end of class GamePanel
修复建议
问题1:左右两侧尺寸不一致修复
核心错误是DimensionPoint类的getYprojected方法中,临时处理分支错误使用了x代替y,导致Y轴投影计算错误。同时立方体Z轴深度设置过小,视觉上被压扁。
修正后的DimensionPoint相关代码:
public int getYprojected(int length){ return length == -z ? y*length/(length+z+1): y*length/(length+z); }
修正setCube方法中的Z轴深度,改为正立方体:
p[4] = new DimensionPoint(anfangX,anfangY-size,anfangZ+size); p[5] = new DimensionPoint(anfangX,anfangY,anfangZ+size); p[6] = new DimensionPoint(anfangX+size,anfangY-size,anfangZ+size); p[7] = new DimensionPoint(anfangX+size,anfangY,anfangZ+size);
问题2:相机后方画面错乱修复
原代码的临时可见性判断逻辑粗糙,且未处理投影公式中分母为负/零的情况,导致坐标异常。
- 给
DimensionPoint添加可见性判断方法:
public boolean isInFront(int cameraDistance) { return cameraDistance + z > 0; // 确保投影分母为正,避免翻转或除零 }
- 修改
GamePanel,添加线段可见性检查后再绘制:
public void drawCube(Graphics g,DimensionPoint[] p){ drawLineIfVisible(g, p[0], p[1], pos); drawLineIfVisible(g, p[0], p[2], pos); drawLineIfVisible(g, p[0], p[4], pos); drawLineIfVisible(g, p[1], p[3], pos); drawLineIfVisible(g, p[1], p[5], pos); drawLineIfVisible(g, p[2], p[6], pos); drawLineIfVisible(g, p[2], p[3], pos); drawLineIfVisible(g, p[3], p[7], pos); drawLineIfVisible(g, p[4], p[5], pos); drawLineIfVisible(g, p[4], p[6], pos); drawLineIfVisible(g, p[7], p[6], pos); drawLineIfVisible(g, p[7], p[5], pos); } private void drawLineIfVisible(Graphics g, DimensionPoint p1, DimensionPoint p2, int cameraDistance) { if (p1.isInFront(cameraDistance) && p2.isInFront(cameraDistance)) { g.drawLine(p1.getXprojected(cameraDistance), p1.getYprojected(cameraDistance), p2.getXprojected(cameraDistance), p2.getYprojected(cameraDistance)); } }
- 移除
draw方法中的临时判断:
public void draw(Graphics g){ g.setColor(Color.black); drawCube(g,cube); }
- 优化投影公式的临时处理,直接通过可见性判断避免除零:
public int getXprojected(int cameraDistance) { return x * cameraDistance / (cameraDistance + z); } public int getYprojected(int cameraDistance) { return y * cameraDistance / (cameraDistance + z); }
内容的提问来源于stack exchange,提问作者JoeSwansonJunior999
相关产品推荐
相关产品推荐

