Detect if target is behind the camera - outdated

Hi, this post is messy and unclear, please check this one instead:

And if an admin comes by, please feel free to delete this one.

.
.
.
.
.

Hello,

I need a bit of help with the maths.
I have a fonction for onscreen coordinates:

function getScreenPosition( target ) {
	
	target.updateMatrixWorld();
	camera.updateProjectionMatrix();
	
	var vector = target.matrixWorld.multiplyVector3( new THREE.Vector3() )
	projScreenMat = new THREE.Matrix4();
	projScreenMat.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
	vector.applyMatrix4( projScreenMat );
	
	var lookAtVector = new THREE.Vector3( 0, 0, 1 );
	lookAtVector.applyQuaternion( camera.quaternion );
	
	var angle = lookAtVector.angleTo( vector );
}

The vector has the right on screen position ( even when off screen) while the target is in front of the camera.

I need the angle so that I can know it the target is behind the camera.
Problem is that the value rises un to Math.PI / 2, and if you turn more ( so when the target is behind the camera ) the angle starts decreasing to 0 instead of rising up to Math.PI.

Can anybody help?
Thanks.

Hello horsetopus,

You could check the camera frustum intersection:

var frustum = new THREE.Frustum();
var cameraViewProjectionMatrix = new THREE.Matrix4();

// every time the camera or objects change position (or every frame)

camera.updateMatrixWorld(); // make sure the camera matrix is updated
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
cameraViewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
frustum.setFromMatrix( cameraViewProjectionMatrix );

// frustum is now ready to check all the objects you need

console.log( frustum.intersectsObject( object ) );

