# How to change FoV in vertex shader?

Is it possible to change the Field of View vertex shader? I zoom my camera by changing the FoV value. However this will cause the skybox also be zoomed and this is not what I wanted. Is it possible to change the FoV value of the projection matrix in vertex shader?

Specifically, I pass the projection matrix by uniform to a glsl shader. I want to change the FoV value of the projection matrix (to make a new projection matrix) in the vertex shader of the skybox.

• Can you please be more specific? What framework/toolset. And do you have a reference picture for what you want. If you want the skybox to not be affected by a different fov then use the original fov when rendering the skybox – CobaltHex Dec 23 '16 at 7:10
• Why not just load a different projection matrix when drawing your sky? – Maximus Minimus Dec 23 '16 at 8:05
• @LeComteduMerde-fou for my project, it's much easier to change the vs shader than to change the loaded projection matrix. So I want to know if there is a way to do it in vs shader. – kkpattern Dec 23 '16 at 8:09
• well the vertex shader only knows what you tell it. – CobaltHex Dec 24 '16 at 22:03

Yes, it's possible

If we look at a typical vertex shader transform (source) we might see something like this:

vTrans = projection * modelview * incomingVertex;


So if we have access to our projection and our modelview matrices from the vertex shader, we can just use a different projection matrix to achieve the desired effect.

As we know, one way of constructing a projection matrix involves feeding it FOV, together with a few other params, for example, this from GLM (source):

// Generates a really hard-to-read matrix, but a normal, standard 4x4 matrix nonetheless
glm::mat4 projectionMatrix = glm::perspective(
FoV,         // The horizontal Field of View, in degrees : the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in)
4.0f / 3.0f, // Aspect Ratio. Depends on the size of your window. Notice that 4/3 == 800/600 == 1280/960, sounds familiar ?
0.1f,        // Near clipping plane. Keep as big as possible, or you'll get precision issues.
100.0f       // Far clipping plane. Keep as little as possible.
);


You can see the matrix this generates in the GLM source code here: https://github.com/g-truc/glm/blob/0.9.5/glm/gtc/matrix_transform.inl#L208

T const rad = fovy;
T const tanHalfFovy = tan(rad / static_cast<T>(2));

detail::tmat4x4<T, defaultp> Result(static_cast<T>(0));
Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
Result[2][2] = - (zFar + zNear) / (zFar - zNear);
Result[2][3] = - static_cast<T>(1);
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);


So if you just rewrite this code in GLSL, plugging in a different FOV value, you can construct your new projection matrix on-the-fly in your vertex shader.

It's actually even easier, because since all you're changing is elements [0][0] and [1][1] you can copy off the existing projection matrix and just change those elements.

But you probably don't want to do it

The first thing you should notice is that this will involve some extra calculations in your vertex shader, including some trigonometry. It's lightweight enough but it will impact performance.

If you're doing CPU-side frustum culling (and you really should be) then your newly constructed projection matrix will not be valid for your frustum, so you'll either end up drawing too much (more performance impact) or not enough (resulting in cleared or hall-of-mirrors regions at the sides of the window), depending on how you adjust FOV.

You will need to send the new FOV as a uniform to your shader.

That last one is crucial: since doing it requires sending a new uniform to your shader, you may as well just send a different projection matrix instead and avoid all of the other issues.