Как правильно проецировать воксельный мир в камеру?
Для выпускания лучей из камеры и проверки столкновения используется следующий код (основные части):
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 = ℞
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 градусов и при этом куб стал правильным