(code from http://stackoverflow.com/questions/17624021/determine-if-a-mesh-is-visible-on-the-viewport-according-to-current-camera)

Hi, INF1N1T.

That’s nice, I didn’t knew of this method, so thank you, but it doesn’t fit my needs.

I don’t know if you play FPS games, but you know how some arrow are used to show you objectives?
If your objective is within the screen a symbol is displayed on top.
If it is outside, an arrow on the side of the screen shows you the direction in which to turn the camera to see the objective on screen.

So I need also targets out of screen. And my method works great for everything that is in front of the camera.
I don’t know how to say it more clearly…
imagine you divide the word with a pane perpendicular to the camera ( 0, 0, -1) vector. For everything in front of this pane, it works great. That’s for what’s behind that it fails.

Is it clear? I might have to do a fiddle… but it would take quite some time…

It works because the object is in the camera frustum, which can be represented by an invisible plane perpendicular to the camera. As soon as the object is outside the area made by this plane the math behind stops working, because the calculated target point is not reachable by the equation and the math does not apply anymore. I don’t believe there is an equation that could calculate the offset unless you increase the size of the invisible plane.

You have to calculate the object offset “manually” when the object is off the camera (top,bottom,left & right of the screen) or clip the object position to the screen bounderies with something like this:

public static Vector3 ScreenPointEdgeClamp( Vector2 screenPos, float edgeBuffer, out float angleDeg )
    {
        // Take the direction of the screen point from the screen center to push it out to the edge of the screen
        // Use the shortest distance from projecting it along the height and width
        Vector2 screenCenter = new Vector2( Screen.width / 2.0f, Screen.height / 2.0f );
        Vector2 screenDir = ( screenPos - screenCenter ).normalized;
        float angleRad = Mathf.Atan2( screenDir.x, screenDir.y );
        float distHeight = Mathf.Abs( ( screenCenter.y - edgeBuffer ) / Mathf.Cos( angleRad ) );
        float distWidth = Mathf.Abs( ( screenCenter.x - edgeBuffer ) / Mathf.Cos( angleRad + ( Mathf.PI * 0.5f ) ) );
        float dist = Mathf.Min( distHeight, distWidth );
        angleDeg = angleRad * Mathf.Rad2Deg;
        return screenCenter + ( screenDir * dist );
    }

(code from https://forum.unity3d.com/threads/camera-worldtoscreenpoint-bug.85311/#post-2844282)

Let us know how it works out…

Ok, so as I thought I really explain myself very badly.

Although I thank you for the method ( better than what I was doing, witch was doing some intersections with the edges of the rectangle ), my issue is not with off-screen coordinates.
It’s just about detecting when the object is behind the camera.

I had to do a jsfiddle to get things clear:
http://jsfiddle.net/Horsetopus/67q690fg/

You’ll see that I have no problem until the cube goes behind the camera.
And even then I know what to do: just reverse the vector, as the guy does in a tutorial video in the post you shared:

But the detection of when the cube is behind is my issue. In the video I just linked, the guy uses the z value of the projected vector, which is negative when the position is behind the camera.

In my fiddle, as you can see, the z value is kinda nonsense.
I say kinda because because there seams to be a logic (when behind it is superior to 1)
And in my project, it’s yet another behavior (goes between 0 and Pi/2).
So I think the z is just some kind of leftover on computations? I don’t know…
But I can’t use it as it is.

So what am I missing? Is this a bug? something I do wrong? is there another method for this detection?

I hope I am clear this time…

I can only see a yellow square that moves with mouse coordinates, where is the “getScreenPosition” function in question? Where are you checking the “behind the camera scenario” causing the issue? Plus, why are you adding complexity using composer?

Sorry, some unsaved drafts on the fiddle… I have updated, please check again.

And I use composers to have two render passes: one for the perspective view, one for the orthogonal one (with the icons and the rest of the UI).

Re horsetopus,

I am little confused at this point! I now see the 2 scenes, the cube rotating around the cam and the issue you are talking about in your previous post, but I don’t see your initial request with the “getScreenPosition” function.

If you want someone to help you with the code in the JSfiddle, plz create a new post.

For the sake of a summury, if you wish to create offscreen target pointers for your 3D objects, you will need to:

  1. Check if the object is on the screen (with the camera frustum intersection code above)

  2. if the object is NOT on the screen use something like ScreenPointEdgeClamp function to derive the object position based on the center of the screen and not the camera frustum

1 Like

My god have I been messy with this thread.
I swear I am usually clearer, but I have work over my head.
Sorry and thank you for your patience.

So, first of all, about what you see.
What maters is what is on the bottom, you see the perspective scene, and the overlaping ui.
On top is the same scene but shown from another camera, so that you can get a better understanding of what is happeing.
The red cube is looping around the camera, and the white pane helps seeing when the cube is in front or behind the camera.

Now you should see clearly that the arrow at the bottom clearly follows the cube, and reflects when it is in or out of the frustum.
But it’s messed up when the cube is behind the camera, wich measns behind the white pane.
And I know how to correct, but I don’t know how to detect if the cube is in front or behind the camera.

In the video I shared before, the projection on screen on Unity also retuns a z valus positive when the object is in front and negative when it is behind.
But the behaviour is different with THREE.js. The value I get is nonsense.

As for the code, I used for projection, I just replaced this:
var vector = target.matrixWorld.multiplyVector3( new THREE.Vector3() )
projScreenMat = new THREE.Matrix4();
projScreenMat.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
vector.applyMatrix4( projScreenMat );

By this, which does eactly the same thing:
var vector = new THREE.Vector3();
vector = vector.setFromMatrixPosition( cube.matrixWorld );
vector.project( testCamera );

So how do I detect if the cube is in front or behind the camera? Which means in front or behind the white pane?
If not from the projection z value, from where?

Thanks.

Re horsetopus,

What you are asking is really a hack and I would not recommend to do it this way for the purpose of “offscreen target pointers”, cause it is doing extra compute not necessary, specially for a game. For everyone sake, close this post and create a new one with your last question and I will give you an answer to determine the position of an object based on imaginary plane.

Done,
You’ll find it here, and I hope I have been clear this time

I believe this post is still valid and the answers are cleared for others to know the proper way to create [quote=“horsetopus, post:3, topic:207”]
game objective arrows
[/quote]

which was horsetopus main goal.

We try to leave all of the topics open so that other users can prosper from problems or mistakes made by previous users! Thanks for contributing to this cause!