Kedriik
Kedriik

Reputation: 331

Texture filtering for terrain generation

I use low resolution topography texture for my terrain generation with three interpolation algorithms however I can't get rid of checkered pattern. How to fix that and made my terrain smooth?

Bilinear interpolation:

Bilinear interpolation

Constrained Bicubic Interpolation:

enter image description here

Catmull-Rom:

CatmullRom

Two first images uses algorithm straight from this site

Catmull-Rom was found on shadertoy but references are in source code.

Bilinear:

    auto sampleData = [this](FVector2D __uv) {
        auto W_function = [](auto X, auto Y) {
            return X * Y;
        };

    float X = __uv.X * float(gX - 1);
    float Y = __uv.Y * float(gY - 1);
    if (X < 0) X = 0;
    if (Y < 0) Y = 0;
    if (X > gX - 1) X = gX - 1;
    if (Y > gY - 1) Y = gY - 1;
    float PixelColor = 0, PixelColor1 = 0, PixelColor2 = 0, PixelColor3 = 0;
    PixelColor = FormatedTopographyImageData[int(X) * (gY)+int(Y)];

    if (X <= gX - 2 && Y <= gY - 2)
        PixelColor1 = FormatedTopographyImageData[(int(X) + 1) * (gY)+int(Y) + 1];
    if (Y <= gY - 2)
        PixelColor2 = FormatedTopographyImageData[int(X) * (gY)+int(Y) + 1];
    if (X <= gX - 2)
        PixelColor3 = FormatedTopographyImageData[(int(X) + 1) * (gY)+int(Y)];

    float temp;
    float FinalColor1 =
        W_function((1.0f - modf(X, &temp)), (1.0f - modf(Y, &temp))) * PixelColor +
        W_function(modf(X, &temp), (1.0f - modf(Y, &temp))) * PixelColor3 + //+X
        W_function((1.0f - modf(X, &temp)), modf(Y, &temp)) * PixelColor2 + //+Y
        W_function(modf(X, &temp), modf(Y, &temp)) * PixelColor1; //+X +Y

    return FinalColor1;
};

Bicubic:

auto sampleDataNotLinear = [this](FVector2D __uv) {
    auto W_function = [](auto X, auto Y) {
        return X * X * Y * Y 
            *( 9 - 6 * X - 6 * Y + 4 * X * Y);
    };

    float X = __uv.X * float(gX - 1);
    float Y = __uv.Y * float(gY - 1);
    if (X < 0) X = 0;
    if (Y < 0) Y = 0;
    if (X > gX - 1) X = gX - 1;
    if (Y > gY - 1) Y = gY - 1;
    float PixelColor = 0, PixelColor1 = 0, PixelColor2 = 0, PixelColor3 = 0;
    PixelColor = FormatedTopographyImageData[int(X) * (gY)+int(Y)];

    if (X <= gX - 2 && Y <= gY - 2)
        PixelColor1 = FormatedTopographyImageData[(int(X) + 1) * (gY)+int(Y) + 1];
    if (Y <= gY - 2)
        PixelColor2 = FormatedTopographyImageData[int(X) * (gY)+int(Y) + 1];
    if (X <= gX - 2)
        PixelColor3 = FormatedTopographyImageData[(int(X) + 1) * (gY)+int(Y)];

    float temp;
    float FinalColor1 =
        W_function((1.0f - modf(X, &temp)), (1.0f - modf(Y, &temp))) * PixelColor +
        W_function(modf(X, &temp), (1.0f - modf(Y, &temp))) * PixelColor3 + //+X
        W_function((1.0f - modf(X, &temp)), modf(Y, &temp)) * PixelColor2 + //+Y
        W_function(modf(X, &temp), modf(Y, &temp)) * PixelColor1; //+X +Y

    return FinalColor1;
};

Catmull-Rom:

auto fract = [](float n) {
    float temp;
    return modf(n, &temp);
};
// note: (not) entirely stolen from https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
//
// Samples a texture with Catmull-Rom filtering, using 9 texture fetches instead of 16.
// See http://vec3.ca/bicubic-filtering-in-fewer-taps/ for more details
auto SampleTextureCatmullRom = [this, &sampleData](FVector2D _uv)
{
    // We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding
    // down the sample location to get the exact center of our "starting" texel. The starting texel will be at
    // location [1, 1] in the grid, where [0, 0] is the top left corner.
    FVector2D samplePos = FVector2D(_uv.X * float(gX), _uv.Y * float(gY));
    FVector2D texPos1 = FVector2D(floor(samplePos.X - 0.5f) + 0.5f, floor(samplePos.Y - 0.5f) + 0.5f);

    // Compute the fractional offset from our starting texel to our original sample location, which we'll
    // feed into the Catmull-Rom spline function to get our filter weights.
    FVector2D f = FVector2D(samplePos.X - texPos1.X, samplePos.Y - texPos1.Y);

    // Compute the Catmull-Rom weights using the fractional offset that we calculated earlier.
    // These equations are pre-expanded based on our knowledge of where the texels will be located,
    // which lets us avoid having to evaluate a piece-wise function.

    FVector2D w0 = f * (FVector2D(-0.5f) + f * (FVector2D(1.0f) - 0.5f * f));
    FVector2D w1 = FVector2D(1.0f) + f * f * (FVector2D(-2.5f) + 1.5f * f);
    FVector2D w2 = f * (FVector2D(0.5f) + f * (FVector2D(2.0f) - 1.5f * f));
    FVector2D w3 = f * f * (FVector2D(-0.5f) + 0.5f * f);

    // Work out weighting factors and sampling offsets that will let us use bilinear filtering to
    // simultaneously evaluate the middle 2 samples from the 4x4 grid.
    FVector2D w12 = w1 + w2;
    FVector2D offset12 = w2 / w12;

    // Compute the final UV coordinates we'll use for sampling the texture
    FVector2D texPos0 = texPos1 - FVector2D(1.0f);
    FVector2D texPos3 = texPos1 + FVector2D(2.0f);
    FVector2D texPos12 = texPos1 + offset12;

    texPos0.X /= float(gX);
    texPos0.Y /= float(gY);
    texPos3.X /= float(gX);
    texPos3.Y /= float(gY);
    texPos12.X /= float(gX);
    texPos12.Y /= float(gY);

    float result = 0.0f;
    result += sampleData(FVector2D(texPos0.X, texPos0.Y)) * w0.X * w0.Y;
    result += sampleData(FVector2D(texPos12.X, texPos0.Y)) * w12.X * w0.Y;
    result += sampleData(FVector2D(texPos3.X, texPos0.Y)) * w3.X * w0.Y;

    result += sampleData(FVector2D(texPos0.X, texPos12.Y)) * w0.X * w12.Y;
    result += sampleData(FVector2D(texPos12.X, texPos12.Y)) * w12.X * w12.Y;
    result += sampleData(FVector2D(texPos3.X, texPos12.Y)) * w3.X * w12.Y;

    result += sampleData(FVector2D(texPos0.X, texPos3.Y)) * w0.X * w3.Y;
    result += sampleData(FVector2D(texPos12.X, texPos3.Y)) * w12.X * w3.Y;
    result += sampleData(FVector2D(texPos3.X, texPos3.Y)) * w3.X * w3.Y;

    return FVector(result, result, result);
};

Upvotes: 1

Views: 185

Answers (0)

Related Questions