My first post here. :) I'm currently coding a raytracer for my school project in C. I already can display spheres, triangles and planes with some light effects. Now I want to display cylinder (then cones, but cylinder first !). I choose to have a cylinder parallel to the Y-Axis. So I have to solve : x² + z² = r². So technically, my function that returns the distance between my camera and the intersection point is :
double n_ray_cylinder(t_ray *ray, t_cylinder *cylinder, t_data *d)
{
double a;
double b;
double c;
double delta;
double root;
a = ray->dir->x * ray->dir->x + ray->dir->z * ray->dir->z;
b = 2 * ray->dir->x * (ray->ori->x - cylinder->base->x) + 2 * ray->dir->z * (ray->ori->z - cylinder->base->z);
c = (ray->ori->x - cylinder->base->x) * (ray->ori->x - cylinder->base->x) + (ray->ori->z - cylinder->base->z) * (ray->ori->z - cylinder->base->z) - cylinder->radius * cylinder->radius;
delta = b * b - 4 * a * c;
if (delta > ACC)
{
root = (-1 * b - sqrt(delta)) / 2 * a - ACC;
if (root <= ACC)
root = (-1 * b + sqrt(delta)) / 2 * a - ACC;
return (root);
}
return (-1);
}
with x = (ray->ori->x - cylinder->base->x) et z = (ray->ori->z - cylinder->base->z) et r = cylinder->radius.
And my function that calculates the normal vector is:
t_vect *cylinder_normal_at(t_cylinder *cylinder, t_vect *intersection)
{
t_vect *base_tmp;
t_vect *normal;
t_vect *intersection_tmp;
base_tmp = copy_vector(cylinder->base);
intersection_tmp = copy_vector(intersection);
base_tmp->y = intersection_tmp->y;
normal = init_vector(intersection->x - base_tmp->x, intersection->y - base_tmp->y, intersection-> z - base_tmp->z);
normalize_vector(normal);
return (normal);
}
With these functions, the result is : Image 1
The reflects "seems" good, the shape too, but the light is not good. Normal problem ? I talked about this to a friend of mine, and he told me to do like him : delete some numbers in my first function. So it becomes :
double n_ray_cylinder(t_ray *ray, t_cylinder *cylinder, t_data *d)
{
double a;
double b;
double c;
double delta;
double root;
a = ray->dir->x * ray->dir->x + ray->dir->z * ray->dir->z;
b = ray->dir->x * (ray->ori->x - cylinder->base->x) +
ray->dir->z * (ray->ori->z - cylinder->base->z);
c = (ray->ori->x - cylinder->base->x) * (ray->ori->x - cylinder->base->x) +
(ray->ori->z - cylinder->base->z) * (ray->ori->z - cylinder->base->z) -
cylinder->radius * cylinder->radius;
delta = b * b - a * c;
if (delta > ACC)
{
root = (-1 * b - sqrt(delta)) / a - ACC;
if (root <= ACC)
root = (-1 * b + sqrt(delta)) / a - ACC;
return (root);
}
return (-1);
}
The normal function stays the same. And then the light is ok !
The second function seems to work fine. But why ? Isn't the first one the exact 'application' of the cylinder equation ?
Here is a second problem. I want to rotate my cylinder around the Ox axis. x²+(y.cosθ+z.sinθ)² = r².
After developing the equation, I have this function (based on my second function, of my friend)
double nn_ray_cylinder(t_ray *ray, t_cylinder *cylinder, t_data *d)
{
double a;
double b;
double c;
double delta;
double root;
double deg = M_PI * 45 / 180;
a = ray->dir->x * ray->dir->x + cos(deg) * cos(deg) * ray->dir->z * ray->dir->z +
2 * ray->dir->z * cos(deg) * ray->dir->y * sin(deg) + ray->dir->y * ray->dir->y *
sin(deg) * sin(deg);
b = (ray->ori->x - cylinder->base->x) * ray->dir->x + cos(deg) * cos(deg) * (ray->ori->z - cylinder->base->z) * ray->dir->z +
2 * (ray->ori->z - cylinder->base->z) * cos(deg) * ray->dir->y * sin(deg) + 2 * ray->dir->z * cos(deg) *
(ray->ori->y - cylinder->base->y) * sin(deg) + 2 * (ray->ori->y - cylinder->base->y) * ray->dir->y * sin(deg) * sin(deg);
c = (ray->ori->x - cylinder->base->x) * (ray->ori->x - cylinder->base->x) + (ray->ori->z - cylinder->base->z) * (ray->ori->z - cylinder->base->z)* cos(deg) * cos(deg) +
2 * (ray->ori->z - cylinder->base->z) * cos(deg) * (ray->ori->y - cylinder->base->y) * sin(deg) + (ray->ori->y - cylinder->base->y) * (ray->ori->y - cylinder->base->y) *
sin(deg) * sin(deg) - cylinder->radius * cylinder->radius;
delta = b * b - a * c;
if (delta > ACC)
{
root = (-1 * b - sqrt(delta)) / a - ACC;
if (root <= ACC)
root = (-1 * b + sqrt(delta)) / a - ACC;
return (root);
}
return (-1);
}
The rotation is a success ! But now I have to change the normal vector on this intersection point. For this, I apply an inverted rotation to the intersection point, then I calculate the normal vector like before, then I reapply the rotation to the point to find the normal vector of the rotated cylinder.
t_vect *cylinder_normal_at(t_cylinder *cylinder, t_vect *intersection)
{
t_vect *base_tmp;
t_vect *normal;
t_vect *intersection_tmp;
t_matrix *rotate = init_rotation_matrix(45, 1, 0, 0);
t_matrix *rotate_inverted = init_rotation_matrix(-45, 1, 0, 0);
base_tmp = copy_vector(cylinder->base);
intersection_tmp = copy_vector(intersection);
apply_matrix(rotate_inverted, intersection_tmp);
base_tmp->y = intersection_tmp->y;
apply_matrix(rotate, intersection_tmp);
apply_matrix(rotate, base_tmp);
normal = init_vector(intersection->x - base_tmp->x,
intersection->y - base_tmp->y,
intersection->z - base_tmp->z);
normalize_vector(normal);
return (normal);
}
The result seems good for a basic cylinder and a cylinder rotated by 90 degrees for example. But the reflects are totally wrong with 45 degrees ...
So where are my mistakes ? Is the normal function wrong, or the other one ? And why ? Thanks a lot to those who can help me. I drown myself in mathematics ...
Edit :
Thanks chux, the quadratic mis-coding is now corrected. The problem about the normal vector is still here.
Here I add some my rotation functions :
t_matrix *init_rotation_matrix(double theta, double x, double y, double z)
{
t_matrix *matrix;
double rad;
if ((matrix = malloc(sizeof *matrix)) == NULL)
return (NULL);
rad = theta * M_PI / 180;
matrix->m11 = x * x * (1 - cos(rad)) + cos(rad);
matrix->m12 = x * y * (1 - cos(rad)) - z * sin(rad);
matrix->m13 = x * z * (1 - cos(rad)) + y * sin(rad);
matrix->m14 = 0;
matrix->m21 = y * x * (1 - cos(rad)) + z * sin(rad);
matrix->m22 = y * y * (1 - cos(rad)) + cos(rad);
matrix->m23 = y * z * (1 - cos(rad)) - x * sin(rad);
matrix->m24 = 0;
matrix->m31 = x * z * (1 - cos(rad)) - y * sin(rad);
matrix->m32 = y * z * (1 - cos(rad)) + x * sin(rad);
matrix->m33 = z * z * (1 - cos(rad)) + cos(rad);
matrix->m34 = 0;
matrix->m41 = 0;
matrix->m42 = 0;
matrix->m43 = 0;
matrix->m44 = 1;
return (matrix);
}
void apply_matrix(t_matrix *matrix, t_vect *v)
{
double x;
double y;
double z;
x = v->x;
y = v->y;
z = v->z;
v->x = matrix->m11 * x + matrix->m12 * y + matrix->m13 * z + matrix->m14;
v->y = matrix->m21 * x + matrix->m22 * y + matrix->m23 * z + matrix->m24;
v->z = matrix->m31 * x + matrix->m32 * y + matrix->m33 * z + matrix->m34;
}
Edit 2 :
In the function that calculates the normal vector, I replaced 45 by -45 and 45 by -45. And now it works ... I must have a problem with my z axis. It seems that the positive z and negative z are inverted ...