I have an issue with the directional transform math for a voxel raytracer I'm working on in Python. X axis is left-right, Y axis is up-down, Z axis is forward-backward: I calculate view direction from a camera angle stored in degrees, direction being the virtual arrow the point of view points toward: Z rotation translates to X and Z directions, Y rotation to Y direction, X rotation is ignored as I didn't implement camera rolling. The conversion is done by running the rotation radians through a sine / cosine, for example: Rotation x = 0, y = 90, z = 45
becomes direction x = 0.7071067811865475, y = 1.0, z = 0.7071067811865475
since a 45* angle for rot Z means dir X and Z equally point toward that direction. A simple demo to visualize what I'm doing.
angle_y = 90
for angle_z in (0, 45, 90, 135, 180, 225, 270, 315, 360):
dir_x = math.sin(math.radians(angle_z))
dir_y = math.sin(math.radians(angle_y))
dir_z = math.cos(math.radians(angle_z))
print("x: " + str(dir_x) + " y: " + str(dir_y) + " z: " + str(dir_z))
Directions X and Z work as intended and form a perfect arch at every rotation as angle_z
goes from 0* to 360*. The issue is the Y direction: If you're looking straight ahead and it has a value of 0 everything works fine. But as you look up and down, the Y direction needs to correctly shrink the magnitude of X and Z with its intensity, otherwise extra "force" is added to the total direction which should never exceed X + Z as defined by their sin + cos. Therefore the following is wrong:
return dir_x, dir_y, dir_z
I tried the simplest solution which is a lot more accurate:
return dir_x * (1 - abs(dir_y)), dir_y, dir_z * (1 - abs(dir_y))
But this too causes incorrect perspective: As I look up or down it looks like the world shrinks vertically. I get an even more correct result by dividing dir_y
by two:
return dir_x * (1 - abs(dir_y) / 2), dir_y, dir_z * (1 - abs(dir_y) / 2)
This stops the world from bending at first, but as you look closer to a full -90 / +90 angle up or down it makes everything bend like a concave mirror. What is the correct way to shrink X and Z with by the intensity of Y to get a direction arrow that correctly maintains magnitude?
As proposed in comment, use spherical polar coordinates with your angles.
But as you look up and down, the Y direction needs to correctly shrink the magnitude of X and Z with its intensity
In your words you "shrink" your X and Z values by cos(angle_y)
dir_x = math.sin(math.radians(angle_z)) * math.cos(math.radians(angle_y))
dir_y = math.sin(math.radians(angle_y))
dir_z = math.cos(math.radians(angle_z)) * math.cos(math.radians(angle_y))
Using you can check it's always a unit vector