нахождение высоты на карте высот, натянутой на сферу C#

Ищу помощи по математике. У меня есть игра, в которой создается 2D-карта высот, а затем растягивается по сфере с использованием формулы длины/направления. Теперь мне нужно знать, как рассчитать высоту между двумя точками на моей карте высот.

Что я знаю:

  • Массив, содержащий карту высот
  • Угол в радианах к моему объекту
  • сколько точек на карте высот

Моя проблема выглядит примерно так:

изображение

дополнительные изображения

Красная и синяя линии — это две точки карты высот, а светло-синяя — это место, где я хотел бы рассчитать высоту.

Вот мой текущий код для этого, но он работает не очень хорошо.

public double getheight(double angle)
{
    //find out angle between 2 heightmap point
    double offset = MathHelper.TwoPi / (heightmap.Length - 1);
    //total brainfart attempt
    double lowerAngle = offset * angle;
    double upperAngle = offset * angle + offset;
    //find heights
    double height1 = heightmap[(int)lowerAngle];
    double height2 = heightmap[(int)upperAngle];
    //find offset angle
    double u = angle - lowerAngle / (upperAngle - lowerAngle);
    //return the height
    return height1 + (height1 - height2) * u;
}

Судя по моему коду растительности, это работает нормально, но его неудобно использовать для юнитов и т. д., поскольку они прыгают вверх/вниз при движении из-за того, что для этого используется только 1 точка карты высот.

double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];

person Doodles    schedule 22.01.2013    source источник


Ответы (1)


EDIT: пример в конце на основе дополнительной информации о вопросе

Звучит как линейная интерполяция - если вы посмотрите на это с двухмерной точки зрения, у вас есть две точки:

(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap

и одна точка где-то между (x1,x2) на неизвестной высоте:

pu = (xu, yu)

Общая формула для LERP:

pu = p0 + (p1 - p0) * u

где:

  • p0 = первое значение
  • p1 = второе значение
  • u = %, где ваша неизвестная точка находится между (p0,p1)

Здесь мы скажем p0 == y2 и p1 == y1. Теперь нам нужно определить, «как далеко» находится неизвестная точка между x1 и x2 — если вы знаете углы к двум точкам карты высот, это легко:

u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))

В качестве альтернативы вы можете спроецировать свой угол на Max(y1,y2) и таким образом получить «неизвестную позицию x», а затем вычислить приведенное выше.

Итак, попробуем надуманный пример:

p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees

обратите внимание, что здесь «ось x» проходит вдоль поверхности сферы, а «ось y» — это расстояние от центра.

pu = object location = py @angle 100 degrees ~ 1.74 radians

px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%

py = y0 + (y1 - y0) * u
   = 2 + (4 - 2) * 0.74
   = 2.96

Надеюсь, я не уронил и не перепутал табличку где-то... :)

Хорошо, ваш пример кода - я его немного подправил, вот что у меня получилось:

Во-первых, давайте определим некоторые мои собственные помощники:

public static class MathHelper
{
    public const double TwoPi = Math.PI * 2.0;
    public static double DegToRad(double deg)
    {
        return (TwoPi / 360.0) * deg;
    }
    public static double RadToDeg(double rad)
    {
        return (360.0 / TwoPi) * rad;
    }

    // given an upper/lower bounds, "clamp" the value into that
    // range, wrapping over to lower if higher than upper, and
    // vice versa    
    public static int WrapClamp(int value, int lower, int upper)
    {
        return value > upper ? value - upper - 1
            : value < lower ? upper - value - 1
            : value;
    }
}

Наша тестовая установка:

void Main()
{
    var random = new Random();

    // "sea level"
    var baseDiameter = 10;

    // very chaotic heightmap
    heightmap = Enumerable
        .Range(0, 360)
        .Select(_ => random.NextDouble() * baseDiameter)
        .ToArray();

    // let's walk by half degrees, since that's roughly how many points we have
    for(double i=0;i<360;i+=0.5)
    {
        var angleInDegrees = i;
        var angleInRads = MathHelper.DegToRad(i);
        Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
            angleInDegrees,
            angleInRads,
            heightmap[(int)angleInDegrees],
            getheight(angleInRads));
    }
}

double[] heightmap;

И наш метод "geheight":

// assume: input angle is in radians
public double getheight(double angle)
{
    //find out angle between 2 heightmap point
    double dTheta = MathHelper.TwoPi / (heightmap.Length);

    // our "offset" will be how many dThetas we are
    double offset = angle / dTheta;

    // Figure out two reference points in heightmap
    // THESE MAY BE THE SAME POINT, if angle ends up
    // landing on a heightmap index!
    int lowerAngle = (int)offset;
    int upperAngle = (int)Math.Round(
        offset, 
        0, 
        MidpointRounding.AwayFromZero);

    // find closest heightmap points to angle, wrapping
    // around if we go under 0 or over max
    int closestPointIndex = MathHelper.WrapClamp(
        lowerAngle, 
        0, 
        heightmap.Length-1);
    int nextPointIndex = MathHelper.WrapClamp(
        upperAngle, 
        0, 
        heightmap.Length-1);

    //find heights
    double height1 = heightmap[closestPointIndex];
    double height2 = heightmap[nextPointIndex];

    // percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
    double percent = (angle - (closestPointIndex * dTheta)) / dTheta;

    // find lerp height = firstvalue + (diff between values) * percent
    double lerp = Math.Abs(height1 + (height2 - height1) * percent);

    // Show what we're doing
    Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]", 
        dTheta, 
        offset, 
        closestPointIndex, 
        nextPointIndex);
    Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}", 
        percent,
        height1, 
        height2,
        lerp);

    return lerp;
}
person JerKimball    schedule 22.01.2013