<p>The math for the projection matrix is (with <code>fov</code> as $\alpha$):</p>

<p>$q \leftarrow \frac{1}{tan(\frac{\alpha}{2})}$</p>

<p>$a \leftarrow \frac{q}{aspect}$</p>

<p>$b \leftarrow \frac{(far + near)}{(near - far)}$</p>

<p>$c \leftarrow \frac{(2 * far * near)}{(near - far)}$</p>

<p>Notice that there're some things you're doing that are differently, such as the order of your subtractions between <code>near</code> and <code>far</code>, how you organize the matrix values, and your multiplication between your <code>r * t</code>.</p>

<p>Using the variables above, the column-major matrix below would be the resulting perspective projection matrix:</p>

<p>\begin{bmatrix}
  a &amp; 0 &amp;  0 &amp; 0 \\
  0 &amp; q &amp;  0 &amp; 0 \\
  0 &amp; 0 &amp;  b &amp; c \\
  0 &amp; 0 &amp; -1 &amp; 0
\end{bmatrix}</p>

<p>From the above, we get:</p>

<pre><code>def perspective_projection_matrix(fov, aspect, near, far):
    q = 1 / tan(radians(fov * 0.5))
    a = q / aspect
    b = (far + near) / (near - far)
    c = (2*near*far) / (near - far)

    # construct column-major matrix here...
</code></pre>

<p><strong>NOTE:</strong> I left the last part out because I'm not familiar enough with <code>numpy</code> to know whether it expects row-major or column-major order.</p>

<p>Also, you should validate all your arguments (e.g. both <code>near &gt; 0</code> and <code>far &gt; 0</code>, <code>far &gt; near</code>, etc.) if you want to avoid future headaches.</p>

CC BY-SA 3.0
2017-02-05T12:39:50.210Z
That's one of the problems you have. If you try to debug code without knowing what the library you use do, then you will not be able to fix much. Use libraries you either understand or write your own code for this.
user18490
CC BY-SA 3.0
2017-02-05T12:41:09.183Z
@user18490 I wrote my own code... I just had to translate my code from Java into Python here and I don't use `numpy` specifically.
code_dredd
CC BY-SA 3.0
2017-02-05T12:44:44.427Z
Please check the reference I provided you. Do you understand the difference between column and row-major order matrices? Also how can you tell the matrix you give above is column-major?
user18490
CC BY-SA 3.0
2017-02-05T12:52:29.397Z
@user18490 I'm going over the reference (it's 5am here, though), which is trying to explain how a view-matrix works, but that should be kept in the *other* question... don't mix stuff up cross-questions. Also, I understand the difference between row and column-major matrices... not sure what you're trying to get at?
code_dredd
CC BY-SA 3.0
2017-02-05T12:59:18.693Z
I don't know what you mean by mixing stuff. You have code that doesn't work. You say this matrix you provide (in form of code) is column-major. I tell you 'no' it's not. The reference I pointed you to, explains how a perspective matrix is actually built, which is what you are trying to do. The fact that it's 5am your place is not relevant to the question: tare the time to digest the theory (before posting anything else). Then it will help you fix your code.
user18490
CC BY-SA 3.0
2017-02-05T16:05:30.137Z
Alright thank you @ray ! It works perfectly. I have no idea why the lecture had column-major matrices. I ended up using your matrix.
divx
CC BY-SA 3.0
2017-02-06T11:02:24.810Z
@kourbou In mathematical texts I've come across, the "standard" convention has always been to use column-major orderings. What's really important (I think) is to be aware of which convention the systems you're using have adopted, be aware of the differences in expected results, and to be consistent (i.e. don't mix up row/column-major orderings).
code_dredd
CC BY-SA 3.0
2017-02-05T12:26:53.187Z
2017-02-08T00:16:12.833Z
2017-02-08T00:16:12.833Z
code_dredd
<p>Please read the <a href="https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix" rel="nofollow noreferrer">references</a> that are given to you:</p>

<p>$$S = \dfrac{1}{\tan(\dfrac{fov}{2}*\dfrac{\pi}{180})}$$</p>

