Advertisement

3D Camera Rotation - Euler Angles - Raytracing

Started by August 24, 2020 05:25 PM
6 comments, last by ellenature 4 years, 3 months ago

Hello,

I'm programming a C raytracer. I'm encountering some difficulties to realize the camera rotation.

I'm using this function :

void        move_cam(t_specs *s, int key)
{
t_camera    *cam;
t_vec3      orientation[3];

cam = s->current_cam;
orientation[0] = cam->right;
orientation[1] = cam->up;
orientation[2] = cam->forward;

if (key == KEY_W)
    cam->coord = vec_add(cam->coord, cam->up);
else if (key == KEY_S)
    cam->coord = vec_sub(cam->coord, cam->up);
else if (key == KEY_A)
    cam->coord = vec_sub(cam->coord, cam->right);
else if (key == KEY_D)
    cam->coord = vec_add(cam->coord, cam->right);
else if (key == KEY_Q)
    cam->coord = vec_sub(cam->coord, cam->vec);
else if (key == KEY_E)
    cam->coord = vec_add(cam->coord, cam->vec);
else if (key == KEY_H)
    cam->yaw += 0.1;
else if (key == KEY_F)
    cam->yaw -= 0.1;
else if (key == KEY_T)
    cam->pitch -= 0.1;
else if (key == KEY_G)
    cam->pitch += 0.1;
else if (key == KEY_R)
    cam->roll += 0.1;
else if (key == KEY_Y)
    cam->roll -= 0.1;

make_rotation_z(orientation, cam->roll);
make_rotation_x(rotation, cam->pitch);
mult_matrix(orientation, rotation, orientation);
make_rotation_y(rotation, cam->yaw);
mult_matrix(orientation, rotation, orientation);

s->current_cam->right = orientation[0];
s->current_cam->up = orientation[1];
s->current_cam->forward = orientation[2];
normalize(&s->current_cam->right);
normalize(&s->current_cam->up);
normalize(&s->current_cam->forward);
}

I increment/decrement the angles by pressing keys on the keyboard and then I reconstruct the matrix.

The problem is that this method assumes that the camera has direction (0, 0, -1), right (1, 0, 0, 0) and up (0, 1, 0) and the 3 angles initialized at zero.

When another camera is not initialized like this one but with a random orientation, as soon as I press a key it rotates from the camera directed to (0, 0, -1). (Because all three angles are initialized to 0). I can't figure out how to initialize my angles with my camera.

I would be very grateful for your help in order to be able to finish this camera rotation function.

I tried this:

float my_atan(float y, float x)
{
	float res;
	
	res = 0;
	if (x > 0)
		res = atan(y / x);
	else if (y >= 0 && x < 0)
		res = M_PI + atan(y / x);
	else if (y < 0 && x < 0)
		res = -M_PI + atan(y / x);
	else if (y > 0 && x == 0)
		res = M_PI / 2;
	else if (y < 0 && x == 0)
		res = -M_PI / 2;
	else if (y == 0 && x == 0)
		res = 0;
	else if (y == 0 && x < 0)
		res = res = 2 * M_PI;
	if (res < 0)
		res += 2 * M_PI;
	return (res);
}

float phi = my_atan(cam->vec.y, cam->vec.x);
float theta = atan(sqrt(cam->vec.x * cam->vec.x + cam->vec.y * cam->vec.y) / cam->vec.z);
	
cam->yaw = theta;
cam->pitch = phi;
cam->roll = 0;
Advertisement

I am unsure how your dot(x,y) / z provides angle of yaw

Instead you should use z and x components, y stands for z and x for x

In this myatan function, hovewer

I had similar function that returned angles and i dont know what exactly your function returns.

But i was doing something like this 360.0*(my_atan()/ 6.2731860);

And this gave me an angle however i have used non unit scale vector as atan func input

template <class type> type  n2dGetPolarCoordAngleA(type x,type y)@{@if ( (x > 0) &amp;&amp; (y >= 0) ) { 	return atan(y/x);        }@@if ( (x > 0) &amp;&amp; (y < 0) )  { 	return atan(y/x)+2.0*3.1415926535897932384626433832795;  	  }@@if  (x < 0)                {	return atan(y/x)+3.1415926535897932384626433832795;   	  }@@if ( (x == 0) &amp;&amp; (y > 0) )  { 	return 3.1415926535897932384626433832795/2.0;                   }@@if ( (x == 0) &amp;&amp; (y < 0) )  { 	return 3.0*(3.1415926535897932384626433832795/2.0);                 }@
return - 1000.0;
}

This is my atan function

This was usage

angle = n2dGetPolarCoordAngleA(vector.x*100.0, vector.y*100.0) / 6.2731860f;


angle = 360.0f*angle;
angle = VALIDUJ(angle); //since angle can be greater than 360
template <class type> type  VALIDUJ(type angle2)

