Not very neat math, but I hope it works for you:
Input
Suppose you are given a
- worldViewPerspectiveMatrix
M
(Transforms world space to perspective space, orthogonal matrices should work as well!) - a constant velocity (more specifcally a distance per Frame) in screen space
v_screen
- a (normalized) direction vector
V_dir
in world space, this will give the direction your object moves. The actual length of the vector will be calculated. - a position
X
where your Object is placed in world space.
Result
- a scaling factor
a
for moving your object 'v_screen' units in screen space alongV_dir
in world space. Thus you want to move your object actuallyX += a*V_dir
.
First we use the formula of mapping the world space to screen space by using homogenous coordinates and w-clipping. The .
denotes the matrix multiplication operator.
X_proj := M.(x,y,z,1);
X_screen := (x_proj/w_proj,y_proj/w_proj);
From there we can specify the position of the object X
in screen space and the position after appling the directional movement X+(a*V_dir)
.
The length in screenspace and therefore the desired distance (per frame) is simply
Length[(X+(a*dir))_screen - X_screen] == v_screen.
Let's solve this for a
. I used my Mathematica to calculate a
for you. If you want further details, I may elabourate the answer.
Suppose M
, X
, and V_dir
is
/ xp \ / vx \ / vxp \
X_proj = | yp | V_dir = | vy | V_proj = M.V_dir = | vyp |
| zp | | vz | | vzp |
\ wp / \ 0 / \ vwp /
And v_screen
is your desired screen space velocity.
Then a
is:
vlen = v_screen*v_screen;
a = (-dwp*vlen*wp + dxp*xp + dyp*yp +
0.5*Sqrt(
Power(-2*dwp*vlen*wp + 2*dxp*xp + 2*dyp*yp,2) -
4*(dxp*dxp + dyp*dyp - dwp*dwp*vlen)*(xp*xp + yp*yp - wp*wp*vlen)
)
) /
(dxp*dxp + dyp*dyp - dwp*dwp*vlen);
Remember, -a
is also a solution.