<p>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.</p>
<p>The camera model is pretty simple, as it's a pinhole camera with <code>(F)orward</code>, <code>(L)eft</code> and <code>(U)p</code> vectors, and a point in space which serves as its <code>(O)rigin</code>. <code>F</code> points towards the center of the image plane, and <code>U</code> and <code>L</code> are perpendicular with each other and with <code>F</code>, forming the camera's coordinate space.</p>
<p>The way it works is that I define everything except the <code>L</code> vector, which is the normalized cross product of <code>F</code> and <code>U</code> multiplied by the aspect ratio, so that its length is &quot;as wide&quot; as the image is going to be compared to its height.</p>
<p>Then, to get a ray pointing at <code>(i, j)</code>, I just create a ray starting at <code>O</code> and pointing at the top-left corner (<code>U + F + L</code>)</p>
<p>The code is this:</p>
<pre class="lang-cpp prettyprint-override"><code>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&lt;double&gt; 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)
        }; //
    }

</code></pre>
<p>If I indicate <code>width</code> and <code>height</code> with an aspect ratio of <code>1</code>, I get this, with 512x512 as an example (ignore the texture, I know)
<a href="https://i.sstatic.net/2b0yC.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/2b0yC.png" alt="non borked" /></a>:</p>
<p>If I indicate <code>width</code> and <code>height</code> with an aspect ratio of <code>2</code> or whatever else, like 640x480 I get this, which at least is somewhat hilarious:</p>
<p><a href="https://i.sstatic.net/4Axab.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/4Axab.jpg" alt="borked" /></a></p>
<p>The vectors used are:</p>
<p><code>Point O(0, 0, -3.5);</code></p>
<p><code>Direction U(0, 1, 0);</code></p>
<p><code>Direction F(0, 0, 3);</code></p>
<p>And <code>L</code> in the first case is Direction <code>L(-1, 0, 0)</code> whereas in the second case is <code>L(-2, 0, 0)</code>.</p>
<p>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.)</p>
<p>Is something else needed to get a &quot;widescreen&quot; image? Should I change the camera model?</p>
<p>Edit:</p>
<p>Outside of the camera stuff, a rendering job writes to the image like this, which itself is a vector representing the flattened matrix of dimensions <code>[height][width]</code>:</p>
<pre class="lang-cpp prettyprint-override"><code>void rendering_job(const Scene &amp;scene, std::vector&lt;Vector3d&gt; &amp;img, size_t i, size_t j) {
    Vector3d temp_emission;

    size_t number_of_bounces = 0;
    for (size_t r = 0; r &lt; scene.camera.rays_per_pixel; r++) {
        Ray ray = scene.camera.get_ray(i, j);

        temp_emission = temp_emission + integrator_sample(scene, ray, number_of_bounces);
    }

    // Printing every single index access shows that
    // every single pixel gets addressed (from 0 to 
    // scene.camera.height*scene.camera.width-1)
    img[i*scene.camera.height + j] = temp_emission / scene.camera.rays_per_pixel; 
}

</code></pre>
<p>It then gets passed to a module which writes the PPM image like this (the factor and MAX stuff is for doing tonemapping):</p>
<pre class="lang-cpp prettyprint-override"><code>void write(std::string nombFich) const {
    double factor = color_resolution / max;

    std::ofstream outfile(nombFich);
    outfile &lt;&lt; std::fixed &lt;&lt; std::setprecision(0);
    outfile &lt;&lt; &quot;P3&quot; &lt;&lt; std::endl;
    outfile &lt;&lt; &quot;# &quot; &lt;&lt; nombFich &lt;&lt; std::endl;
    outfile &lt;&lt; &quot;#MAX=&quot; &lt;&lt; max &lt;&lt; std::endl;
    outfile &lt;&lt; width &lt;&lt; &quot; &quot; &lt;&lt; height &lt;&lt; std::endl;
    outfile &lt;&lt; color_resolution &lt;&lt; std::endl;

    int i = 0;
    for (auto v : img) {
        outfile &lt;&lt; v[0]*factor &lt;&lt; &quot; &quot; &lt;&lt; v[1]*factor &lt;&lt; &quot; &quot; &lt;&lt; v[2]*factor &lt;&lt; &quot; &quot;;

        i++;
        if (i == width - 1) {i = 0; outfile &lt;&lt; std::endl;}
    }
}

</code></pre>
<p>The writing process writes correctly <code>height</code> rows of <code>width</code> values in the PPM.</p>
<p>Edit2:</p>
<p>DAMN...It was so obvious I couldn't see the forest for the trees. I was indexing the image as <code>img[i*scene.camera.height + j]</code> instead of <code>img[i*scene.camera.width + j]</code>...
<a href="https://i.sstatic.net/V8qUm.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/V8qUm.jpg" alt="enter image description here" /></a></p>
<p>I assume that now I should tweak my camera in order to get rid of the stretching?</p>

CC BY-SA 4.0
2022-11-26T17:08:51.303Z
The “repeating pattern” image is a sign that the way you're filling your pixel data buffer (with a linear sequence of pixel values) is not the right shape for the width and height of the image you want to produce. Specifically, it looks like you've produced only 480 pixels for each row, instead of 640 (as if the image was square), so that the next row starts too soon and wraps around. You haven't shown us that part of your code, so I can't tell you how to fix it, but it may be related to or separate from your camera/projection aspect ratio problem.
Kevin Reid
CC BY-SA 4.0
2022-11-26T20:33:19.743Z
@KevinReid You were right, it was a really dumb mistake at indexing the image matrix as an array. Now the result image is as above, I guess it's just refining the "camera setup" from now?
Lightsong
<raytracing><3d><pathtracing><camera>
Trouble getting a raytracer camera representation for different aspect ratios
Lightsong
# Trouble getting a raytracer camera representation for different aspect ratios

(Asked by: username_0 on 2022-11-26)

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:

```cpp
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](https://i.sstatic.net/2b0yC.png)](https://i.sstatic.net/2b0yC.png):

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](https://i.sstatic.net/4Axab.jpg)](https://i.sstatic.net/4Axab.jpg)

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?

Edit:

Outside of the camera stuff, a rendering job writes to the image like this, which itself is a vector representing the flattened matrix of dimensions `[height][width]`:

```cpp
void rendering_job(const Scene &scene, std::vector<Vector3d> &img, size_t i, size_t j) {
    Vector3d temp_emission;

    size_t number_of_bounces = 0;
    for (size_t r = 0; r < scene.camera.rays_per_pixel; r++) {
        Ray ray = scene.camera.get_ray(i, j);

        temp_emission = temp_emission + integrator_sample(scene, ray, number_of_bounces);
    }

    // Printing every single index access shows that
    // every single pixel gets addressed (from 0 to 
    // scene.camera.height*scene.camera.width-1)
    img[i*scene.camera.height + j] = temp_emission / scene.camera.rays_per_pixel; 
}

```

It then gets passed to a module which writes the PPM image like this (the factor and MAX stuff is for doing tonemapping):

```cpp
void write(std::string nombFich) const {
    double factor = color_resolution / max;

    std::ofstream outfile(nombFich);
    outfile << std::fixed << std::setprecision(0);
    outfile << "P3" << std::endl;
    outfile << "# " << nombFich << std::endl;
    outfile << "#MAX=" << max << std::endl;
    outfile << width << " " << height << std::endl;
    outfile << color_resolution << std::endl;

    int i = 0;
    for (auto v : img) {
        outfile << v[0]*factor << " " << v[1]*factor << " " << v[2]*factor << " ";

        i++;
        if (i == width - 1) {i = 0; outfile << std::endl;}
    }
}

```

The writing process writes correctly `height` rows of `width` values in the PPM.

Edit2:

DAMN...It was so obvious I couldn't see the forest for the trees. I was indexing the image as `img[i*scene.camera.height + j]` instead of `img[i*scene.camera.width + j]`...
[![enter image description here](https://i.sstatic.net/V8qUm.jpg)](https://i.sstatic.net/V8qUm.jpg)

I assume that now I should tweak my camera in order to get rid of the stretching?

**Tags:** raytracing, 3d, pathtracing, camera

> **Comments:**
> **username_1**: The “repeating pattern” image is a sign that the way you're filling your pixel data buffer (with a linear sequence of pixel values) is not the right shape for the width and height of the image you want to produce. Specifically, it looks like you've produced only 480 pixels for each row, instead of 640 (as if the image was square), so that the next row starts too soon and wraps around. You haven't shown us that part of your code, so I can't tell you how to fix it, but it may be related to or separate from your camera/projection aspect ratio problem.  
> **username_0**: @username_1 You were right, it was a really dumb mistake at indexing the image matrix as an array. Now the result image is as above, I guess it's just refining the "camera setup" from now?  

---

## 0 answers

