Take every point (vertex), project them into screen space (basically multiply them with the projection * view * model
matrix).
After you got this, take every point and get the 2 with the biggest x difference and repeat the same for y. This way you get the bounding box of these points.
Now, you'll also need 2 things: the aspect ratio of the camera, and the vertical FOV.
Take the bounding box, multiply it's height by the aspect ratio, and check if it is bigger or smaller than the width. If it's bigger, then we need to take the height into consideration when we position our camera, if it's smaller, we need to take the width into consideration.
Now we need the vertical FOV (and some trigonometry). If you need to place your camera based on the vertical, then you simply use the following formula (height is the bounding box height, FOV is the FOV):
dist := height / 2 / tan(FOV / 2)
If you need to base it off of the width, then you need to do a slight modification (width is the width of the bounding box, aspect is the aspect ratio):
dist := width / 2 / tan(FOV * aspect / 2)
(Every angle is in radian BTW)
Now, you now how far you need to place the camera.
Now, if you need the distance based on width, then let the 2 farthest points on the horizontal axis be A
and B
. If you need the height version, then let the farthest points on the vertical axis be A
and B
(Doesn't matter which one's which).
Now, A
and B
form a line (marked with AB
). Get this line's center ((A + B) / 2
), let this be M
. Now the last part. Take the direction vector (it should be normalized), negate it, multiply it with dist
you got earlier, add it to M
and done, you have the position of the camera.
Because many people don't understand math, and because I'm a nice guy, here's this in pseudo-code:
func find_pos(mat4 proj_view_model, float aspect, float FOV, vec3[] points, vec3 camera_direction) {
vec4[] projected_points
foreach (vec3 pos in points) do
push(projected_points, proj_view_model * vec4(pos, 1))
end
vec4 A1, B1, A2, B2
float width, height
for (i = 0 .. length(projected_points) - 1) do
for (j = i .. length(projected_points)) do
float new_width := abs(projected_points[i].x - projected_points[j].x)
float new_height := abs(projected_points[i].y - projected_points[j].y)
if (new_width > width) then
A1 := projected_points[i]
B1 := projected_points[j]
width := new_width
end
if (new_height > height) then
A2 := projected_points[i]
B2 := projected_points[j]
height := new_height
end
end
end
vec4 A, B
float dist
if (height * aspect > width) then
dist := height / 2 / tan(FOV / 2)
A := A1
B := B1
else
dist := widh / 2 / tan(FOV * aspect / 2)
A := A2
B := B2
end
vec3 center := vec3((A + B) / 2)
vec3 camera_pos = center - camera_direction * dist
return camera_pos
}
This function is very fast if you're wondering, the biggest drawback is getting the 2 farthest points. If you know any other algorithms, feel free to use that.
If you find any errors in my logic, post a comment below.