<p>$$
\left[\begin{array}{cccc}
S &amp;&amp; 0 &amp;&amp; 0 &amp;&amp; 0 \\
0 &amp;&amp; S &amp;&amp; 0 &amp;&amp; 0 \\
0 &amp;&amp; 0 &amp;&amp; -\dfrac{f}{(f-n)} &amp;&amp; -1\\
0 &amp;&amp; 0 &amp;&amp; -\dfrac{f*n}{(f-n)} &amp;&amp; 0\\
\end{array}\right]
$$</p>

<p>In this reference they use row-major order matrices so your assumption that your matrix is column-major is wrong (to start with). Then you will need to check the code you use to multiply this matrix with points. All source code is provided on the reference, so you just need to <strong>read</strong> and reproduce.</p>

CC BY-SA 3.0
2017-02-05T16:08:07.080Z
Yes, I'm sorry. I'm not actually in a Computer Graphics class, just trying to teach myself. May I ask why this matrix (from the reference) uses -f/(f-n) and -f*n/(f-n)? Do you end up with the same results with the matrix @ray gave?
divx
CC BY-SA 3.0
2017-02-05T17:25:03.033Z
@Kourbou: doesn't the matrix look the same to you? I you learn computer graphics it is important you learn how to read equations (as well as code)
user18490
CC BY-SA 3.0
2017-02-05T12:51:12.860Z
2017-02-05T12:51:12.860Z
user18490
<p>So I've been messing with perspective projection matrices recently. I used numpy and GTK/Cairo to make a very small Python renderer. I'm very confused with the results I'm getting though.</p>

<p>I took this Homogeneous Coordinates technique from an online lecture. If I understood correctly, the objective is to transform every point inside a "Viewing Pyramid" that's frustum shaped so they fit in a cube. (Image from of songho.ca)</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://i.sstatic.net/AoWoV.png" rel="noreferrer"><img src="https://i.sstatic.net/AoWoV.png" alt="Image courtesy of songho.ca"></a></p>

<p>You need a Field of View angle ($\alpha$), the Near and Far plane distances ($n$ and $f$ respectively), and the aspect ratio ($r$). Firstly you turn every 3D Point into a Homogeneous Point by adding  a 1 like so:</p>

<p>\begin{align*}
\begin{pmatrix}
  x &amp; y &amp; z
\end{pmatrix}
\xrightarrow{\text{4D}}
\begin{bmatrix}
  x &amp; y &amp; z &amp; 1
\end{bmatrix}
\end{align*}</p>

<p><br/></p>

<p>Then you multiply your point matrix by a perspective projection matrix:</p>

<p>\begin{align*}
\begin{bmatrix}x &amp; y &amp;z &amp; 1 \end{bmatrix}
\begin{bmatrix}
  1\over\tan(\alpha/2) &amp; 0 &amp; 0 &amp; 0\\ 
  0 &amp; r\over\tan(\alpha/2) &amp; 0 &amp; 0\\
  0 &amp; 0 &amp; (f+n)\over(f-n) &amp; -1 \\
  0 &amp; 0 &amp; (2nf)\over(f-n) &amp; 0
\end{bmatrix}
=
\begin{bmatrix}
x' &amp; y' &amp; z' &amp; w
\end{bmatrix}
\end{align*}</p>

<p><br/></p>

<p>And to go back to a 3D point in space you divide by the fourth dimension:</p>

<p>\begin{align*}
\begin{bmatrix}
  x' &amp; y' &amp; z' &amp; w
\end{bmatrix}
\xrightarrow{\text{3D}}
\begin{pmatrix}
  x' \over w &amp; y' \over w &amp; z' \over w
\end{pmatrix}
\end{align*}</p>

<p><br/><br/>
This is exactly what I've done with numpy:</p>

<pre><code>def projection_matrix(fov, aspect, near, far):
    t = 1/math.tan(math.radians(fov)/2)
    a = (far + near)/(far - near)
    b = (2*near*far)/(far-near)
    r = aspect

    return numpy.matrix([[t,   0,   0,   0],
                         [0, r*t,   0,   0],
                         [0,   0,   a,  -1],
                         [0,   0,   b,   0]])
