Stationary camera, moving scene

Previously, we talked about revolving the entire 3D scene about the camera, and also the problem of the camera looking directly downwards. Today, we’ll look at the mechanics of implementing that stationary camera (it ain’t pretty).

There are 2 transformations to take care of: translation and rotation. Translation takes care of the distance between the camera and the point it’s looking at. Rotation takes care of simulating the camera turning around to look at objects, roughly speaking. Let me use a 2D version to illustrate the concept.

Reverse translation and rotation of 2D scene

Suppose the camera is at some arbitrary position looking at an object. Based on the positions of the camera and the object, you can find the distance between them. You know, with this:
d = sqrt( (cx-ox)^2 + (cy-oy)^2 )
where cx and cy are the x-coordinate and y-coordinate of the camera respectively, and ox and oy are the x-coordinate and y-coordinate of the object respectively.

The camera is looking at the object, so the angle (theta) of its line of sight with respect to the (for example) x-axis can be calculated.

Suppose we want the stationary camera to look in the direction of the positive y-axis, and be positioned at the origin (0,0). To make the scene viewed through a stationary camera the same as that in the original version (the default by the 3D engine), we would rotate the entire scene (90 – theta) degrees, then translate the result of that d units along the positive y-axis.

Remember that order of transformations is important. Rotating first then translating, is (generally) different from translating then rotating.

So that’s the general idea of making a stationary camera work, by moving and rotating the entire scene. The fun part comes because it’s in 3D.

The distance calculation still holds true:
d = sqrt(x^2 + y^2 + z^2)

The angle… not so much. Because it’s in 3D, I adopted spherical coordinates. The radius would simply be the distance calculated previously. But there are now 2 angles to calculate, theta and phi.

Spherical coordinate angles

Suppose the camera is at (a,b,c) and the viewed object is at (p,q,r). We make the viewed object the centre of our attention, so we start our calculations with the object at the origin. Therefore, the camera is at (a-p, b-q, c-r).

We can calculate the distance between them as
d = sqrt( (a-p)^2 + (b-q)^2 + (c-r)^2 )

Then we also solve for the following set of simultaneous equations (note I’m using y-axis as the “upward” axis)
x = r * sin(theta) * sin(phi)
y = r * cos(phi)
z = r * cos(theta) * sin(phi)

==>

a-p = d * sin(theta) * sin(phi)
b-q = d * cos(phi)
c-r = d * cos(theta) * sin(phi)

to look for the angles theta and phi, where
0 <= theta <= 2*PI 0 <= phi < PI Once found, the rendering occurs by rotating the entire scene phi degrees about the positive z-axis (starting from negative y-axis as 0 degrees), then rotate about the positive y-axis (starting from the positive z-axis as 0 degrees), then translate by (-a,-b,-c) (this moves the entire scene away from the camera positioned at the origin). Well, that was a lot of trouble. What was I trying to solve again? Oh yeah, that looking down and losing the "up" vector problem. Notice anything wrong in this implementation? The "up" vector of the camera was never considered. But figuring out all the math was fun... if only it solved something too... *sigh* [Note: all use of "degrees" in this article can be substituted with "radians", depending on your situation. Use accordingly.]

Dissecting Trigonometric Particles part 2 – Axis functions

This is a continuation of the explanation behind Trigonometric Particles. Read up on part 1 if you haven’t done so.

Actually, there isn’t a clever coding construct I used to implement the axis functions. I just created a function that does a combination of polynomials and trigonometry functions. It looks something like this:

float function AxisFunction(float t, float p0, float p1, float p2, float p3, float p4, float p5, float p6)
{
	float result = 0.0;
	result += p0 + p1*t + p2*t*t + p3*t*t*t;
	result += p4*sin(t) + p5*cos(t) + p6*tan(t);

	return result;
}

The variable t is the time elapsed. The first part of the function is basically a cubic polynomial. The second part is a sum of the 3 standard trigonometric functions. Basically, I’m just passing in parameters which are the coefficients of the respective terms.

To simulate a sphere, since I can’t quite summon the mental energy to switch between my preferred Y-axis-pointing-skywards and the more widely known Z-axis-pointing-skywards coordinate system, I’ll just use the more famous version to illustrate. So the axes are:
x = r * sin(theta) * cos(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(theta)

To calculate the X, Y, Z coordinates, I just use different combinations of AxisFunction(). So
x = AxisFunction(t, 0,0,0,0, r,0,0) * AxisFunction(t/2, 0,0,0,0, 0,1,0)
y = AxisFunction(t, 0,0,0,0, r,0,0) * AxisFunction(t/2, 0,0,0,0, 1,0,0)
z = AxisFunction(t, 0,0,0,0, 0,r,0)

I’m passing t/2 for a different-valued phi. I can’t remember the exact multiple of t I used… so I’m just using t/2 as an example. For the sphere simulation, I was playing around with t to get the particles to swirl and end roughly near the top of the sphere. Took me a while to figure out the right magic number…

To simulate a cylinder, just set polar coordinates on X and Y, then use Z as the height.
x = AxisFunction(t, 0,0,0,0, 0,r,0)
y = AxisFunction(t, 0,0,0,0, r,0,0)
z = AxisFunction(t, 0,H,0,0, 0,0,0)
where H corresponds to the speed value you want the particles to “climb” the cylinder. Again, much time taken to figure out the right magic number…

As for the tornado simulation, notice that it’s similar to that of a cylinder, and the particles circle with a wider radius as they climb the cylinder. So the radius is now a function of the height.

Actually, we just need the radius to increase as t increases, not necessarily as a function of height. So we apply a linear function to r.
x = AF(t, 0,0,0,0, 0, r * AF(t, 0,L,0,0, 0,0,0), 0)
y = AF(t, 0,0,0,0, r * AF(t, 0,L,0,0, 0,0,0), 0,0)
z = AF(t, 0,H,0,0, 0,0,0)
where L is some magic number such that the radius increases in a reasonable manner proportionate to the time elapsed (and effectively proportionate to the height).

I have a confession to make. When I first introduced Trigonometric Particles, I said I used a W axis as well as composite functions. Well I truly remembered having a W axis, and I think I used it as such:
x = W * AF(…)
or maybe
x = AF( … W, …)

But I’m not using W in my explanations above. Hmm… somehow while writing the explanations, the W axis wasn’t required.

As for composite functions, a short description. H is a composite function when
H(x) = F(G(x))
meaning you calculate G(x) first, then calculate function F using G(x). That’s what we’re doing when we passed in one form of AxisFunction() to another AxisFunction() as a parameter.

Well, my memory being foggy, I seem to recall having both W axis and the use of composite functions. It appears one can simulate the results with one or the other. Oh well, no one’s perfect…

And the last simulation pattern, the sun’s surface? I think I used a pure polynomial. Quadratic, I think…