Cached at:
06/22/26, 11:33 AM
# Computing Camera Rays
Source: [https://momentsingraphics.de/CameraRays.html](https://momentsingraphics.de/CameraRays.html)
Published 2026\-06\-20
We are in a transition period, where ray tracing and rasterization coexist\. Typical real\-time rendering pipelines still use rasterization \(often with deferred shading\) for primary visibility and then trace rays for shadows, reflections or global illumination\. Though, we are getting to the point where one can seriously consider to ditch rasterization and to use ray tracing for primary visibility as well, at least on some platforms\. Then you need to compute camera rays, characterized by ray origin, ray direction and ray length, in a way that is consistent with what you would otherwise do for rasterization\. In rasterization, you commonly use a world to clip space transformation matrix, also known as view\-projection matrix to specify the camera\. In this blog post, I will derive how to compute camera rays based on such a matrix\.
The goal is to get something that works equally well for perspective and orthographic projection and whatever else such a matrix may represent\. The obvious approach turns out to be prone to numerical cancellation and I present an alternative that works much more reliably\. Overall, this is not a hard problem: For any given camera model \(e\.g\. perspective projection with known field of view\), it is quite easy to come up with an ad hoc solution that will work\. Though, I find it valuable to have a solution based on readily available transformation matrices that does not require any further tinkering per camera model\. If you do not care about the derivation, feel free to just copy the shader code in[Listing 4](https://momentsingraphics.de/CameraRays.html#get_camera_ray)\(which I hereby release into the public domain, like all code in this blog post\)\.
## Camera rays in clip space
The rasterization pipeline and clip space rely heavily on homogeneous coordinates, so let us begin by reviewing that\. If we have a point with 3D Cartesian coordinates \\\(\(x^\\prime, y^\\prime, z^\\prime\)^\\mathsf\{T\}\\\), we can get homogeneous coordinates for that point by simply attaching a 1 as fourth coordinate: \\\(\(x^\\prime,y^\\prime,z^\\prime,1\)^\\mathsf\{T\}\\\)\. This still describes a 3D point, but we have now gained the freedom to scale its coordinates by any non\-zero factor \\\(w\\neq 0\\\), which gives us
\\\[\(x,y,z,w\)^\\mathsf\{T\} = \(wx^\\prime, wy^\\prime, wz^\\prime, w\)^\\mathsf\{T\}\\text\{\.\}\\\]No matter how we choose \\\(w\\\), these coordinates still describe the same point\. We can recover the inhomogeneous coordinates \(i\.e\. dehomogenize\) by dividing by the fourth component \\\(w\\\):
\\\[\\frac\{1\}w\(x,y,z,w\)^\\mathsf\{T\} = \\left\(\\frac\{x\}\{w\}, \\frac\{y\}\{w\}, \\frac\{z\}\{w\}, 1\\right\)^\\mathsf\{T\} = \(x^\\prime,y^\\prime,z^\\prime,1\)^\\mathsf\{T\}\\text\{\.\}\\\]Homogeneous coordinates make many formulas simpler\. For example, we will see below that we can also write down homogeneous coordinates for planes in 3D space, and then to check whether a point is on a plane, we just take a dot product\. They also allow us to express translation with \\\(4\\times 4\\\) matrices\. Furthermore, they are useful for rasterization with a perspective projection\. A perspective projection inherently requires us to perform a division at some point\. With homogeneous coordinates, this division happens shortly before rasterization and it is simply the dehomogenization mentioned above\.
Along with this design of rasterizers comes the notion of clip space\. For screen space, we use a coordinate frame where coordinates \\\(x^\\prime\_c\\\) and \\\(y^\\prime\_c\\\) range from \-1 to 1 across the extent of the camera frustum \(the subscript \\\(c\\\) stands for clip space\)\. In homogeneous coordinates, these bounds translate to \\\(\-w\_c\\leq x\_c\\leq w\_c\\\) and \\\(\-w\_c\\leq y\_c\\leq w\_c\\\)\. In addition, we define a near and far clipping plane based on the clip space z\-coordinate\. The far clipping plane is at \\\(z^\\prime\_c=1\\\), which translates to \\\(z\_c\\leq w\_c\\\)\. The near clipping plane is defined differently, depending on the API: For[Direct3D](https://learn.microsoft.com/en-us/windows/uwp/graphics-concepts/viewports-and-clipping?utm_source=chatgpt.com#set-up-the-viewport-for-clipping), the inequality is \\\(0\\leq z\_c\\\)\. For OpenGL, the default behavior is that the near clipping plane is at \\\(\-w\_c\\leq z\_c\\\), but this has been made configurable through the extension[GL\_ARB\_clip\_control](https://registry.khronos.org/OpenGL/extensions/ARB/ARB_clip_control.txt), which lets you choose the Direct3D behavior of \\\(0\\leq z\_c\\\)\. This extension has moved into core functionality in OpenGl 4\.5\. For[Vulkan](https://docs.vulkan.org/spec/latest/chapters/vertexpostproc.html#vertexpostproc-clipping), the behavior is similarly configurable\. To account for these differences, I will use a variable \\\(z^\\prime\_n\\\) which is \\\(0\\\) for the Direct3D conventions and \\\(\-1\\\) for the old OpenGL default, i\.e\. either way the near clipping plane is \\\(z^\\prime\_n w\_c\\leq z\_c\\\)\.
A typical renderer will use lots of other coordinate frames, such as camera space and object space for every single object, but the only other one that we care about here is world space, because we want to get the ray in world space\. In the context of ray tracing, it is pretty easy to define what we mean by world space: It is whatever the[top\-level acceleration structure](https://docs.vulkan.org/spec/latest/chapters/accelstructures.html)uses\. For rasterization, we then prepare the world to clip space matrix \\\(M\_\{w,c\}\\in\\mathbb\{R\}^\{4\\times4\}\\\) that transforms points from world space to clip space\. If \\\(p\_w=\(x\_w, y\_w, z\_w, w\_w\)^\\mathsf\{T\}\\\) denotes the world\-space coordinates of a point, the corresponding clip\-space coordinates are
\\\[p\_c := \(x\_c, y\_c, z\_c, w\_c\)^\\mathsf\{T\} := M\_\{w,c\} p\_w\\text\{\.\}\\\]I am using OpenGL conventions here, which are consistent with common linear algebra conventions\. In Direct3D conventions, the matrices and vectors would be transposed and the order of multiplication would be reverted \(not sure who thought that was a good idea or why\)\. More on that[below](https://momentsingraphics.de/CameraRays.html#Direct3D_HLSL_conventions)\.
Now if we have screen\-space coordinates \\\(x^\\prime\_c, y^\\prime\_c\\\) ranging from \\\(\-1\\\) to \\\(1\\\), we can easily determine points on the near and far clipping planes for that pixel in terms of their homogeneous coordinates in clip space\. They are
\\\[n\_c := \(x^\\prime\_c, y^\\prime\_c, z^\\prime\_n, 1\)^\\mathsf\{T\} \\quad\\text\{and\}\\quad f\_c := \(x^\\prime\_c, y^\\prime\_c, 1, 1\)^\\mathsf\{T\}\\text\{,\}\\\]respectively\. Our goal is to turn that into a ray origin and ray direction in world space\. Sounds simple enough, right?
## World space ray origin
As far as the ray origin is concerned, it really is simple\. We take the point on the near clipping plane, transform it to world space and dehomogenize\. To do so, we need the clip to world space transformation matrix \\\(M\_\{c,w\}:=M^\{\-1\}\_\{w,c\}\\\)\. Then the point we want is simply \\\(n\_w := M\_\{c,w\} n\_c\\\)\. Since ray tracing APIs do not deal with homogeneous coordinates, we then want to dehomogenize that, i\.e\. divide by the w\-coordinate\.[Listing 1](https://momentsingraphics.de/CameraRays.html#get_camera_ray_origin)provides an implementation in GLSL\.
**Listing 1:**Returns a point on the near clipping plane for a camera with the given clip to world space transform\. When`clip\_space\_xy`is \\\(\(\-1, \-1\)\\\) the point is at the left top of the viewport, for \\\(\(1, \-1\)\\\) it is at the right top and for \\\(\(1, 1\)\\\) it is at the right bottom \(at least with default OpenGL, Vulkan and Direct3D behavior\)\.
```
vec3 get_camera_ray_origin(
vec2 clip_space_xy, mat4 clip_to_world)
{
// With old OpenGL conventions, use z_n=-1
float z_n = 0.0;
vec4 n_c = vec4(clip_space_xy, z_n, 1.0);
vec4 n_w = clip_to_world * n_c;
return n_w.xyz / n_w.w;
}
```
## Bad approach for the ray direction
To describe the camera ray, we need the ray direction in addition to the ray origin\. The obvious strategy is as follows: We use the code snippet above with`z\_n=1`to compute a point on the far plane\. Then we take the difference of the points on the far and near plane and normalize this vector\. From a mathematical point of view, there is nothing wrong with this approach\. It gives the correct result and it is also quite efficient to compute\. Nonetheless, I strongly advise against using it\. In fact, I will not even provide a code listing for it, lest somebody copies it by accident\.
Instead, let us just look at the results of this approach in[Figure 1](https://momentsingraphics.de/CameraRays.html#bad_ray_direction)\. I like a good graphics glitch as much as anyone, but still this is not quite the result we were going for\. A slow, continuous camera motion turns into a jittery mess\. What is going on?
**Figure 1:**A video with a camera that moves slowly along a straight line\. It is quite far from the origin and looks at a scene centered around the origin \(namely the[Lumberyard Bistro](https://developer.nvidia.com/orca/amazon-lumberyard-bistro)\)\. Due to rounding errors in the computation of ray direction vectors, the camera motion appears jittery\.
Looking at the last two columns of the clip to world space matrix \\\(M\_\{c,w\}\\\), we see something like this:
```
-5995.15332031 5994.86083984
11787.78613281 -11786.94140625
-4032.25268555 4031.86547852
-50.01052094 50.01062393
```
Note how these two columns are almost identical, except for their sign\. When we compute points on the far plane, we compute \\\(M\_\{c,w\}f\_c\\\) and the last two entries of \\\(f\_c\\\) are 1\. Thus, as we compute this matrix\-vector product, we are summing the last two columns of the clip to world space matrix\. Since they are nearly equal, but with opposite sign, this addition causes a catastrophic cancellation: These large floats have large absolute rounding errors and their tiny difference will have a similarly large absolute rounding error\. Then the point on the far plane has reduced precision\. The exact way in which this rounding error works out depends on the exact entries, which in turn depend on the camera position\. Hence the unsmooth result in[Figure 1](https://momentsingraphics.de/CameraRays.html#bad_ray_direction)\. Computing the clip to world space matrix in double precision arithmetic \(before casting it to float when it is passed to the shader\) diminishes the magnitude of these artifacts, but does not eliminate them\. There may be other issues at work here, but this cancellation seems to be the main culprit\.
## Good approach for the ray direction
Let us try something different then\. Instead of characterizing the line for the camera ray by specifying two points \\\(n\_c, f\_c\\\) on it, we will define two planes such that the line is the intersection of these two planes\. We characterize these two planes using homogeneous coordinates
\\\[G\_c:=\(1, 0, 0, \-x^\\prime\_c\)^\\mathsf\{T\} \\quad\\text\{and\}\\quad H\_c:=\(0, 1, 0, \-y^\\prime\_c\)\\text\{\.\}\\\]By definition, a point in clip space \\\(p\_c\\in\\mathbb\{R\}^4\\\) is on the plane \\\(G\_c\\\) if and only if \\\(G\_c^\\mathsf\{T\} p\_c = 0\\\)\. In this equation, we are multiplying a row vector \(due to the transpose\) by a column vector, which gives us a dot product\. That expands to
\\\[G\_c^\\mathsf\{T\} p\_c = x\_c \- x^\\prime\_c w\_c = 0\\text\{,\}\\\]which is equivalent to
\\\[\\frac\{x\_c\}\{w\_c\} = x^\\prime\_c\\text\{\.\}\\\]Thus, this plane encompasses all points that have the inhomogeneous clip\-space x\-coordinate \\\(x^\\prime\_c\\\)\. In particular, it contains all points on our camera ray\. In a similar fashion, we can derive that
\\\[H\_c^\\mathsf\{T\} p\_c = 0 \\quad\\Leftrightarrow\\quad \\frac\{y\_c\}\{w\_c\}=y^\\prime\_c \\text\{\.\}\\\]So all points \\\(p\_c\\\) on the camera ray are in the intersection of the two planes, i\.e\. they satisfy
\\\[G\_c^\\mathsf\{T\}p\_c = H\_c^\\mathsf\{T\}p\_c = 0\\text\{\.\}\\\]The next step in the derivation is to transform these planes into world space\. Note that
\\\[G\_c^\\mathsf\{T\}p\_c = 0 \\quad\\Leftrightarrow\\quad G\_c^\\mathsf\{T\}M\_\{w,c\}p\_w = 0 \\quad\\Leftrightarrow\\quad \(M\_\{w,c\}^\\mathsf\{T\} G\_c\)^\\mathsf\{T\}p\_w = 0\\text\{\.\}\\\]Thus, \\\(G\_w := M\_\{w,c\}^\\mathsf\{T\} G\_c\\\) holds the world\-space coordinates of the plane \\\(G\_c\\\)\. Note how we are using the world to clip space transform to transform from clip space to world space\. The reason for that is that you need to use the inverse transpose to transform planes\. What we just saw is the derivation of this rule\.
We transform the other plane in the same way: \\\(H\_w := M\_\{w,c\}^\\mathsf\{T\} H\_c\\\)\. Now we seek a direction vector \\\(d\\in\\mathbb\{R\}^3\\\) for the intersection line of these two planes\. To get that, we note that the first three homogeneous coordinates of the planes \\\(G\_w,H\_w\\\) are their unnormalized world\-space normal vectors\. We denote these normals by \\\(u,v\\in\\mathbb\{R\}^3\\\)\. Then the direction vector must be orthogonal to both of these normal vectors\. Thus, we simply take the cross product: \\\(d := u\\times v\\\)\. That gives us a practical algorithm to compute the ray direction, which does not suffer from the numerical issues of the previous approach\.[Listing 2](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction_cross)implements it in GLSL\.
**Listing 2:**Returns a normalized ray direction for a camera ray of a camera with the given world to clip space transform\. Some computation in this function can be moved into precomputation of constants, see below\. The meaning of`clip\_space\_xy`is as in[Listing 1](https://momentsingraphics.de/CameraRays.html#get_camera_ray_origin)\.
```
vec3 get_camera_ray_direction_cross(
vec2 clip_space_xy, mat4 world_to_clip)
{
vec3 mx = transpose(world_to_clip)[0].xyz;
vec3 my = transpose(world_to_clip)[1].xyz;
vec3 mw = transpose(world_to_clip)[3].xyz;
float x = clip_space_xy.x;
float y = clip_space_xy.y;
vec3 u = fma(vec3(-x), mw, mx);
vec3 v = fma(vec3(-y), mw, my);
vec3 d = cross(u, v);
return normalize(d);
}
```
There is one caveat with this derivation: We have not said anything about the sign of \\\(d\\\), so for all we know, our ray could be pointing backwards\. At least, flipping the sign on`world\_to\_clip`does not change the sign of`d`, but other operations such as swapping the rows for`mx`and`my`do\. The bottom line is that the code above will give you the expected sign as long as the camera does not produce a mirrored image\. If you want a result that is correct no matter what, keep reading and we will circle back to this issue\.
## Faster solution for the ray direction
There is still marginal potential for optimization in[Listing 2](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction_cross)\. Computing`d`takes \\\(3\+3\+6=12\\\)[fused multiply\-add \(FMA\) instructions](https://momentsingraphics.de/FMA.html)\. We can reduce that to 6 FMAs by moving part of the computation into the computation of constants that are passed to the shader\. If we get ten teraflops of throughput out of our GPU \(which is not that much by modern standards\), we get this saving once per pixel at 4k resolution and we are actually limited by compute throughput, we are saving
\\\[\\frac\{3840\\cdot2160\\cdot6\}\{10\\,\\mathrm\{THz\}\} = 0\.005\\,\\mathrm\{ms\}\\text\{\.\}\\\]Saving 5 microseconds per frame is really negligible, so it is perfectly fine to stick to[Listing 2](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction_cross)\. But maybe you need to run this operation much more frequently for some reason and a wise man once said[finish your derivations, please](https://fgiesen.wordpress.com/2010/10/21/finish-your-derivations-please/)\. So here we go\.
To derive this, let \\\(m\_x\\in\\mathbb\{R\}^\{3\}\\\) denote the first three entries of the first row of \\\(M\_\{w,c\}\\\)\. Similarly, let \\\(m\_y,m\_w\\in\\mathbb\{R\}^\{3\}\\\) be the first three entries of the second and fourth row of \\\(M\_\{w,c\}\\\), respectively\.[Listing 2](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction_cross)uses the same convention\. Then
\\\[u=m\_x\-x^\\prime\_cm\_w\\quad\\text\{and\}\\quad v=m\_y\-y^\\prime\_cm\_w\\text\{\.\}\\\]Thus,
\\\[ \\begin\{align\*\} d = u\\times v &= \(m\_x\-x^\\prime\_c m\_w\)\\times\(m\_y\-y^\\prime\_c m\_w\) \\\\ &= m\_x\\times m\_y \- m\_x\\times\(y^\\prime\_c m\_w\) \- \(x^\\prime\_c m\_w\)\\times m\_y\\\\ &= x^\\prime\_c \(m\_y\\times m\_w\) \+ y^\\prime\_c \(m\_w\\times m\_x\) \+ m\_x\\times m\_y\\text\{\.\} \\end\{align\*\} \\\]So we only have to precompute three cross products, and write them to a constant buffer\. The code to do that depends a lot on what math library you are using, so you will have to write it yourself\. Then we can use[Listing 3](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction)to compute the ray direction\. We could have used a`mat3`as input to this function and used matrix\-vector multplication, but with separate vectors it was easier to document what the input means and with the use of FMA I am more confident that the computation of`d`actually compiles to six FMAs\.
**Listing 3:**Returns a normalized ray direction for a camera ray\.`my\_mw`,`mw\_mx`and`mx\_my`are precomputed cross products for the first three entries of row vectors of the world to clip space transformation matrix\. The meaning of`clip\_space\_xy`is as in[Listing 1](https://momentsingraphics.de/CameraRays.html#get_camera_ray_origin)\.
```
vec3 get_camera_ray_direction(
vec2 clip_space_xy,
vec3 my_mw, vec3 mw_mx, vec3 mx_my)
{
float x = clip_space_xy.x;
float y = clip_space_xy.y;
vec3 d = fma(vec3(x), my_mw, mx_my);
d = fma(vec3(y), mw_mx, d);
return normalize(d);
}
```
## The ray parameter at the far plane
Our description of the camera ray is not really complete yet\. Unless we do not care about the far clipping plane, we still have to compute the ray parameter \\\(t\_\\max\\in\\mathbb\{R\}\\\) so that the ray ends at the far plane\. The homogeneous coordinates of the far plane in clip space are \\\(F\_c=\(0,0,\-1,1\)^\\mathsf\{T\}\\\)\. Its world space cordinates are \\\(F\_w := M\_\{w,c\}^\\mathsf\{T\} F\_c\\\)\. Computing the intersection between a ray and a plane in homogeneous coordinates is simple enough\. If we let \\\(d\_w:=\\begin\{pmatrix\}d\\\\0\\end\{pmatrix\}\\\) denote homogeneous coordinates for the ray direction, we have to solve for a \\\(t\_\\max\\\) where the corresponding point is on the far plane:
\\\[\\begin\{align\} & F\_w^\\mathsf\{T\} \(n\_w \+ t\_\\max d\_w\) = 0 \\\\ \\Leftrightarrow~ & t\_\\max F\_w^\\mathsf\{T\} d\_w = \-F\_w^\\mathsf\{T\} n\_w \\\\ \\Leftrightarrow~ & t\_\\max = \-\\frac\{F\_w^\\mathsf\{T\} n\_w\}\{F\_w^\\mathsf\{T\} d\_w\}\\text\{\.\} \\end\{align\}\\\]We can take a shortcut in evaluating the numerator if we exploit how \\\(n\_w\\\) is defined:
\\\[ F\_w^\\mathsf\{T\} n\_w = F\_c^\\mathsf\{T\} M\_\{w,c\} n\_w = F\_c^\\mathsf\{T\} n\_c = 1\-z^\\prime\_n\\text\{\.\} \\\]So this dot product is either 1 or 2, depending on the convention for the near clipping plane\. In practice, we work with a dehomogenized version of n\_w, so there is another factor here, but we know its value\. I am sure one could also come up with a clever shortcut for the denominator, but I have not found one right away and do not want to put too much thought into avoiding one three\-component dot product\.
With all of that in mind, we arrive at[Listing 4](https://momentsingraphics.de/CameraRays.html#get_camera_ray)which combines everything into one function to facilitate a few optimizations\. For the ray direction, this implementation uses the method from[Listing 2](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction_cross), but to merge it with[Listing 3](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction), you only have to swap out the lines that compute`d`\. If you want to make another tiny optimization while you are at it, precompute`mw \- mz`\. As promised above, this also takes care of potential issues with the sign of the ray direction\. Overall, this may look like quite a lot of code, but most lines do not actually compute anything\. Honestly, many of them are just there to fit the code listing into my blog layout\.
**Listing 4:**Produces a camera ray`ray\_origin \+ t \* ray\_dir`that starts at the near clipping plane for`t=0`and ends at the far clipping plane for`t=t\_max`\.`ray\_dir`is normalized and`t\_max`is positive\. The meaning of`clip\_space\_xy`is as in[Listing 1](https://momentsingraphics.de/CameraRays.html#get_camera_ray_origin)\.
```
void get_camera_ray(
out vec3 ray_origin, out vec3 ray_dir,
out float t_max, vec2 clip_space_xy,
mat4 world_to_clip, mat4 clip_to_world)
{
// With old OpenGL conventions, use z_n=-1
float z_n = 0.0;
// Compute the ray origin
vec4 n_c = vec4(clip_space_xy, z_n, 1.0);
vec4 n_w = clip_to_world * n_c;
float n_fac = 1.0 / n_w.w;
ray_origin = n_w.xyz * n_fac;
// Compute the unnormalized ray direction
vec3 mx = transpose(world_to_clip)[0].xyz;
vec3 my = transpose(world_to_clip)[1].xyz;
vec3 mz = transpose(world_to_clip)[2].xyz;
vec3 mw = transpose(world_to_clip)[3].xyz;
float x = clip_space_xy.x;
float y = clip_space_xy.y;
vec3 u = fma(vec3(-x), mw, mx);
vec3 v = fma(vec3(-y), mw, my);
vec3 d = cross(u, v);
// Compute the normalization factor for d
float d_fac = inversesqrt(dot(d, d));
// Compute the maximal ray parameter
float num = 1.0 - z_n;
float den = dot(mw - mz, d);
t_max = -n_fac * num / (d_fac * den);
// Handle negative signs
d_fac = (t_max > 0.0) ? d_fac : -d_fac;
t_max = abs(t_max);
ray_dir = d * d_fac;
}
```
## Direct3D/HLSL conventions
I will not provide HLSL code here but porting the GLSL code above should be simple\. Replace all`vec3`by`float3`,`mat4`by`float4x4`, etc\. Change`inversesqrt`to`rsqrt`and`fma`to`mad`\. I think you will also have to change`clip\_to\_world \* n\_c`to`mul\(n\_c, clip\_to\_world\)`\. Other than that, there should be no need to change anything about how matrices are being used or indexed\. The reasons for that are a bit complicated: When indexing matrices in GLSL the first index pertains to columns, the second to rows\. For HLSL it is the other way around, which is consistent with linear algebra conventions\. At the same time, the Direct3D convention is to work with row vectors so that all matrices need to be transposed compared to OpenGL/Vulkan/math conventions\. In the code above, these two discrepancies cancel out and hence no change should be needed\. However, if you compute inputs for[Listing 3](https://momentsingraphics.de/CameraRays.html#get_camera_ray_direction), you have to work with column vectors of the world to clip space transformation matrix \(not rows\)\. All clear? No? Oh well, I tried\.
## Integration into a renderer
If you want more complete source code, you can take a look at a[branch of my educational path tracer](https://github.com/MomentsInGraphics/path_tracer/tree/camera_rays)\. In there,[camera\_utilities\.glsl](https://github.com/MomentsInGraphics/path_tracer/blob/camera_rays/src/shaders/camera_utilities.glsl)implements several different techniques to compute camera rays, including all the ones described in this blog post\. These are being used in[pathtrace\.frag\.glsl](https://github.com/MomentsInGraphics/path_tracer/blob/1c36a50066cba0b43ebacd690c8eace17a6e8a4e/src/shaders/pathtrace.frag.glsl#L377)\. Constants for the optimized method for computation of ray directions are computed in[main\.c](https://github.com/MomentsInGraphics/path_tracer/blob/1c36a50066cba0b43ebacd690c8eace17a6e8a4e/src/main.c#L663)\.
## Conclusion
As I said at the start, computing camera rays for a perspective camera is not particularly hard\. There are many legit solutions that do not require as much thought, e\.g\. what I did for my[path tracing Shadertoy](https://www.shadertoy.com/view/7sGBDK)\. Though, when I was writing my newest renderer, I already had world to clip space matrices at hand and wanted to use those\. When the bad approach for ray directions failed me, I was somewhat baffled and descended into this rabit hole for a day or two\. What I found seems useful enough that it is worth sharing\. I hope this will be helpful to others as they work on real\-time ray tracing\. It might also help with standardization\. I frequently see people struggling to make sense of how others implemented their cameras, e\.g\. in the context of Gaussian splatting\. If everybody does it differently, it can be hard to get consistent results out of two different renderers\. Clip space is standardized quite well \(with only a few API\-specific caveats\), so these transformation matrices could be nice as a sortof exchange format for cameras\. Then again, computer vision has its own conventions, so maybe it is better to stick to those in these contexts\.