netlib.narod.ru | < Назад | Оглавление | Далее > |
После того, как мы сконструировали ландшафт, хорошо добавить возможность перемещать камеру таким образом, чтобы она имитировала ходьбу по ландшафту. То есть нам надо менять высоту камеры (координату Y) в зависимости от того, в каком месте ландшафта мы находимся. Для этого мы сначала должны определить по координатам X и Z камеры квадрат ландшафта в котором мы находимся. Все это делает функция Terrain::getHeight; в своих параметрах она получает координаты X и Z камеры и возвращает высоту, на которой должна быть расположена камера, чтобы она оказалась над ландшафтом. Давайте рассмотрим реализацию функции.
float Terrain::getHeight(float x, float z) { // Выполняем преобразование перемещения для плоскости XZ, // чтобы точка START ландшафта совпала с началом координат. x = ((float)_width / 2.0f) + x; z = ((float)_depth / 2.0f) - z; // Масштабируем сетку таким образом, чтобы размер // каждой ее ячейки стал равен 1. Для этого используем // коэффициент 1 / cellspacing поскольку // cellspacing * 1 / cellspacing = 1. x /= (float)_cellSpacing; z /= (float)_cellSpacing;
Сперва мы выполняем перемещение в результате которого начальная точка ландшафта будет совпадать с началом координат. Затем мы выполняем операцию масштабирования с коэффициентом равным единице деленной на размер клетки; в результате размер клетки ландшафта будет равен 1. Затем мы переходим к новой системе координат, где положительное направление оси Z направлено «вниз». Конечно, вы не найдете кода меняющего систему координат, просто помните, что ось Z направлена вниз. Все эти этапы показаны на рис. 13.9.
Рис. 13.9. Исходная сетка ландшафта и сетка после переноса начальной точки ландшафта в начало координат, масштабирования, делающего размер квадрата равным 1 и смены направления оси Z
Мы видим, что измененная координатная система соответствует порядку элементов матрицы. То есть верхний левый угол — это начало координат, номера столбцов увеличиваются вправо, а номера строк растут вниз. Следовательно, согласно рис 13.9, и помня о том, что размер ячейки равен 1, мы сразу видим что номер строки и столбца для той клетки на которой мы находимся вычисляется так:
float col = ::floorf(x); float row = ::floorf(z);
Другими словами, номер столбца равен целой части координаты X, а номер строки — целой части координаты Z. Также вспомните, что результатом функции floor(t) является наибольшее целое число, которое меньше или равно t.
Теперь, когда мы знаем в какой ячейке находимся, можно получить высоты ее четырех вершин:
// A B // *---* // | / | // *---* // C D float A = getHeightmapEntry(row, col); float B = getHeightmapEntry(row, col+1); float C = getHeightmapEntry(row+1, col); float D = getHeightmapEntry(row+1, col+1);
Теперь мы знаем в какой ячейке находимся и высоты всех четырех ее вершин. Нам надо найти высоту (координату Y) точки ячейки с указанными координатами X и Z, где находится камера. Это нетривиальная задача, поскольку ячейка может быть наклонена, как показано на рис. 13.10.
Рис. 13.10. Высота ячейки (координата Y) для заданных координат местоположения камеры X и Z |
Чтобы определить высоту мы должны узнать в каком треугольнике ячейки мы находимся. Вспомните, что каждая ячейка визуализируется как два треугольника. Чтобы определить в каком треугольнике мы находимся, мы берем тот квадрат сетки в котором находимся и перемещаем его таким образом, чтобы верхняя левая вершина совпадала с началом координат.
Поскольку переменные row и col определяют местоположение левой верхней вершины той ячейки где мы находимся, необходимо выполнить перемещение на –col по оси X и –row по оси Z. Преобразование координат X и Z выполняется так:
float dx = x - col; float dz = z - row;
Ячейка после выполнения преобразования показана на рис. 13.11.
Рис. 13.11. Ячейка до и после преобразования, переносящего ее верхнюю левую вершину в начало координат
Теперь, если dz < 1.0 – dx мы находимся в «верхнем» треугольнике Δv0v1v2. В ином случае мы находимся в «нижнем» треугольнике Δv0v2v3 (рис. 13.10).
Теперь мы посмотрим как определить высоту, если мы находимся в «верхнем» треугольнике. Для «нижнего» треугольника процесс аналогичен и ниже будет приведен код для обоих вариантов. Чтобы найти высоту когда мы находимся в «верхнем» треугольнике, надо сформировать два вектора u = (cellSpacing, B – A, 0) и v = (0, C – A, –cellSpacing), совпадающих со сторонами треугольника и начинающихся в точке, заданной вектором q = (qx, A, qz), как показано на рис. 13.12(а) Затем мы выполняем линейную интерполяцию вдоль вектора u на dx и вдоль вектора v на dz. Эти интерполяции показаны на рис. 13.12(б). Координата Y вектора (q + dxu + dzv) дает нам высоту для заданных координат X и Z; чтобы увидеть, как это происходит, вспомните геометрическую интерпретацию сложения векторов.
Рис. 13.12. (а) Вычисляем два вектора, совпадающих со сторонами треугольника. (б) Высота вычисляется путем линейной интерполяции u на dx и v на dz
Обратите внимание, что поскольку нас интересует только значение высоты, мы можем выполнять интерполяцию только для компоненты y и игнорировать остальные компоненты. В этом случае высота определяется по формуле A + dxuy + dzvy.
Итак, вот заключительная часть кода метода Terrian::getHeight:
if(dz < 1.0f - dx) // верхний треугольник ABC { float uy = B - A; // A->B float vy = C - A; // A->C height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz); } else // нижний треугольник DCB { float uy = C - D; // D->C float vy = B - D; // D->B height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz); } return height; }
Функция Lerp выполняет линейную интерполяцию вдоль одномерной линии и ее реализация выглядит так:
float d3d::Lerp(float a, float b, float t) { return a - (a*t) + (b*t); }
netlib.narod.ru | < Назад | Оглавление | Далее > |