0

I am trying to make my camera representation work for images whose aspect ratio isn't 1 (640x480, 1920x1080...), but I am having some trouble getting it to work.

The camera model is pretty simple, as it's a pinhole camera with (F)orward, (L)eft and (U)p vectors, and a point in space which serves as its (O)rigin. F points towards the center of the image plane, and U and L are perpendicular with each other and with F, forming the camera's coordinate space.

The way it works is that I define everything except the L vector, which is the normalized cross product of F and U multiplied by the aspect ratio, so that its length is "as wide" as the image is going to be compared to its height.

Then, to get a ray pointing at (i, j), I just create a ray starting at O and pointing at the top-left corner (U + F + L)

The code is this:

class Camera {
public:
    Direction L, U, F;
    Point O;
    size_t width, height;
    size_t rays_per_pixel;

private:
    // For randomizing ray's directions inside the pixel's square
    static std::mt19937 gen;
    static std::uniform_real_distribution<double> pixel_distr;

public:

    Camera(Point _O, Direction _U, Direction _F, size_t _width, size_t _height, size_t _rays_per_pixel) :
            U(_U),
            F(_F),
            O(_O),
            width(_width),
            height(_height),
            rays_per_pixel(_rays_per_pixel) {
        // L perpendicular to F and U, then multiplied with the aspect ratio

        double aspect_ratio = ((double) _width / (double) _height);
        // * between vectors is cross product, 
        // .v just addresses their internal vector class
        L = (F.v * U.v).normalize() * aspect_ratio;
    }


    // Return a ray pointing from O to a pixel in the image, with a small 
    // random variation across the pixel's area
    [[nodiscard]] Ray get_ray(size_t _i, size_t _j) const {
        // Puts the ray in the pixel's center, then adds to it a random value between [0, 0.5)
        double i = (double) _i + 0.5 + pixel_distr(gen);
        double j = (double) _j + 0.5 + pixel_distr(gen);

        return {
            O, // Origin

            Direction((U.v + F.v + L.v                      // Top left corner
                    - ((2*L.v.modulus()*L.v)/((double) width) * j)        // Right advance (as a substraction)
                    - ((2*U.v.modulus()*U.v)/((double) height) * i)))     // Downwards advance (as a substraction)
        }; //
    }

If I indicate width and height with an aspect ratio of 1, I get this, with 512x512 as an example (ignore the texture, I know) non borked:

If I indicate width and height with an aspect ratio of 2 or whatever else, like 640x480 I get this, which at least is somewhat hilarious:

borked

The vectors used are:

Point O(0, 0, -3.5);

Direction U(0, 1, 0);

Direction F(0, 0, 3);

And L in the first case is Direction L(-1, 0, 0) whereas in the second case is L(-2, 0, 0).

It seems to repeat the image vertically with a pattern, and I made sure the logic error shouldn't be outside of this (I ask for every single pixel across the plane correctly, etc.)

Is something else needed to get a "widescreen" image? Should I change the camera model?

Lightsong
  • 312
  • 2
  • 8
  • 1
    the output looks more like pixelformat mismatch than aspect ratio problem ... take a look at [GLSL 3D Mesh back raytracer](https://stackoverflow.com/a/45140313/2521214) for some inspiration on how I deal with camera there (vertex shader) – Spektre Nov 26 '22 at 16:47
  • just an idea if after change from 1:1 aspect to 2:1 you got 2 images ... then you most likely multiplied instead of divide the x axis ray spread by aspect (or viceversa) – Spektre Nov 28 '22 at 07:34
  • 1
    @Spektre Hi, thanks! I actually noticed there was a computer graphics community so I posted it there and forgot about here. It was actually really dumb: I was indexing the flattened matrix for the image as `i*height + j` instead of `i*width + j`...thank you nonetheless :) – Lightsong Nov 29 '22 at 20:26

0 Answers0