</code></pre>

<p>But for some reason the renderer is totally messed up. This is supposed to be a spinning cube... What am I missing here?</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://i.sstatic.net/YSERI.gif" rel="noreferrer"><img src="https://i.sstatic.net/YSERI.gif" alt="Messed up cube"></a></p>

CC BY-SA 3.0
2017-02-04T20:46:23.770Z
The full project is at https://github.com/kourbou/pythree-gtk. The lecture I used was [here](https://www.youtube.com/watch?v=mpTl003EXCY).
divx
CC BY-SA 3.0
2017-02-05T11:01:28.473Z
Please refer to https://www.scratchapixel.com/lessons/3d-basic-rendering/get-started and https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix. There is ample information on the perspective projection process out there (and helas debugging people's program is not what a forum is about). Eventually that's teacher's jobs)))
user18490
<rendering><projections><camera-matrix>
My perspective projection is messed up?
divx
# My perspective projection is messed up?

(Asked by: username_0 on 2017-02-04)

So I've been messing with perspective projection matrices recently. I used numpy and GTK/Cairo to make a very small Python renderer. I'm very confused with the results I'm getting though.

I took this Homogeneous Coordinates technique from an online lecture. If I understood correctly, the objective is to transform every point inside a "Viewing Pyramid" that's frustum shaped so they fit in a cube. (Image from of songho.ca)

