Как правильно получить высоту местности в точке (x, z) на пересеченной местности - PullRequest
1 голос
/ 28 апреля 2011

Чтобы правильно отобразить каждый пиксель изображения карты высот на местности, мы получим ландшафт как по ширине, так и по длине на 1 единицу меньше, чем фактическая ширина и длина карты высот. Например, если у нас есть изображение шириной всего 2 пикселя, каждый пиксель будет отображаться в каждой вершине четырехугольника. У нас есть 2 пикселя, но только 1 единица местности.

Я пытаюсь «исправить» эту проблему, масштабируя местность, чтобы заполнить пустой квад в конце местности. Однако это проблематично, когда я пытаюсь получить высоту ландшафта в некоторой точке (x, z). Проблема не совсем в масштабировании, как вы увидите.

Сначала давайте посмотрим на код, который работает. Тот, где масштабирование не учитывает отсутствующий квад. У меня есть карта высот 1024x1024, которая создаст ландшафт с квадратами 1023x1023 (я на самом деле использую треугольники, но это легко объяснить с помощью квадраторов) без какого-либо масштабирования.

Теперь давайте масштабируем ландшафт примерно до 2000x2000, передав соответствующие аргументы следующей функции:

void Terrain::ScaleDimension(float dimWidth, float dimLength) {
    if(dimWidth <= 0.0f || dimLength <= 0.0f) return;

    stepWidth = dimWidth / width; // 1.953125 = 2000 / 1024
    stepLength = dimLength / length; // 1.953125 = 2000 / 1024
}

Когда я рисую ландшафт, я использую stepWidth и stepLength, чтобы соответствующим образом масштабировать ландшафт соответственно. Ландшафт будет масштабироваться, но будет использоваться только 1023 квадра, всегда 1023, не более (что нормально). Теперь предположим, что я перемещаю своего игрового персонажа и мне нужно получить высоту ландшафта в текущей позиции игрока. У меня есть функция, которая будет принимать x и z координаты положения игрока и возвращать высоту в этой точке. Но поскольку размер местности измерен, я не могу просто использовать x и z, переданные в функцию, мне нужно вычислить правильные, например:

x = x / stepWidth;
z = z / stepLength;

После этого мне просто нужно найти 4 смежные вершины новых x и z и выполнить билинейную интерполяцию по высоте всех вершин, чтобы вычислить правильную высоту на позиции игрока.

Все это работает просто отлично. Ландшафт правильно прорисован, и все высоты правильно рассчитаны. Моя проблема в том, что я пытаюсь обойти «ограничение», описанное в начале этого вопроса.

Чтобы обойти проблему, я изменяю свою функцию масштаба следующим образом:

void Terrain::ScaleDimension(float dimWidth, float dimLength) {
    if(dimWidth <= 0.0f || dimLength <= 0.0f) return;

    stepWidth = dimWidth / (width - 1.0f);
    stepLength = dimLength / (length - 1.0f);
}

Допустим, мой размер рельефа (значения, которые я передаю вышеупомянутой функции масштабирования) - это точно такой же размер карты высот, как 1024x1204. Это даст мне шаг 1.000977517106549364613880742913, при котором 1023 квадра будут точно соответствовать от 0 до 1024 (размер местности). Пока все хорошо, местность прорисована точно так, как я ожидал.

Настоящая проблема - вычисление высоты. Я пробовал много вещей, но я просто не могу понять это уравнение для вычисления правильных x и z, которые будут использоваться в функции, которая возвращает высоту, , учитывая новый шаг вычисления выше . Дайвинг x и z с размером шага, как я делал раньше, просто не поможет. Шаг теперь вычисляется немного по-другому и не так просто, как раньше.

Ответы [ 2 ]

0 голосов
/ 24 апреля 2014

Методы слишком просты.А как насчет сложной трехмерной местности со зданиями и объектами?Я использовал glReadPixels, чтобы прочитать буфер глубины и использовать простую математику, чтобы найти высоту над ландшафтом в любом месте сцены.

0 голосов
/ 28 апреля 2011

Если вы хотите перейти от 0..1023 в масштабе до 0..1999, вам необходимо выполнить выборку по каждой позиции, чтобы получить правильные значения.

Given:  old        new
       0,0       == 0,0
       0,1023    == 0,1999
       1023,0    == 1999,0
       1023,1023 == 1999,1999

Другими словами, четыре угла остаются прежними.

Теперь вам нужно вычислить высоту любой заданной точки, вы бы назвали ее так:

float height = convert(1500,1450,1024,2000);

float convert(int newx, int newy, int origScale, int newScale) {
    float factor = ((float)(origScale)) / (float)(newScale);
    float oldx = newx * factor;
    float oldy = newy * factor;

    int oldxlo = floorInt(oldx); // oldx == 3.4, oldxlo = 3
    int oldxhi = ceilInt(oldx); // oldx == 3.4, oldxhi = 34
    int oldylo = floorInt(oldy); // oldy == 3.4, oldylo = 3
    int oldyhi = ceilInt(oldy); // oldy == 3.4, oldyhi = 34

    float topLeftHeight = getHeight(oldxlo,oldylo);
    float topRightHeight = getHeight(oldxhi,oldylo);
    float botLeftHeight = getHeight(oldxlo,oldyhi);
    float botRightHeight = getHeight(oldxhi,oldyhi);

    // now you have the heights of the four corners of the old grid
    // that surround the new grid. You can use a form of interpolation on this grid,
    // I'm sure you can find one somewhere.

    Point3D topLeft(oldxlo,oldylo,topLeftHeight);
    Point3D topRight(oldxhi,oldylo,topRightHeight);
    Point3D botLeft(oldxlo,oldyhi,botLeftHeight);
    Point3D botRight(oldxhi,oldyhi,botRightHeight);


    // or something
    return bilinearInterpolate(topLeft,topRight,botLeft,botRight,newx,newy);

}
...