Search code examples
java3dintersectionparametric-equations

How should I be implementing a view clipping plane in a 3D Engine?


This project is written entirely from scratch in Java. I've just been bored ever since Covid started, so I wanted something that would take up my time, and teach me something cool. I've been stuck on this problem for about a week now though. When I try to use my near plane clipping method it skews the new vertices to the opposite side of the screen, but sometimes times it works just fine.

Failure Screenshot

Success Screenshot

So my thought is maybe that since it works sometimes, I'm just not doing the clipping at the correct time in the pipeline?

I start by face culling and lighting,

Then I apply a Camera View Transformation to the Vertices,

Then I clip on the near plane

Finally I apply the projection matrix and Clip any remaining off screen Triangles

Code:

This calculates the intersection points. Sorry if it's messy or to long I'm not very experienced in coding, my major is physics, not CS.

public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){

    float planeDot = planeNorm.dotProduct(planePos);
    float startDot = lineStart.dotProduct(planeNorm);
    float endDot = lineEnd.dotProduct(planeNorm);
    float midPoint = (planeDot - startDot) / (endDot - startDot);

    Vector3d lineStartEnd = lineEnd.sub(lineStart);
    Vector3d lineToIntersect = lineStartEnd.scale(midPoint);

    return new Vertex(lineStart.add(lineToIntersect));
}

public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){

    float x = planeNorm.getX() * vert.getX();
    float y = planeNorm.getY() * vert.getY();
    float z = planeNorm.getZ() * vert.getZ();

    return (x + y + z - (planeNorm.dotProduct(planePos)));
}

//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){

    Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
    Vector3d planeNorm = Z_AXIS.clone();

    final int length = triangles.size();

    for(int i = 0; i < length; i++) {

        Triangle t = triangles.get(i);

        if(!t.isDraw())
            continue;

        Vector3d[] insidePoint = new Vector3d[3];
        int insidePointCount = 0;

        Vector3d[] outsidePoint = new Vector3d[3];
        int outsidePointCount = 0;

        float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
        float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
        float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);

        //Storing distances from plane and counting inside outside points
        {
            if (d0 >= 0){

                insidePoint[insidePointCount] = t.getVerticesVectors()[0];
                insidePointCount++;
            }else{

                outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
                outsidePointCount++;
            }
            if (d1 >= 0){

                insidePoint[insidePointCount] = t.getVerticesVectors()[1];
                insidePointCount++;
            }else{

                outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
                outsidePointCount++;
            }
            if (d2 >= 0){

                insidePoint[insidePointCount] = t.getVerticesVectors()[2];
                insidePointCount++;
            }else{

                outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
            }
        }
     
        //Triangle has 1 point still inside view, remove original triangle add new clipped triangle
        if (insidePointCount == 1) {

            t.dontDraw();

            Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
            Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
            vertices.add(newVert1);
            vertices.add(newVert2);
            
            //Triangles are stored with vertex references instead of the actual vertex object. 
            Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
            temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
            triangles.add(temp);

            continue;
        }


        //Triangle has two points inside remove original add two new clipped triangles
        if (insidePointCount == 2) {

            t.dontDraw();

            Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
            Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
            vertices.add(newVert1);
            vertices.add(newVert2);

            Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
            temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
            triangles.add(temp);

            temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
            temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
            triangles.add(temp);

            continue;
        }
    }
}

Solution

  • I figured out the problem, The new clipped triangles were not being given the correct vertex references. they were just being given the first vertex of the triangle irregardless of if that was inside the view or not.