4
\$\begingroup\$

I'm playing around with OpenGL for a few weeks now.

For the following screenshot I picked the glm::ortho values for my lightsource by trial and error. There are two directional light sources with shadows.

My scene.

I would like to calculate the values for glm::ortho by creating a bounding box around the camera frustum.

I have the corners of the camera frustum in world space ... what is the next step? I think I should move the camera frustum into light space, calculate the bound box and put the dimensions of that bounding box into glm::ortho. But ... how? :)

The block below is a simplified version of my code with only one light source.

// Camera
mat4 cameraViewMatrix = lookAt(
    vec3(1.2f, 1.2f, 1.2f),
    vec3(0.0f, 0.0f, 0.0f),
    vec3(0.0f, 0.0f, 1.0f)
);

mat4 cameraProjectionMatrix = perspective(45.0f, 800.0f / 600.0f, 0.5f, 10.0f);

// Light
mat4 lightViewMatrix = lookAt(
    vec3(3.0f, -2.0f, 2.0f),
    vec3(0.0f, 0.0f, 0.0f),
    vec3(0.0f, 0.0f, 1.0f)
);

// Camera Frustum
vector<vec4> cubeNDC;
cubeNDC.push_back(vec4(-1.0f, -1.0f, -1.0f, 1.0f));
cubeNDC.push_back(vec4(1.0f, -1.0f, -1.0f, 1.0f));
cubeNDC.push_back(vec4(1.0f, -1.0f, 1.0f, 1.0f));
cubeNDC.push_back(vec4(-1.0f, -1.0f, 1.0f, 1.0f));
cubeNDC.push_back(vec4(-1.0f, 1.0f, -1.0f, 1.0f));
cubeNDC.push_back(vec4(1.0f, 1.0f, -1.0f, 1.0f));
cubeNDC.push_back(vec4(1.0f, 1.0f, 1.0f, 1.0f));
cubeNDC.push_back(vec4(-1.0f, 1.0f, 1.0f, 1.0f));

mat4 viewProjectionMatrixInverse = inverse(matProj * matView);

vector <vec4> cameraFrustum;
for(vec4 vertex: cubeNDC) {
    vec4 vertexTransformed = viewProjectionMatrixInverse * vertex;
    vertexTransformed /= vertexTransformed.w;
    cameraFrustum.push_back(vertexTransformed);
}

// Magic

mat4 lightProjectionMatrix = ortho(...);

Thanx for your help. :)

\$\endgroup\$
  • \$\begingroup\$ Did I get you right? You want to calculate your ortho matrix from the light-camera's bounding box? \$\endgroup\$ – Tara Aug 24 '14 at 15:33
  • \$\begingroup\$ @Dudeson Yes. I want to calculate the parameters for the glm::ortho(...) function using the cameras bounding box. So that the shadow map fits the visible area. \$\endgroup\$ – MAZ Aug 24 '14 at 19:13
  • \$\begingroup\$ To be honest, I'd do exactly the opposite. I see no point at all in doing it the way you try to do it. I'd simply set up a light camera and calculate bounding box from it, instead of setting up a bounding box and trying to compute the matrix from it. But maybe you really need to do it your way? \$\endgroup\$ – Tara Aug 24 '14 at 20:02
  • \$\begingroup\$ @Dudeson Uhh, I think I missread your first question/comment. Sorry. I have the "camera" (this is what the user is looking at), and a directional "light". When rendering the shadow map from the "lights" point of view, I want the resulting shadow map to fit the area viewable from the "camera". \$\endgroup\$ – MAZ Aug 25 '14 at 8:04
  • \$\begingroup\$ Aaahhh! Now I think I understand! You want your light-camera to thightly encompass your viewing volume, right? I'll write you an answer about that tomorrow then. \$\endgroup\$ – Tara Aug 25 '14 at 8:07
2
\$\begingroup\$

Okay. Seems like you just want a single light-camera.
But there are many different approaches. Like using multiple frustum splits (which means multiple light-cameras), which is called "Cascaded Shadow Mapping". Even the way you construct the frustum of your light-camera to encompass the main camera's frustum can be done in various ways.

First some useful links:

Building the matrix:

How I'd go about the very simple case you have:
You have an orthographic projection for your light-camera. So your frustum is just an Oriented Bounding Box (OBB). That means you can simply feed its world space coordinates (like the width and height) to glm::ortho().

But how do you construct the OBB around the frustum?
Good question. ;D

Here's a simple approach:
First determine the direction of your light (as a normalized vector). Now simply project every vertex of your main-camera's frustum onto that vector and find the nearest and furthest one (simply store the distances along the vector). Now subtract the two distances.
Congratulations! You already have your OBB's depth (Z).
Now repeat that process for the other two vectors. One pointing upwards or downwards (Y) and the other to the right or left (X) relative to your light-camera. Now you have your OBB's orientation (the three vectors) and dimensions. Now simply pass the OBB's dimensions to glm::ortho() and then transform the orthographic matrix so it has the same orientation as your OBB.
You're done. :D

Projecting a point onto a vector:
This step is actually very easy. Just take the dot product between your vector and your point (both stored as vec3).
Example code:

float distance_on_vector = dot(p, vector);

Vector should be normalized, because you need the world-space distance. You don't need the actual position of p in world space (you just need the projected length) to calculate the dimensions of the OBB. That's why the above code is enough.

Some free optimization tips:

  • If you do it with the above approach, you already get your three basis vectors. That means glm::ortho() actually becomes quite useless. All you have to do is scale the basis vectors and translate them. Done is your light-camera's projection matrix.

  • GLM is not really fast... But don't get me wrong. It's a very good library.

(damn... this answer took me quite some time...)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer. I didn't have any time to try this out. To be honest ... your simple approach still sounds like magic to me. :) But I do appreciate the effort you put in this answer. Can you please explain in more detail how to project a vertex of the main-camera's frustum onto a vector? (I'm not sure what is done in that step.) \$\endgroup\$ – MAZ Sep 2 '14 at 7:51
  • 1
    \$\begingroup\$ I have updated the answer to include an explanation about vector projection. \$\endgroup\$ – Tara Sep 5 '14 at 15:58
  • \$\begingroup\$ @Dudeson I find your answer helpful but I have one minor problem: When constructing the matrix, how do you find the position/The translation values? \$\endgroup\$ – akaltar Jan 23 '15 at 21:22
  • 1
    \$\begingroup\$ @akaltar: The position/translation should be equal to the center of the +Z face of the OBB. You'll need the OBB's model matrix to calculate that position (the three vectors X, Y and Z of the light camera I mentioned). Just use those three vectors to get from the OBB's center to the center of the +Z face. I hope you understand what I mean. \$\endgroup\$ – Tara Jan 27 '15 at 0:23
  • 1
    \$\begingroup\$ @Dudeson: Totally awesome! I almost had it with using the OBB center... But something was always cropped. I only had to use the +Z face as the center. So thanks, it works. \$\endgroup\$ – akaltar Jan 27 '15 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.