{
				   type angle = angle2;

int kat=(int)angle2;

kat=kat/360;

if (angle < 0 )

   {

   angle = angle + (type)(kat+1)*360.0;

   }

if (angle >= 360)

   {

   angle = angle - (type)kat*360.0;

   }



return angle;

}

Anyways output has to cover to your 0,0,0 angle reference that means

Right for 0 angle is actually positive x so its ok left is negative x up is positive y

So for yaw this states that you add 270.0 (or 3.0*(pi/2.0)) to yaw = my_atan(z,x)+270.0

Pitch = my_atan(y,x);

Not sure why i divided this by 6.2731860 i dont remember, cant find the reason i did that its really old code i think it has to do something corelated between dehrees and radians

So actually i dont kniw what your question is

float			wrap_angle(float angle)
{
	float two_pi;

	two_pi = 2 * M_PI;
	return (angle - two_pi * floor((angle + M_PI) / two_pi));
}

void	init_camera_angles(t_camera *cam)
{
	cam->yaw = atan2(cam->vec.x, -cam->vec.z);
	cam->pitch = asin(cam->vec.y / -sqrt(get_norm_2(cam->vec)));
	cam->roll = 0;
}

void        move_cam(t_specs *s, int key)
{
t_camera    *cam;
t_vec3      orientation[3];

cam = s->current_cam;
orientation[0] = cam->right;
orientation[1] = cam->up;
orientation[2] = cam->forward;

if (key == KEY_W)
    cam->coord = vec_add(cam->coord, cam->up);
else if (key == KEY_S)
    cam->coord = vec_sub(cam->coord, cam->up);
else if (key == KEY_A)
    cam->coord = vec_sub(cam->coord, cam->right);
else if (key == KEY_D)
    cam->coord = vec_add(cam->coord, cam->right);
else if (key == KEY_Q)
    cam->coord = vec_sub(cam->coord, cam->vec);
else if (key == KEY_E)
    cam->coord = vec_add(cam->coord, cam->vec);
else if (key == KEY_H)
    cam->yaw += 0.1;
else if (key == KEY_F)
    cam->yaw -= 0.1;
else if (key == KEY_T)
    cam->pitch -= 0.1;
else if (key == KEY_G)
    cam->pitch += 0.1;
else if (key == KEY_R)
    cam->roll += 0.1;
else if (key == KEY_Y)
    cam->roll -= 0.1;
    
cam->yaw = wrap_angle(cam->yaw);
cam->pitch = wrap_angle(cam->pitch);
cam->roll = wrap_angle(cam->roll);

make_rotation_z(orientation, cam->roll);
make_rotation_x(rotation, cam->pitch);
mult_matrix(orientation, rotation, orientation);
make_rotation_y(rotation, cam->yaw);
mult_matrix(orientation, rotation, orientation);

s->current_cam->right = orientation[0];
s->current_cam->up = orientation[1];
s->current_cam->forward = orientation[2];
normalize(&s->current_cam->right);
normalize(&s->current_cam->up);
normalize(&s->current_cam->forward);
}

I have a problem at the beginning of the programme when I initialise the three vectors of my camera.
I use this function to initialise these three vectors:

void    init_camera_axis(t_camera *cam)
{
   t_vec3 up_tmp;

   cam->forward = get_normalised(invert(cam->vec));
   if (fabs(cam->vec.y) > 0.7)
   {
       if (cam->vec.z <= 0)
       up_tmp = (t_vec3){0, 0, 1};
       else
           up_tmp = (t_vec3){0, 0, -1};
   }
   else
       up_tmp = (t_vec3){0, 1, 0};
   cam->right = vec_cross(up_tmp, cam->forward);
   cam->up = vec_cross(cam->forward, cam->right);
}

The problem is that I get a right equal to (0.85, 0, 0) instead of (1, 0, 0), which produces the effect of a different zoom on the first image compared to the others generated with move_cam().

Maybe I'm doing the initialization of the three vectors wrong or I need to use another method?

ellenature said:
I use this function to initialise these three vectors:

Oh, maybe the strange stretching comes from there. I see it does not guarantee orthogonal axis. See here:

cam->right = vec_cross(up_tmp, cam->forward);

Angle between up_temp and forward is not guaranteed to be 90 degrees, so the result won't have unit length and you should normalize it:

cam->right = get_normalised(vec_cross(up_tmp, cam->forward));

Indeed it was necessary to normalize right and up.

Thanks again for your help. ?

Advertisement

The way I find up_tmp in init_camera_axis() is not optimal because if my cam->vec is (1, 1, -1) I can't find the right up_tmp. I currently find (0, 0, 1) and it should be equal to (-1, 0, 1). Do you have any idea how I can correct this ?

This topic is closed to new replies.

Advertisement