You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让自定义绘制的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:相机后方画面错乱修复

原代码的临时可见性判断逻辑粗糙,且未处理投影公式中分母为负/零的情况,导致坐标异常。

  1. 给DimensionPoint添加可见性判断方法:
public boolean isInFront(int cameraDistance) {
    return cameraDistance + z > 0; // 确保投影分母为正,避免翻转或除零
}
  1. 修改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));
    }
}
  1. 移除draw方法中的临时判断:
public void draw(Graphics g){
    g.setColor(Color.black);
    drawCube(g,cube);
}
  1. 优化投影公式的临时处理,直接通过可见性判断避免除零:
public int getXprojected(int cameraDistance) {
    return x * cameraDistance / (cameraDistance + z);
}
public int getYprojected(int cameraDistance) {
    return y * cameraDistance / (cameraDistance + z);
}

内容的提问来源于stack exchange,提问作者JoeSwansonJunior999

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:37:43