- Home /
How to make a plane fill the field of view?
I'm wondering if it is possible to construct a plane that fills the camera's fov at an arbitrary distance from the camera?
For instance, I have a plane with a texture on it that I want to fill the screen, but the camera must be in perspective mode (for other reasons that I won't go into). And yes, it must be on a plane. I can't use a GUITexture because it is drawn last.
Thanks for any insights.
Answer by mouurusai · Sep 06, 2012 at 08:56 PM
using UnityEngine;
public class FillScreen:MonoBehaviour
{
void Update()
{
Camera cam = Camera.main;
float pos = (cam.nearClipPlane + 0.01f);
transform.position = cam.transform.position + cam.transform.forward * pos;
float h = Mathf.Tan(cam.fov*Mathf.Deg2Rad*0.5f)*pos*2f;
transform.localScale = new Vector3(h*cam.aspect,h,0f);
}
}
Its work for plane with size (1,1)
The third component of transform.localScale is 0. It results in a 0 in scale z and makes the plane invisible. How to solve this?
Simple - just hardcode the z-value to something you need.
I have a plane of size (1,1.5) & tried this but it doesn't work, am I missing something?
This will work for a quad but not for the unity plane. The unity plane is 10 times bigger and rotated 90 degrees around the x axis. The rotation should also be changed to face the camera:
using UnityEngine;
public class FillScreen:$$anonymous$$onoBehaviour
{
void Update()
{
Camera cam = Camera.main;
float pos = (cam.nearClipPlane + 0.01f);
transform.position = cam.transform.position + cam.transform.forward * pos;
transform.LookAt (cam.transform);
transform.Rotate (90.0f, 0.0f, 0.0f);
float h = ($$anonymous$$athf.Tan(cam.fov*$$anonymous$$athf.Deg2Rad*0.5f)*pos*2f) /10.0f;
transform.localScale = new Vector3(h*cam.aspect,1.0f, h);
}
}
Thank you!!! I have tried a lot of codes, but finally this works perfectly!
Answer by CHPedersen · Sep 06, 2012 at 02:25 PM
The correct way to do this is to use GUITextures. Nevermind that it's drawn last. :) I'll get to this in a bit:
First, make a GUITexture, and set the Texture to whatever you want to cover the whole screen. Now, set all Pixel Inset values to 0, as well as all borders. Now set scale to (1,1,0) and position to (0.5,0.5,0). This causes the texture to take up the entire screen.
As for the drawing last thing:
Define a new layer called "BGLayer" or whatever, and set the GUITexture to belong to this layer. Now, create a new camera, and set its culling mask to draw nothing BUT the layer that contains the background. Set this camera's depth property to a value which is less than all other cameras in your scene. -1, for example, and then all other cameras to 0 or above. All other cameras must have their Clear Flags set to Depth Only.
This causes the background to always get drawn first, because it's rendered by a camera that always renders first. All other cameras then only clear the depth buffer, then renders their content right on top of your background, as you wanted.
I think this is precisely what I was looking for. I'll try it right now...
I forgot to mention that the culling mask of all other cameras must of course EXCLUDE the layer you're sticking the background in. :) (Or remove their GUILayer component, so they can't render GUITextures)
$$anonymous$$eep in $$anonymous$$d that this works at runtime and in the game view, but not in the scene view, because you don't have access to modify the layer properties of the scene view camera.
I was using a plane because I thought I couldn't get a GUITexture to render the way I wanted it to... but I was wrong.
However, I still have a problem. Yes, I can get the GUITexture to render the way I want. But when the user presses a button, he is given an alternative view. The first view he is presented with is a "closeup" view (where the GUITexture gives the desired background). But the secondary view us a more "distant" view where the GUITexture should still provide a background, but only of a small section of the view (that is still proportional). With a plane, this would be easy because you just engage a 2nd camera and the plane still has it's same position... but I'm not sure what to do with the GUITexture approach.
I hope this makes some sense.
In other words... the GUITexture should not take up the entire screen in the 2nd view... only in the first "closeup" view.
He wants to have a plane fill the entire field of view. He does not want a skybox. A skybox has seams where the sides meet which require you to design textures that repeat neatly so the seams are invisible. This is unnecessary given the simple requirement that he just wants a texture to fill the entire screen.
Putting an actual geometrical plane in 3D and carefully scaling and positioning it so it fills up the far plane is an awkward solution because it forces polygons through the pipeline. There is no reason to multiply 3D vertices with an $$anonymous$$VP matrix, no matter how simple an object, if what you want is just an orthographic texture covering the entire screen.
Answer by lodendsg · Sep 06, 2012 at 01:29 PM
Check into this (http://docs.unity3d.com/Documentation/ScriptReference/GeometryUtility.CalculateFrustumPlanes.html) it calculates the planes of the camera's frustum being the limits of the cameras view.
Might also have a look at this, (http://docs.unity3d.com/Documentation/ScriptReference/Camera.ViewportToWorldPoint.html) which could be used to simply find the points at the edge of the screen given a distance from the camera.
Once you have the planes there you just need to pick the one you want being the "back" one.
Ok, so by using ViewportToWorldPoint, I can definitely get the bounds of the plane at some value for Z, but that doesn't help me scale the Plane object in my scene so that it fits the viewport perfectly... unless I'm missing something.
I'm not where I can code and test but but psudo
(assu$$anonymous$$g your plane is $$anonymous$$yPlane)
//Get a world space vector to the upper right corner of the screen
Vector3 UpRight = camera.ViewportToWorldPoint (new Vector3 (1,1, $$anonymous$$yPlane.transform.z));
//Get a would space vector to the lower left corner of the screen
Vector3 DownLeft = camera.ViewportToWorldPoint (new Vector3 (-1,-1, $$anonymous$$yPlane.transform.z));
//Set our width scale to be right - left
$$anonymous$$yPlane.scale.x = UpRight.x - DownLeft.x;
//Set our height scale to be up - down
$$anonymous$$yPlane.scale.y = UpRight.y - DownLeft.y;
Yes, so that scales the plane to be the correct aspect ratio as far as I can tell... but it doesn't fit it inside the viewport (it's much too large).
The bottom-left of the camera is (0,0) so if you change DownLeft to
Vector3 DownLeft = camera.ViewportToWorldPoint (new Vector3 (0,0, $$anonymous$$yPlane.transform.z)); it will work fine.
Answer by hawken · Sep 15, 2019 at 01:05 PM
I realise this topic is old as the hills, but you don't need to rotate the plane in Update, just do it once in Start. To make it always face the camera just parent it to the camera.
Answer by RakNet · Nov 11, 2019 at 02:45 PM
The earlier answers are close but not quite right. Here is the correct answer for a plane. using UnityEngine;
[ExecuteInEditMode]
public class PlaneFillScreen : MonoBehaviour
{
private void Update()
{
Camera cam = Camera.main;
float pos = (cam.nearClipPlane + 10.0f);
transform.position = cam.transform.position + cam.transform.forward * pos;
transform.LookAt(cam.transform);
float h = (Mathf.Tan(cam.fieldOfView * Mathf.Deg2Rad * 0.5f) * pos * 2f) * cam.aspect / 10.0f;
float w = h * Screen.height / Screen.width;
transform.localScale = new Vector3(h, w, 1);
}
}
Your answer
Follow this Question
Related Questions
Understanding Texture Atlasing 0 Answers
Error CS8025: Parsing error. HELP! 1 Answer
GUI using planes - pixel perfect on iPad 2, not on iPad 1 0 Answers
Double-sided cross section ("slice") 0 Answers