[![Image courtesy of songho.ca](https://i.sstatic.net/AoWoV.png)](https://i.sstatic.net/AoWoV.png)

You need a Field of View angle ($\alpha$), the Near and Far plane distances ($n$ and $f$ respectively), and the aspect ratio ($r$). Firstly you turn every 3D Point into a Homogeneous Point by adding a 1 like so:

\begin{align\*}
\begin{pmatrix}
x & y & z
\end{pmatrix}
\xrightarrow{\text{4D}}
\begin{bmatrix}
x & y & z & 1
\end{bmatrix}
\end{align\*}

Then you multiply your point matrix by a perspective projection matrix:

\begin{align\*}
\begin{bmatrix}x & y &z & 1 \end{bmatrix}
\begin{bmatrix}
1\over\tan(\alpha/2) & 0 & 0 & 0\\
0 & r\over\tan(\alpha/2) & 0 & 0\\
0 & 0 & (f+n)\over(f-n) & -1 \\
0 & 0 & (2nf)\over(f-n) & 0
\end{bmatrix}
=
\begin{bmatrix}
x' & y' & z' & w
\end{bmatrix}
\end{align\*}

And to go back to a 3D point in space you divide by the fourth dimension:

\begin{align\*}
\begin{bmatrix}
x' & y' & z' & w
\end{bmatrix}
\xrightarrow{\text{3D}}
\begin{pmatrix}
x' \over w & y' \over w & z' \over w
\end{pmatrix}
\end{align\*}

This is exactly what I've done with numpy:

```
def projection_matrix(fov, aspect, near, far):
    t = 1/math.tan(math.radians(fov)/2)
    a = (far + near)/(far - near)
    b = (2*near*far)/(far-near)
    r = aspect

    return numpy.matrix([[t,   0,   0,   0],
                         [0, r*t,   0,   0],
                         [0,   0,   a,  -1],
                         [0,   0,   b,   0]])

```

But for some reason the renderer is totally messed up. This is supposed to be a spinning cube... What am I missing here?

[![Messed up cube](https://i.sstatic.net/YSERI.gif)](https://i.sstatic.net/YSERI.gif)

**Tags:** rendering, projections, camera-matrix

> **Comments:**
> **username_0**: The full project is at https://github.com/kourbou/pythree-gtk. The lecture I used was [here](https://www.youtube.com/watch?v=mpTl003EXCY).  
> **username_1**: Please refer to https://www.scratchapixel.com/lessons/3d-basic-rendering/get-started and https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix. There is ample information on the perspective projection process out there (and helas debugging people's program is not what a forum is about). Eventually that's teacher's jobs)))  

---

## 2 answers

### Answer by username_2 on 2017-02-05. Score: 2 | Accepted answer

The math for the projection matrix is (with `fov` as $\alpha$):

$q \leftarrow \frac{1}{tan(\frac{\alpha}{2})}$

$a \leftarrow \frac{q}{aspect}$

$b \leftarrow \frac{(far + near)}{(near - far)}$

$c \leftarrow \frac{(2 \* far \* near)}{(near - far)}$

Notice that there're some things you're doing that are differently, such as the order of your subtractions between `near` and `far`, how you organize the matrix values, and your multiplication between your `r * t`.

Using the variables above, the column-major matrix below would be the resulting perspective projection matrix:

\begin{bmatrix}
a & 0 & 0 & 0 \\
0 & q & 0 & 0 \\
0 & 0 & b & c \\
0 & 0 & -1 & 0
\end{bmatrix}

From the above, we get:

```
def perspective_projection_matrix(fov, aspect, near, far):
    q = 1 / tan(radians(fov * 0.5))
    a = q / aspect
    b = (far + near) / (near - far)
    c = (2*near*far) / (near - far)

    # construct column-major matrix here...

```

**NOTE:** I left the last part out because I'm not familiar enough with `numpy` to know whether it expects row-major or column-major order.

Also, you should validate all your arguments (e.g. both `near > 0` and `far > 0`, `far > near`, etc.) if you want to avoid future headaches.

> **Comments:**
> **username_1**: That's one of the problems you have. If you try to debug code without knowing what the library you use do, then you will not be able to fix much. Use libraries you either understand or write your own code for this.  
> **username_2**: @username_1 I wrote my own code... I just had to translate my code from Java into Python here and I don't use `numpy` specifically.  
> **username_1**: Please check the reference I provided you. Do you understand the difference between column and row-major order matrices? Also how can you tell the matrix you give above is column-major?  
> **username_2**: @username_1 I'm going over the reference (it's 5am here, though), which is trying to explain how a view-matrix works, but that should be kept in the *other* question... don't mix stuff up cross-questions. Also, I understand the difference between row and column-major matrices... not sure what you're trying to get at?  
> **username_1**: I don't know what you mean by mixing stuff. You have code that doesn't work. You say this matrix you provide (in form of code) is column-major. I tell you 'no' it's not. The reference I pointed you to, explains how a perspective matrix is actually built, which is what you are trying to do. The fact that it's 5am your place is not relevant to the question: tare the time to digest the theory (before posting anything else). Then it will help you fix your code.  
> **username_0**: Alright thank you @ray ! It works perfectly. I have no idea why the lecture had column-major matrices. I ended up using your matrix.  
> **username_2**: @kourbou In mathematical texts I've come across, the "standard" convention has always been to use column-major orderings. What's really important (I think) is to be aware of which convention the systems you're using have adopted, be aware of the differences in expected results, and to be consistent (i.e. don't mix up row/column-major orderings).  

---

### Answer by username_1 on 2017-02-05. Score: 2

Please read the [references](https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix) that are given to you:

$$S = \dfrac{1}{\tan(\dfrac{fov}{2}\*\dfrac{\pi}{180})}$$

$$
\left[\begin{array}{cccc}
S && 0 && 0 && 0 \\
0 && S && 0 && 0 \\
0 && 0 && -\dfrac{f}{(f-n)} && -1\\
0 && 0 && -\dfrac{f\*n}{(f-n)} && 0\\
\end{array}\right]
$$

In this reference they use row-major order matrices so your assumption that your matrix is column-major is wrong (to start with). Then you will need to check the code you use to multiply this matrix with points. All source code is provided on the reference, so you just need to **read** and reproduce.

> **Comments:**
> **username_0**: Yes, I'm sorry. I'm not actually in a Computer Graphics class, just trying to teach myself. May I ask why this matrix (from the reference) uses -f/(f-n) and -f*n/(f-n)? Do you end up with the same results with the matrix @ray gave?  
> **username_1**: @Kourbou: doesn't the matrix look the same to you? I you learn computer graphics it is important you learn how to read equations (as well as code)  

---

