Search code examples
c++unreal-engine4heightmap

Spherical Terrain generated from HeightMap has Gaps


I created a Icosahedron with DynamicLOD. This works greate. Now i wanted to use a Heightmap to add Terrain. First i tried with GrayScale HeightMap. But the Result was just random displacement. Than i tried with the B/W Image below and got this: enter image description here

For testing i use a B/W Image. enter image description here

As you can see there is no Data that shows Gaps. First i thought i had some Error in reading the Image. But when i write the Data from Memory back into a File i have the same Image.

This is how i read the Image:

TArray<uint8> TempData;
if (!FFileHelper::LoadFileToArray(TempData, TEXT("../../../../../UnrealProjects/LODTest/Content/Materials/nocompression.png") , FILEREAD_Silent))
{
    UE_LOG(LogTemp, Warning, TEXT("Could not load File"));
}
else
{
    IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>("ImageWrapper");
    TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);

    if (!ImageWrapper->SetCompressed(TempData.GetData(), TempData.Num()))
    {
        UE_LOG(LogTemp, Warning, TEXT("Compress Error"));
    }

    if (ImageWrapper->GetFormat() != ERGBFormat::Gray)
    {
        UE_LOG(LogTemp, Warning, TEXT("Not GrayScale"));
    }
    if (ImageWrapper->GetBitDepth() != 16)
    {
        UE_LOG(LogTemp, Warning, TEXT("Not 16Bit"));
    }

    const TArray<uint8>* RawData = nullptr;

    if (!ImageWrapper->GetRaw(ERGBFormat::Gray, 16, RawData))
    {
        UE_LOG(LogTemp, Warning, TEXT("Could not get Raw Data"));
    }
    else
    {

        heightValues.SetNum(ImageWrapper->GetWidth() * ImageWrapper->GetHeight());
        FMemory::Memcpy(heightValues.GetData(), RawData->GetData(), ImageWrapper->GetWidth() * ImageWrapper->GetHeight() * 2);
        UE_LOG(LogTemp, Warning, TEXT("Success"));
    }
}

And here how i read the Values: (point is a UV coordinate)

uint16 heightValue = heightMap[(point.X * imageWidth) * (point.Y * imageHeight)];

float value = heightValue / 65535.f;

return value;

I exported the Mesh into Blender and used the HeightMap there with no Issues. Now i am out of Ideas :(

EDIT: As Azarus said the way i get the height value was wrong. Now i use this:

int32 locX = UKismetMathLibrary::MapRangeClamped(point.X, 0.f, 1.f, 0.f, imageWidth);
int32 locY = UKismetMathLibrary::MapRangeClamped(point.Y, 0.f, 1.f, 0.f, imageHeight);

uint16 heightValue = heightMap[imageWidth * locY + locX];

Solution

  • Your height sampling seems to be wrong:

    heightMap[(point.X * imageWidth) * (point.Y * imageHeight)]
    

    It's basic math really.

    • What if X is 0 and Y is 1?
    • What if X is 1 and Y is 0?

    The answer is the same and that's the problem 1*0=0 and so does 0*1=0

    While you clearly wanted different input.

    Assuming you have read the image correctly then just compressing the 2D coordinate into 1D the following formula should work:

    Compressing 2D Array Coordinates to 1D:

    Index=(X*RowSize)+Y
    

    Applying this to your code:

    heightMap[(point.X * imageWidth) + point.Y]