Need help with this mathematical problem between camera and orbitcontrols

Hi. this what I trying to solve:

My scene starts with:

*camera positions xyz set to: (0,10,30)
*Orbitcontrols target xyz set to: (0,10,0)

console.log(camera.position.distanceTo(controls.target) shows 30
So: the Distance between camera and target is 30

now the problem

this is what I want:

1.- *when distance between camera and target is >=20 orbit target postion.y and camera position.y will remains at 10
2.- *when distance between camera and target is ==10. target postion.y and camera position.y must be 15
3.- *when distance between camera and target is < 20. orbit target and camera will start to increse their position.y values until reach what I said in point two (15). in other words after < 20. target.y and camera position.y will increase in function of the decrease of distance.

I hope you understand me because I know my English is bad.

Any help will be appreciated

The math doesn’t seem that complicated to me. What I gather from your description is the following:

  • the camera is always looking horizontally (camera.y == target.y)
  • the target moves strictly along the y-axis (x= 0, z = 0)

Since we are all visually oriented (aren’t we?), I would wrap your description up as follows:


One thing strikes me though:
you don’t specify what you expect for distances below 10 [units] and above 30 [units] from the target. I sketched two possible outcomes above, visualized as dashed lines. Please specify.

So, what this boils down to, from my point of view is:

the distance “d” between camera and target can be computed as

d = Math.sqrt( camera.x * camera.x + camera.z * camera.z)

And you want to do a LERP of the target.y (and camera.y) , based on the camera’s distance from the y-axis, between the values 10 and 15.

I propose the following:

target.y = camera.y = 10 - ( d - 20 ) / (20 - 10 ) * (15 - 10)

Thanks for answer. I ask this question over 10 months and I still not full solved.
I did a workaround time ago on my tick() function trying to solved it; works but is not the desired result.

if (this.camera.position.distanceTo(this.controls.target)<=20)
{
  this.controls.target.y=15; //head bone position
  this.camera.position.y=15;// prevents vertical rotation
  this.controls.update();
    }
    else
    {
        this.controls.target.y=10; //torso bone
        this.controls.update();
    } 

If you see the video above. When I zooming in and th distance between camera & orbitControls target is below 20 it jumps aggressively instead of gradually being increased (as you showed in your graphic).

About what I expect for distances below 10 UNITS and above 30 UNITS. Nothing I put limits. So user can´t zoom out above 30 UNITS and can´t Zoom in below 10 UNITS.

I will test your code ASAP. Thanks for your help. I really appreciated :slight_smile:

Coming back to your problem: I’ve looked at the video and it matches your code snippet. It may not be what you wanted, but it is exactly what you coded. Here’s why:

The blue line shows your intended behaviour, the red line shows your implemented behaviour.

Actually you want a continuous transition from the lower-right state (d ≥ 20, y = 10) to the upper-left state (d = 10, y = 15). It’s crucial to understand, that the transition zone comprises both a distance interval and a height interval.

You implemented a mere threshold, an instant switch from y.start to y.end without spreading the height change over the allowed distance interval.

As you enter the transition zone, coming from the lower-right side and following along the blue arrow, you’ll want to increase the y-value at the same rate as you’re consuming the distance zone.

  0% distance covered =>   0% height added
100% distance covered => 100% height added

  n% distance covered =>   n% height added

You can’t do that using an if .. else construct. You need to compute the fraction of the distance zone consumed, then add that same fraction of the height zone to the initial height. The following should do the trick:

let dStart = 20;
let dEnd = 10;
let yStart = 10;
let yEnd = 15;
let deltaWidth = dStart - dEnd;
let deltaHeight = yEnd - yStart;
let d, y fract;
...
d = this.camera.position.distanceTo(this.controls.target)
fract = ( dStart - d ) / deltaWidth;

y = fract > 0 ? yStart + fract * deltaHeight : yStart;  // LERP only inside transition zone

this.controls.target.y = y; // head bone position
this.camera.position.y = y; // prevents vertical rotation
this.controls.update();

1 Like

Thank you very much. Your solution gave me exactly the result I was looking for. You are very good at math.:+1:

1 Like

Thanks for your praise, but I would like to deflect that a little bit:
I think I’m good at explaining things, while the math involved was actually pretty insignificant.

Please post a new video showing the result. :nerd_face:

In fact that is true too. You know how to explain very well.

What I like about your code is that it blends very well with the code I have and it doesn’t affect the times when I “stress” the camera.

Here is the video. And again. Thanks

1 Like