简体   繁体   中英

finding height on a heightmap stretched over a sphere C#

I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.

What I know:

  • The array that holds the heightmap
  • The angle in radians to my object
  • how many points there are on the heightmap

My problem look somewhat like so:

image

more images

The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.

Here's my current code to do it, but it doesn't work to well.

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;
}

from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.

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

EDIT: example at end based on additional question info

Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:

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

and one point somewhere between (x1,x2) at an unknown height:

pu = (xu, yu)

A generic formula for LERP is:

pu = p0 + (p1 - p0) * u

where:

  • p0 = first value
  • p1 = second value
  • u = % where your unknown point lies between (p0,p1)

Here, we'll say p0 == y2 and p1 == y1 . Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:

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

Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.

So, let's try a contrived example:

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

note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.

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

Hopefully I didn't drop or misplace a sign there somewhere... :)

Ok, your example code - I've tweaked it a bit, here's what I've come up with:

First, let's define some helpers of my own:

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;
    }
}

Our Test setup:

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;

And our "getheight" method:

// 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM