0
\$\begingroup\$

I'm trying to figure out back-face culling, with little results.

My code, from what i've gathered:

    int a = tri[t].t[0], b = tri[t].t[1], c = tri[t].t[2]; // point identifiers from a triangle array
    Point3D pa = draw[a], pb = draw[b], pc = draw[c]; // points of a triangle
    Point3D la = new Point3D(pb.x - pa.x, pb.y - pa.y, pb.z - pa.z); // Line a            
    Point3D lb = new Point3D(pc.x - pa.x, pc.y - pa.y, pc.z - pa.z); // Line b

    Point3D cross = new Point3D(la.y * lb.z - la.z * lb.y, la.z * lb.x - la.x * lb.z, la.x * lb.y - la.y * lb.x);

    cross.x = cross.x * pa.x;
    cross.y = cross.y * pa.y;
    cross.z = cross.z * pa.z;           

    if(cross.z < 0.0){
          // draw triangle
    }

On the left looks like with the above code, after fixing it and adding projection, the culling is broken again, as seen on right.

Backface-distortion Backface-dirtotion2

So what I'm asking is, what am I doing wrong?

A whole example to duplicate gif:

  • Updated for flexibiilty
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class Main extends JPanel {
    
    public class LINE {
        public LINE () {
            this.slope = 0.0;
            this.start = 0.0;
            this.steps = 0.0;
            this.end = 0.0;
            this.rev = false;            
        }
        
        public double slope, start, steps, end;
        public boolean rev;

        public void Calc(Point3D from, Point3D to){
            Point3D a = from, b = to;
            double slope = 0.0, steps = 0.0, start = 0.0, end = 0.0;
            boolean rev = false;
            if(a.x != b.x) slope = (b.y - a.y) / (b.x - a.x);

            if(a.x != b.x && Math.abs(slope) <= 1.0){
                if(a.x > b.x){
                    a = to;
                    b = from;
                }
                start = a.x;
                end = b.x;
                steps = a.y - slope * a.x;
            } else {
                if(a.y > b.y){
                    a = to;
                    b = from;
                }            
                slope = (b.x - a.x) / (b.y - a.y);
                start = a.y;
                end = b.y;
                steps = a.x - slope * a.y;
                rev = true;
            }   
            this.slope = slope;
            this.end = end;
            this.start = start;
            this.rev = rev;
            this.steps = steps;
        }
    }

    static public class Point3D {
        public Point3D (double x, double y, double z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public Point3D (int x, int y, int z) {
            this.x = (double)x;
            this.y = (double)y;
            this.z = (double)z;
        }
        public double x, y, z;
        
    }

    static public class CONT {
        CONT(int a, int b, int c){
            this.t = new int[3];
            t[0] = a;
            t[1] = b;
            t[2] = c;
        }

        public int[] t;

    }
    public Main () {
        
    }

    public static Point3D[] points = new Point3D[8], draw = new Point3D[8], calc = new Point3D[8];
    public static int[][] lines = new int[6][4];
    public static CONT[] tri = new CONT[12];
    public static boolean[] dt = new boolean[12];
    public static Point3D center = new Point3D(0, 0, 0);
    public static Point3D rotation = new Point3D(0, 0, 0);

    public static void main(String[] args){

        JFrame frame = new JFrame();
        Dimension size = new Dimension(640, 480);
        Main canvas = new Main();
        canvas.setPreferredSize(size);
        canvas.setBackground(new Color(0, 0, 0));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(canvas);
        frame.pack();
        frame.setLocation(10, 10);
        frame.setTitle("Title");
        frame.setVisible(true);

        points[0] = new Point3D(-1.0, -1.0, -1.0);
        points[1] = new Point3D(1.0, -1.0, -1.0);
        points[2] = new Point3D(1.0, 1.0, -1.0);
        points[3] = new Point3D(-1.0, 1.0, -1.0);
        points[4] = new Point3D(-1.0, -1.0, 1.0);
        points[5] = new Point3D(1.0, -1.0, 1.0);
        points[6] = new Point3D(1.0, 1.0, 1.0);
        points[7] = new Point3D(-1.0, 1.0, 1.0);
        
        draw[0] = new Point3D(-1.0, -1.0, -1.0);
        draw[1] = new Point3D(1.0, -1.0, -1.0);
        draw[2] = new Point3D(1.0, 1.0, -1.0);
        draw[3] = new Point3D(-1.0, 1.0, -1.0);
        draw[4] = new Point3D(-1.0, -1.0, 1.0);
        draw[5] = new Point3D(1.0, -1.0, 1.0);
        draw[6] = new Point3D(1.0, 1.0, 1.0);
        draw[7] = new Point3D(-1.0, 1.0, 1.0);

        calc[0] = new Point3D(-1.0, -1.0, -1.0);
        calc[1] = new Point3D(1.0, -1.0, -1.0);
        calc[2] = new Point3D(1.0, 1.0, -1.0);
        calc[3] = new Point3D(-1.0, 1.0, -1.0);
        calc[4] = new Point3D(-1.0, -1.0, 1.0);
        calc[5] = new Point3D(1.0, -1.0, 1.0);
        calc[6] = new Point3D(1.0, 1.0, 1.0);
        calc[7] = new Point3D(-1.0, 1.0, 1.0);

        
        lines[0][0] = 0;
        lines[0][1] = 1;
        lines[0][2] = 2;
        lines[0][3] = 3;

        lines[1][0] = 4;
        lines[1][1] = 5;
        lines[1][2] = 6;
        lines[1][3] = 7;

        lines[2][0] = 0;
        lines[2][1] = 4;
        lines[2][2] = 7;
        lines[2][3] = 3;

        lines[3][0] = 1;
        lines[3][1] = 5;
        lines[3][2] = 6;
        lines[3][3] = 2;

        lines[4][0] = 0;
        lines[4][1] = 4;
        lines[4][2] = 5;
        lines[4][3] = 1;

        lines[5][0] = 3;
        lines[5][1] = 7;
        lines[5][2] = 6;
        lines[5][3] = 2;

        tri[0] = new CONT(0, 1, 3);
        tri[1] = new CONT(1, 2, 3);
        tri[2] = new CONT(0, 4, 7);
        tri[3] = new CONT(0, 3, 7);
        tri[4] = new CONT(1, 5, 2);
        tri[5] = new CONT(2, 6, 5);
        tri[6] = new CONT(4, 5, 7);
        tri[7] = new CONT(5, 6, 7);
        tri[8] = new CONT(0, 4, 1);
        tri[9] = new CONT(4, 5, 1);
        tri[10] = new CONT(3, 7, 2);
        tri[11] = new CONT(7, 6, 2);

        for(int i = 0; i < 8; i++) {
            center.x += points[i].x;
            center.y += points[i].y;
            center.z += points[i].z;
        }
        center.x /= 8;
        center.y /= 8;
        center.z /= 8;
        while(true){         
            RotatePoints();     
            canvas.repaint();
            try{
            Thread.sleep(16);
            } catch(InterruptedException e) {}
        }
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(new Color(125, 23, 238));
        LINE line = new LINE();
        for(int t = 0; t < 12; t++){
            int a = tri[t].t[0], b = tri[t].t[1], c = tri[t].t[2]; // point identifiers from a triangle array
            Point3D pa = calc[a], pb = calc[b], pc = calc[c]; // points of a triangle
            Point3D la = new Point3D(pb.x - pa.x, pb.y - pa.y, pb.z - pa.z); // Line a            
            Point3D lb = new Point3D(pc.x - pa.x, pc.y - pa.y, pc.z - pa.z); // Line b

            Point3D cross = new Point3D(la.y * lb.z - la.z * lb.y, la.z * lb.x - la.x * lb.z, la.x * lb.y - la.y * lb.x);
            double e = Math.sqrt(cross.x * cross.x + cross.y * cross.y + cross.z * cross.z);
            cross.z /= e;
            dt[t] = (cross.z < 0.0);
            //cross.z /= e;
            /*
            double size = 20.0;
            */
        }
        double size = 120.0;
        for(int p = 0; p < 8; p++){
            draw[p].x = calc[p].x * size + 250.0;
            draw[p].y = calc[p].y * size + 250.0;
            draw[p].z = calc[p].z * size + 250.0;
        }

        for(int t = 0; t < 12; t++){
            if(dt[t]){   
                int a = tri[t].t[0], b = tri[t].t[1], c = tri[t].t[2]; // point identifiers from a triangle array
                Point3D pa = draw[a], pb = draw[b], pc = draw[c]; // points of a triangle
                line.Calc(pa, pb);
                for(; line.start < line.end; line.start += 1.0){
                    double w = line.slope * line.start + line.steps;
                    if(line.rev){
                        g.drawRect((int)w, (int)line.start, 1, 1);
                    } else {
                        g.drawRect((int)line.start, (int)w, 1, 1);
                    }
                }
                
                line.Calc(pb, pc);
                for(; line.start < line.end; line.start += 1.0){
                    double w = line.slope * line.start + line.steps;
                    if(line.rev){
                        g.drawRect((int)w, (int)line.start, 1, 1);
                    } else {
                        g.drawRect((int)line.start, (int)w, 1, 1);
                    }
                }
                line.Calc(pc, pa);
                for(; line.start < line.end; line.start += 1.0){
                    double w = line.slope * line.start + line.steps;
                    if(line.rev){
                        g.drawRect((int)w, (int)line.start, 1, 1);
                    } else {
                        g.drawRect((int)line.start, (int)w, 1, 1);
                    }
                }
            }
        }
        

    }

    public static void RotatePoints(){
        rotation.x += 0.25;
        rotation.y += 0.5;
        rotation.z += 0.75;

        double sx = Math.sin(Math.toRadians(rotation.x)), sy = Math.sin(Math.toRadians(rotation.y)), sz = Math.sin(Math.toRadians(rotation.z));
        double cx = Math.cos(Math.toRadians(rotation.x)), cy = Math.cos(Math.toRadians(rotation.y)), cz = Math.cos(Math.toRadians(rotation.z));
        center = new Point3D(0, 0, 0);
        for(int i = 0; i < 8; i++){
            Point3D spot = points[i];//new Point3D(points[i].x, points[i].y, points[i].z);
            calc[i].y = (cx * (spot.y - center.y) - sx * (spot.z - center.z)) + center.y;
            calc[i].z = (sx * (spot.y - center.y) + cx * (spot.z - center.z)) + center.z;
            spot = new Point3D(spot.x, calc[i].y, calc[i].z);
            calc[i].x = (cy * (spot.x - center.x) - sy * (spot.z - center.z)) + center.x;
            calc[i].z = (sy * (spot.x - center.x) + cy * (spot.z - center.z)) + center.z;
            spot = new Point3D(calc[i].x, calc[i].y, calc[i].z);
            calc[i].x = (cz * (spot.x - center.x) - sz * (spot.y - center.y)) + center.x;
            calc[i].y = (cz * (spot.y - center.y) + sz * (spot.x - center.x)) + center.y;
        }    
    }

}
\$\endgroup\$
6
  • \$\begingroup\$ I don't know why you're multiplying cross by pa — can you clarify what the reasoning for that is, or what source suggested to do that? \$\endgroup\$
    – DMGregory
    Commented Apr 21 at 14:11
  • \$\begingroup\$ @DMGregory It is just what I ended up with, was just playing around with variables hoping to get a better result. \$\endgroup\$
    – Valtsuh
    Commented Apr 21 at 14:19
  • \$\begingroup\$ What behaviour do you see without that? Can you record a fresh gif? \$\endgroup\$
    – DMGregory
    Commented Apr 21 at 14:54
  • \$\begingroup\$ @DMGregory The behaviour is, from what I can tell without posting another gif, exactly the same. I can't tell the difference. \$\endgroup\$
    – Valtsuh
    Commented Apr 21 at 15:00
  • \$\begingroup\$ Next thing to check: are your cube's triangles wound the right way? Can you share the vertex/index data you're using? \$\endgroup\$
    – DMGregory
    Commented Apr 21 at 15:40

1 Answer 1

2
\$\begingroup\$

Your triangles have a mix of clockwise and counterclockwise winding.

Try these indices instead, which should all have the same winding:

tri[0] = new CONT(0, 1, 3);
tri[1] = new CONT(1, 2, 3);
tri[2] = new CONT(0, 7, 4);
tri[3] = new CONT(0, 3, 7);
tri[4] = new CONT(1, 5, 2);
tri[5] = new CONT(2, 5, 6);
tri[6] = new CONT(4, 6, 5);
tri[7] = new CONT(4, 7, 6);
tri[8] = new CONT(0, 4, 1);
tri[9] = new CONT(4, 5, 1);
tri[10] = new CONT(3, 2, 7);
tri[11] = new CONT(7, 2, 6);

You can also delete these lines, since they don't change the sign of cross.z:

 double e = Math.sqrt(cross.x * cross.x + cross.y * cross.y + cross.z * cross.z);
 cross.z /= e;
\$\endgroup\$
2
  • \$\begingroup\$ This worked very well, and now the cubes cull accordingly. Next step I tried adding projection, which sort of broke the culling, every triangle but the front are drawn. \$\endgroup\$
    – Valtsuh
    Commented Apr 22 at 14:16
  • 1
    \$\begingroup\$ Flip the sign of your cross.z test or reverse the winding of the triangles then. \$\endgroup\$
    – LudoProf
    Commented Apr 22 at 19:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .