Search code examples
javaraytracing

Writing a Raytracer, and cant get the image to be centred properly?


I'm writing a Raytracer in Java, I've gotten to the point where I can create objects, rays, test for intersections and then colour pixels. I've also got some basic anti aliasing done. My problem is that if a create a sphere, which should be in the centre of the world (i.e 0.0, 0.0, 0.0) and then draw the image, I end up with a picture like this.

When the red circle should be in the middle of the image.

Main method

public static void main(String[] args) {
    System.out.println("Rendering...");
    long start = System.nanoTime();

    // Setting up the size of the image to be rendered
    world = new World(1920, 1080, 1.0);
    image = new Image("image.png");
    sampler = new SimpleSampler(4);
    projector = new OrthographicProjector();

    // Main loop of program, goes through each pixel in image and assigns a colour value
    for (int y = 0; y < world.viewPlane.height; y++) {
        for (int x = 0; x < world.viewPlane.width; x++) {
            // Render pixel colour
            trace(x, y);
        }
    }

    image.saveImage("PNG");

    long end = System.nanoTime();

    System.out.print("Loop Time = " + ((end - start)/1000000000.0f));
}

Trace method

public static void trace(int x, int y) {    
    Colour colour = new Colour();
    //int colour = RayTracer.world.backgroundColour.toInteger();

    for (int col = 0; col < sampler.samples; col++) {
        for (int row = 0; row < sampler.samples; row++) {
            Point2D point = sampler.sample(row, col, x, y);
            Ray ray = projector.createRay(point);
            double min = Double.MAX_VALUE;
            Colour tempColour = new Colour();

            for (int i = 0; i < world.worldObjects.size(); i++) {
                double temp = world.worldObjects.get(i).intersect(ray);

                if (temp != 0 && temp < min) {
                    min = temp;
                    tempColour = world.worldObjects.get(i).colour;
                }
            }

            colour.add(tempColour);
        }
    }

    colour.divide(sampler.samples*sampler.samples); 
    image.buffer.setRGB(x, y, colour.toInteger());
}

World.java

public class World {
    public ViewPlane viewPlane;
    public ArrayList<Renderable> worldObjects;
    public Colour backgroundColour;

    public World(int width, int height, double size) {
        viewPlane = new ViewPlane(width, height, size);
        backgroundColour = new Colour(0.0f, 0.0f, 0.0f);
        worldObjects = new ArrayList<Renderable>();

        worldObjects.add(new Sphere(new Point3D(0.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
        //worldObjects.add(new Sphere(new Point3D(-150.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
        //worldObjects.add(new Sphere(new Point3D(0.0, -540.0, 0.0), 50, new Colour(0.0f, 1.0f, 0.0f)));

    }
}

SimpleSampler.java

public class SimpleSampler extends Sampler {
    public SimpleSampler(int samples) {
        this.samples = samples;
    }

    public Point2D sample(int row, int col, int x, int y) {
        Point2D point = new Point2D(
            x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
            y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);

        return point;
    }
}

OrthographicProjector.java

public class OrthographicProjector extends Projector{
    public Ray createRay(Point2D point) {
        Ray ray = new Ray();

        ray.origin = new Point3D(
            RayTracer.world.viewPlane.size * point.x,
            RayTracer.world.viewPlane.size * point.y,
            100);
        ray.direction = new Vector3D(0.0, 0.0, -1.0);

        return ray;
    }
}

I have a feeling that somewhere along the way I've mixed an x with a y and this has rotated the image, but I haven't been able to track down the problem. If you would like to see any more of my code I would be happy to show it.


Solution

  • In SimpleSampler.java:

    Point2D point = new Point2D(
        x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
        y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);
    

    You use width for both coordinates. Maybe you should use width and height.