Jump to content
  • Advertisement
wuyakuma

R&D Prefiltered LightProbe discontinuous

This topic is 444 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Seams.thumb.png.a2190c9874109aede2f9db4f9a7ae83c.png

I was reworking on my LightProbe filter, and I wrote some code to generate the Reference Cubemap, but then I noticed some discontinuous on the border of each face.(Top:CPU implementaion, Bottom: GPU implementation, the contrast has been adjusted on the right side)

At first I think it maybe caused by the interpolation, but then I tried the same algorithm in 2D (like a slice in the normal light probe prefiltering) for better visualization, and the result really confused me.

See the attachments, the top half is the Prefiltered Color value, displayed per channel, it's upside down because I used the ColorValue directly as the y coordinate. 

59a3ec7a626ae_Roughness0.5.thumb.png.9f58ef3ec648dfee8c9fda0ab821ecce.png

59a3ec7b1a040_Roughness1.thumb.png.e8f96810505a36740b99e5b4224fe617.png

The bottom half is the differential of the color, it's very clearly there is a discontinuous, and the position is where the border should be. And as the roughness goes higher, the plot gets stranger .

So, I am kinda of stuck in here, what's happening and what to do to remove this artifact? Anybody have any idea? 

and here is my code

inline FVector2D Map(int32 FaceIndex, int32 i, int32 FaceSize, float& SolidAngle)
{
    float u = 2 * (i + 0.5) / (float)FaceSize - 1;

    FVector2D Return;
    switch (FaceIndex)
    {
    case 0: Return = FVector2D(-u, -1); break;
    case 1: Return = FVector2D(-1, u);  break;
    case 2: Return = FVector2D(u, 1); break;
    case 3: Return = FVector2D(1, -u); break;
    }

    SolidAngle = 1.0f / FMath::Pow(Return.SizeSquared(), 3.0f / 2.0f);
    return Return.SafeNormal();
}

void Test2D()
{
    const int32 Res = 256;
    const int32 MipLevel = 8;

    TArray<FLinearColor>    Source;
    TArray<FLinearColor>    Prefiltered;

    Source.AddZeroed(Res * 4);
    Prefiltered.AddZeroed(Res * 4);

    for (int32 i = 0; i < Res; ++i)
    {
        Source = FLinearColor(1, 0, 0);
        Source[Res + i] = FLinearColor(0, 1, 0);
        Source[Res * 2 + i] = FLinearColor(0, 0, 1);
        Source[Res * 3 + i] = FLinearColor(0, 0, 0);
    }

    const float Roughness = MipLevel / 8.0f;
    const float a = Roughness * Roughness;
    const float a2 = a * a;

    // Brute force sampling with GGX kernel
    for (int32 FaceIndex = 0; FaceIndex < 4; ++FaceIndex)
    {
        for (int32 i = 0; i < Res; ++i)
        {
            float SolidAngle = 0;
            FVector2D N = Map(FaceIndex, i, Res, SolidAngle);

            double TotalColor[3] = {};
            double TotalWeight = 0;
            for (int32 SampleFace = 0; SampleFace < 4; ++SampleFace)
            {
                for (int32 j = 0; j < Res; ++j)
                {
                    float SampleJacobian = 0;
                    FVector2D L = Map(SampleFace, j, Res, SampleJacobian);
                    const float NoL = (L | N);
                    if (NoL <= 0)
                        continue;

                    const FVector2D H = (N + L).SafeNormal();
                    const float NoH = (N | H);

                    float D = a2 * NoL * SampleJacobian / FMath::Pow(NoH*NoH * (a2 - 1) + 1, 2.0f) ;
                    TotalWeight += D;
                    FLinearColor Sample = Source[SampleFace * Res + j] * D;
                    TotalColor[0] += Sample.R;
                    TotalColor[1] += Sample.G;
                    TotalColor[2] += Sample.B;
                }
            }
            if (TotalWeight > 0)
            {
                Prefiltered[FaceIndex * Res + i] = FLinearColor(
                    TotalColor[0] / TotalWeight,
                    TotalColor[1] / TotalWeight,
                    TotalColor[2] / TotalWeight);
            }
        }
    }

    // Save to bmp
    const int32 Width = 4 * Res;
    const int32 Height = 768;

    TArray<FColor> Bitmap;
    Bitmap.SetNum(Width * Height);

    // Prefiltered Color curve per channel
    float MaxDelta = 0;
    for (int32 x = 0; x < Width; ++x)
    {
        FColor SourceColor = Source[x].ToFColor(false);

        Bitmap[x] = SourceColor;

        FColor Sample = Prefiltered[x].ToFColor(false);


        check(Sample.R < 256);
        check(Sample.G < 256);
        check(Sample.B < 256);
        Bitmap[Sample.R * Width + x] = FColor(255, 0, 0);
        Bitmap[Sample.G * Width + x] = FColor(0, 255, 0);
        Bitmap[Sample.B * Width + x] = FColor(0, 0, 255);

        if (x > 0)
        {
            const FLinearColor Delta = Prefiltered[x] - Prefiltered[x - 1];

            MaxDelta = FMath::Max(MaxDelta, FMath::Max3(FMath::Abs(Delta.R), FMath::Abs(Delta.G), FMath::Abs(Delta.B)));
        }
    }

    // Differential per channel
    const float Scale = 128 / MaxDelta;
    for (int32 x = 1; x < Width; ++x)
    {
        const FLinearColor Delta = Prefiltered[x] - Prefiltered[x - 1];

        Bitmap[int32(512 + Delta.R * Scale) * Width + x] = FColor(255, 0, 0);
        Bitmap[int32(512 + Delta.G * Scale) * Width + x] = FColor(0, 255, 0);
        Bitmap[int32(512 + Delta.B * Scale) * Width + x] = FColor(0, 0, 255);
    }

    FFileHelper::CreateBitmap(TEXT("Test"), Width, Height, Bitmap.GetData());
}

 

Roughness 0.5.bmp

Roughness 1.bmp

Edited by wuyakuma

Share this post


Link to post
Share on other sites
Advertisement

Well, the diff is NOT continuous. As we sample L/N by texcoordinate, if you write down the function, you'll notice the function is piecewise. It's different at each plane/line. So the discontinuity of diff is natural. 

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!