Всем привет. Недавно я занялся нахождением возможности бросить луч не используя классический метод с перемножением обратных матриц. Меня эта идея зацепила и я стал исследовать, возможно ли как-то сделать то же самое, но без обратных матриц. И вот что получилось. Есть видео и также описание код приложу в туториал.
Начнём с того, что нам понадобятся нормали сторон камеры. Я приведу свой пример из кода. Вот как выглядит получение нормалей всех сторон камеры. Код на C.
void camera_commit (struct camera *cam) { float p[3]; float f[3]; vec3_add (p, cam->pos, cam->front); vec3_cross (f, cam->front, cam->up); vec3_norm (cam->right, f); vec3_cross (cam->real_up, cam->front, cam->right); lookat(cam->view, cam->pos, p, cam->up); }
Для вычислений позиции X на расстоянии нам понадобиться масштабирование экрана, которое вычисляется по формуле:
Идея очень простая и действенная. Мы берём фронт камеры и умножаем на нужную нам дальность.
vec3_mul_scalar (v0, cam->front, z_far);
Потом нам надо вычислять настоящие расстояния для ближней точки и для дальней.
float near_x = (float) xx / (h / kh); float near_y = (float) yy / (v * (aspect / kh)); float far_x = (near_x) / (2.f * kh); float far_y = near_y / 2.f; float x0 = ((far_x * ((z_far + 1.f) * kh))); float y0 = ((far_y * ((z_far + 1.f))));
z_far нам надо складывать с единицей. Честно сказать, я немного подзабыл некоторый замысел этих вычислений, я просто несколько дней не вылазил из расчетов. Но на единицу я прибавил, чтобы вроде как увеличить коэффициент для дальности и соответственно для правильного расположения координат.
Потом сдвигаем правую и верхнюю нормаль камеры в стороны:
vec3_mul_scalar (v1, cam->right, x0); vec3_add (v2, v0, v1); vec3_mul_scalar (v1, cam->real_up, y0); vec3_sub (v0, v2, v1); vec3_copy (ray->dir, v0); vec3_mul_scalar (v1, cam->right, near_x); vec3_mul_scalar (v2, cam->real_up, near_y); vec3_add (v3, cam->front, v1); vec3_sub (v0, v3, v2); vec3_copy (ray->origin, v0);
ray->dir это направление. ray->origin это точка начала. Думаю по коду можно разобраться что здесь происходит.
И всё, луч готов.
Приведу полный код функции, которую конечно же надо немного подправить, так как я тестировал на ней другие лучи вместе.
void make_ray_from_cursor_to_far (struct game *game, struct camera *cam, struct ray *ray, int x, int y, float z_far) { float v0[3]; float v1[3]; float v2[3]; float v3[3]; float d0[3]; float d1[3]; int xx = x - game->screen->w / 2; int yy = game->screen->h / 2 - y; float aspect = game->screen->aspect; float h = game->screen->fw * 0.5f; float v = game->screen->fh * 0.5f; float null_vector[3] = {0.f, 0.f, 0.f}; float kh = (aspect + 1.f) / (1.f + aspect / 2.f); float near_x = (float) xx / (h / kh); float near_y = (float) yy / (v * (aspect / kh)); float far_x = near_x / (2.f * kh); float far_y = near_y / 2.f; float x0 = ((far_x * ((z_far + 1.f) * kh))); float y0 = ((far_y * ((z_far + 1.f)))); vec3_mul_scalar (v0, cam->front, z_far); vec3_mul_scalar (v1, cam->right, x0); vec3_add (v2, v0, v1); vec3_mul_scalar (v1, cam->real_up, y0); vec3_sub (v0, v2, v1); vec3_copy (ray->dir, v0); vec3_mul_scalar (v1, cam->right, near_x); vec3_mul_scalar (v2, cam->real_up, near_y); vec3_add (v3, cam->front, v1); vec3_sub (v0, v3, v2); vec3_copy (ray->origin, v0); float e0[3]; vec3_add (e0, cam->pos, v0); vec3_copy (ray->pos, e0); opengl_ray_setup (ray); translate (ray->transform, cam->pos[0], cam->pos[1], cam->pos[2]); float view[16]; mat4x4_mul (view, ray->transform, cam->view); mat4x4_mul (ray->model, view, ray->projection); }
Таким образом мы теперь знаем ещё один способ бросить луч и мне кажется, что это будет работать быстрее, чем мой способ нахождения двух обратных матриц, а потом и их перемножение.
Для точности нужно дальность луча нужно устанавливать очень большую, к примеру 100 или 1000.
Вот видео как происходит сравнение луча с AABB.
