OpenGL 天空盒纹理环绕伪像

OpenGL skybox texture wrapping artifacts

我正在尝试使用 OpenTK 和计算着色器制作路径追踪器,但我一直在努力处理在我的天空盒边缘重复的纹理。我遵循 the tutorial from learnopengl 并将其调整为与我的计算着色器一起使用,但我无法摆脱这些工件。

这是加载天空盒纹理的片段:

private TextureHandle _skyboxTexture;
...
protected override void OnLoad() {
    base.OnLoad();
    GL.Enable(EnableCap.TextureCubeMapSeamless);
    ...
    _skyboxTexture = GL.CreateTexture(TextureTarget.TextureCubeMap);
    GL.BindTexture(TextureTarget.TextureCubeMap, _skyboxTexture);
    foreach (var file in Directory.GetFiles(@"Images\Skybox")) {
        using (var image = SixLabors.ImageSharp.Image.Load(file)) {
            image.Mutate(img => img.Rotate(180)); // without this the textures dont line up
            using (var ms = new MemoryStream()) {
                image.Save(ms, new BmpEncoder());
                GL.TexImage2D(Texture.CubeMapTextureTargetFromString(file), 0, (int)InternalFormat.Rgb, 2048, 2048, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, ms.ToArray());
            }
        }
    }
    GL.TexParameteri(TextureTarget.TextureCubeMap, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
    GL.TexParameteri(TextureTarget.TextureCubeMap, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
    GL.TexParameteri(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
    GL.TexParameteri(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
    GL.TexParameteri(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
    ...
}

这些是 RenderDoc 的几个截图,你可以清楚地看到天空盒纹理中的伪影。在另一张图片中,您可以看到夹紧和无缝设置已正确加载。

这张图片在 Whosebug 上是模糊的,但当你点击它时,它会更好。

我认为我的计算着色器中的采样逻辑不是问题,因为在使用 RenderDoc 时我还可以在纹理中看到伪影。我还尝试将图像从 MemoryStream 保存到 .bmp 以检查图像加载过程中是否出现问题,但导出的图像看起来很好。这也不是天空盒纹理的问题,它发生在我尝试的所有纹理上。

多亏了 derhass,我才开始工作。问题是位图 header 导致纹理未对齐。我正在使用 ImageSharp 进行图像处理,因此我创建了一个新的 class 来实现 IImageEncoder 并将原始 rgb 值放入流中。我不需要 alpha 通道,所以我省略了它,但我认为它可以很容易地添加。

public class RawBytesEncoder : IImageEncoder {
    public void Encode<TPixel>(Image<TPixel> image, Stream stream) where TPixel : unmanaged, IPixel<TPixel> {
        for (var y = 0; y < image.Height; y++)
        for (var x = 0; x < image.Width; x++) {
            var target = new Rgba32();
            image[x, y].ToRgba32(ref target);
            stream.WriteByte(target.R);
            stream.WriteByte(target.G);
            stream.WriteByte(target.B);
        }
    }

    public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel<TPixel> {
        throw new NotImplementedException();
    }
}

我还必须更改代码其他部分的一些内容。图像不再需要旋转180度,现在需要翻转,glTexImage中的PixelFormat现在又是Rgb

foreach (var file in Directory.GetFiles(@"Images\Skybox"))
    using (var image = Image.Load(file)) {
        image.Mutate(img => img.Flip(FlipMode.Horizontal));
        using (var ms = new MemoryStream()) {
            image.Save(ms, new RawBytesEncoder());
            GL.TexImage2D(Texture.CubeMapTextureTargetFromString(file), 0, (int)InternalFormat.Rgb, 2048, 2048, 0, PixelFormat.Rgb, PixelType.UnsignedByte, ms.ToArray());
        }
    }
}