I have a sphere which is divided in segments, each segment is a quad. I want to find the points of the sphere which are visible to the screen so from there I can get the segments which are shown to the screen.
So I need to find 4 points for left (green), right (red), top and bottom.
I have some solutions where I check every single vertex if it is in camera frustrum but that seems CPU intensive, Im looking for some other solution, I thought of using the camera planes and projects rays from the normals but I couldn’t get a good result, maybe my math was wrong.
You haven’t explained your problem very well. Please try again.
What does visible on the screen means? If occlusion matters, a point on the other side of the sphere is not visible. You can’t naively check if a point is in frustum because it doesn’t take occlusion into account.
If occlusion matters you want to pierce the sphere with a ray going from the camera origin through some point X on the sphere. The point X is visible if the solution returns one or two points (it’s a quadratic function, so it can have 0, 1, or 2 solutions), and the nearest point of intersection is (approximately the same as) X.
You can learn how to do compute ray-sphere intersection here. It is fairly fast (if done properly), you can test a bunch of points every frame.
On start and before start the sphere faces the camera at its facing angle. 0,0,0
if that angle is set 0,180,0 Euler
then you are looking at the opposite side. If it’s 90* then it’s half of the left and half of the front face. And so on.
So…
By knowing the faces you can see, and knowing the Euler of the object at start, you can know the faces you are looking at by comparing the euler and visible faces at the meridian( the start ) and then knowing that 180* is the exact opposite you should be able to just know these things without ray. Though Ray is a quick solution and will work! And it was my original plan to comment but Osiris beat me to it
That kinda works when the conditions are ideal (i.e. crazy small FoV or great distance). Normal perspective FoV between 45-120 messes things up big time and such guesswork will never work correctly for certain proximity to the sphere. Maybe you can fiddle with it but rays don’t lie.
The simplest solution would be to just use math. Of course we would assume you know the radius and the position of the sphere. The analytical approach would be this:
We know the distance of the observer to the sphere center, lets call it D and we also know the radius of the sphere R. We can construct a right triangle where D is the hypotenuse and R is the adjacent. We can directly calculate the sine of that angle by calculating adjacent / hyporenuse. Or s = R / D
. The actual offset from the center towards the viewer is o = s*R
Since we want to know the point on the surface there are multiple approaches. You can use the “asin” on s
to get the angle and then calculate backwards the offsets, or just complete the circle by using R² = o² + x²
where x is the offset perpendicular to the view direction. Solve for x x = sqrt(R² - o²)
and just apply the numbers in a coordinate system that is aligned with your view direction (the VD vector from the viewer to the center which has length D).
If your viewer is always at the same height as the center, this is kinda trivial and reduces the problem to a 2d case. If the observer can be way above or below the sphere and looking at an angle, you have to make clear what you mean by left, right, top and bottom. The easiest solution is probably to use Quaternion.LookRotation with your D vector as forward vector and your camera’s up vector as up vector. This should give you the “straight on” rotation Q
. Now you can simply construct the required vectors
Vector3 right = Q * Vector3.right * x;
Vector3 up = Q * Vector3.up * x;
Vector3 fwd = -Q * Vector3.forward * o;
// or like this
// Vector3 fwd = -VD.normalized * o;
Vector3 rightP = sCenter + fwd + right;
Vector3 leftP = sCenter + fwd - right;
Vector3 topP = sCenter + fwd + up;
Vector3 bottomP = sCenter + fwd - up;
Just for completeness everything together:
Vector3 dir = sCenter - cam.transform.position;
Quaternion Q = Quaternion.LookRotation(dir, cam.transform.up);
float D = dir.magnitude;
float o = R*R/D;
float x = Mathf.Sqrt(R*R - o*o);
Vector3 right = Q * Vector3.right * x;
Vector3 up = Q * Vector3.up * x;
Vector3 fwd = -Q * Vector3.forward * o;
Vector3 rightP = sCenter + fwd + right;
Vector3 leftP = sCenter + fwd - right;
Vector3 topP = sCenter + fwd + up;
Vector3 bottomP = sCenter + fwd - up;
Note that your fov does not change how much you can see of a sphere. This is only controlled by the distance to the sphere. At the same distance you always see the same amount of surface. This is a pure function of perspective and your tangent lines to the sphere. A greater / smaller fov makes you see more / less at the edges of your view, but the tangents to the sphere do not change.
Note that those 4 points are points in worldspace, not in screenspace.

1 Like
Note that “o” is essentially the distance from the center towards the camera / viewer where you could place a cutting plane perpendicular to your center line (from viewer to sphere center). This cutting plane would cut off a sphere cap that is the visible part. Of course at a theoretical infinite distance you would see half of the sphere. The closer you get, the less is visible. At 0 distance from the surface (when D == R) you cant see anything as your camera is essentially half buried in the ground. So at this point x
would be 0 and o
would be R. Of course if the camera inside the sphere, you would get a square root of a negative number. So if you can get really close or partly inside the sphere, you may want to check the discriminant before calculating the square root.
1 Like
It is an interesting problem I prefer the mathematical solution, though if you were not dealing with a symmetrical object then the ray solution could work.
If you put a circle inside your sphere and billboard it to the camera you have what bunny described as your cutting plane. If this cutting plane was a child of the sphere same radius and centred on the sphere, the difference of the current circle angle compared to what the sphere angle was a start can give a clue to which portion of that symmetrical object is visible. Or in front of your cutting plane.
it’s easy to work with a small object at first a sphere made of a circle with say 10 vertice points on its edges, the ten points can be shifted with rotation in the Y axis, though there may be inaccuracies at the far edges, about when exactly an edge becomes considered visible or invisible it would still provide data of the certain verts that are visible.
Similar to bunnies images I draw a quick image to depict how the cutting plane can decipher its visibles
Another method is to use the actual camera frustrum culling distance. 
See the points you have in the editor window, to control the clipping plane. Find a way to grab those points in script, far clipping plane, and run a few checks on your verts. Or deduce. The cam itself always renders the verts if they are in cutting plane even if the objects center is not as seen below.
How many vertices does the sphere have?
Since if it’s not a crazy amount, I’d not throw out the bruteforce aproach immediately. “KISS” is a thing!
Just make sure to not read the mesh vertices every frame but cache them in a list. You can still add an offset if the sphere moves.
If you haven’t yet, run the profiler over your code. Maybe you find some culprit which is not the bounds checks themselves.
Oh and also cache the cam view frustrum! That’s a call to the cpp side of Unity if I recall right and thus slow when used massively.
If all else fails and you are familiar with it (what I’d recommend getting): Using jobs and the burst compiler allows to calculate hundred thousands of things per frame at decent framerate.