Как правильно проецировать воксельный мир в камеру?

Для выпускания лучей из камеры и проверки столкновения используется следующий код (основные части):

typedef struct 
{
    int    i;       //// текущий индекс ячейки решётки
    double dt;      //// шаг между пересечениями
    double next_t;  //// момент следующего пересечения решётки
    int    di;      //// как меняется индекс при пересечении решётки
} range_t;

void init_range(double start, double end, range_t* r) {
    r->i = (int)floor(start);
    r->dt = 1. / fabs(end - start);
    if (start < end) {
        r->next_t = r->dt * (floor(start) + 1. - start);
        r->di = 1;
    }
    else if (start > end) {
        r->next_t = r->dt * (start - floor(start));
        r->di = -1;
    }
    else {
        r->next_t = 2;  //// заведомо большое значение
        r->di = 0;
    }
}
    int startX = 0, startY = 0, startZ = 0; // координаты камеры
    int endX = 0; // экран в 3д мире (циклы ниже)
    int endY = 0; 
    int endZ = Screen_Width; // Влияет на угол обзора

    for (endY = -Screen_Half_Height; endY < Screen_Half_Height; endY++)
    {
        for (endX = -Screen_Half_Width; endX < Screen_Half_Width; endX++)
        {
            int aX = endX - startX;
            int aY = endY - startY;
            int aZ = endZ - startZ;

            double aX1 = aX * multiplier; 
            double aY1 = aY * multiplier;
            double aZ1 = aZ * multiplier;

            double sx = startX; // начальная точка отрезка в 3д пространстве
            double sy = startY;
            double sz = startZ;

            double ex = aX1; // конечная точка отрезка в 3д пространстве
            double ey = aY1;
            double ez = aZ1;

            range_t rx;
            range_t ry;
            range_t rz;

            init_range(sx, ex, &rx);
            init_range(sy, ey, &ry);
            init_range(sz, ez, &rz);

            double t = 0;
            while (t < 1) {

                if (rx.i == 2 && ry.i == 2 && rz.i == 10) // координаты вокселя 2, 2, 10
                {
                    Screen[endY * Screen_Width * 3 + endX * 3 + 0] = 255; // установить цвет пикселя на экране по координатам Y, X
                    Screen[endY * Screen_Width * 3 + endX * 3 + 1] = 0; // Green
                    Screen[endY * Screen_Width * 3 + endX * 3 + 2] = 0; // Blue
                    break; // завершить цикл while, если найден воксель
                }

                //// rx->next_t - параметр t, когда отрезок пересечёт целый x
                //// ry->next_t - параметр t, когда отрезок пересечёт целый y
                //// rz->next_t - параметр t, когда отрезок пересечёт целый z
                //// выбираем из них наименьший
                range_t* nearest = &rx;
                if (ry.next_t < nearest->next_t) {
                   nearest = &ry;
                }
                if (rz.next_t < nearest->next_t) {
                    nearest = &rz;
                }

                t = nearest->next_t;  //// увеличиваем параметр t

                nearest->next_t += nearest->dt;  //// запоминаем следующее пересечение
                nearest->i += nearest->di;       //// исправляем координату куба
            }
        }
    }

При разрешении экрана 640х480 (соотношение 4:3) всё работает хорошо, из-за соотношения проблема не заметна:

При изменении разрешения экрана на 2к или FHD, из-за чего меняется и соотношение сторон на 16:9, начинает появляется дефект:

Интересно, что дальняя часть больше, чем передняя (176 и 156), однако если сравнивать с 4:3 должно быть наоборот.

По моим ощущениям, оси Y не хватает высоты, из-за чего все эти прямоугольники, которые наслаиваются друг на друга, съезжают вниз. Я попробовал сделать следующее:

int aY = (endY - startY) * 1.1;

Коэффициент был выбран случайно, экспериментальным путём.

Это несколько исправило ситуацию:

Однако мне бы хотелось понять: Как рассчитать данный коэффициент относительно заданного разрешения экрана?

int const Screen_Width = 1920;
int const Screen_Height = 1080;

Чтобы можно было использовать коэффициент при разном разрешении и соотношении сторон.

При увеличении коэффициента 1.1 куб превращается в параллелепипед (разница между нижней и правой гранью увеличивается). При уменьшении обрезается картинка. Видимо придётся и X и Z домножать на коэффициент, но на какой?

Заменил

int endZ = Screen_Width;

на

int endZ = Screen_Height;

Угол обзора почти 90 градусов и при этом куб стал правильным


Ответы (0 шт):