Закадровый буфер кадров OpenTK искажен и неправильно отображается с идентичным материалом?

#opengl #framebuffer #opentk #fbo #nsight

#opengl #буфер кадров #opentk #fbo #nsight

Вопрос:

Я пытаюсь реализовать отображение теней: визуализировать глубину всех соответствующих объектов в закадровой текстуре, а затем использовать эту текстуру в моем основном проходе, чтобы выяснить, какие фрагменты скрыты источником света.

Но до этого я просто пытаюсь получить буферы кадров любого типа, которые отображаются правильно. Я создал FBO, содержащий 4-канальное изображение размером 2056×2056, а затем другое, которое не имеет текстуры и просто представляет и привязывается к буферу кадров по умолчанию (окно opentk).

Я использую простой шейдер, который использует uvs в качестве R, цвет G в закадровом буфере и фонг-шейдер в текстуре основного экрана, и я отправляю идентичные формы. Единственное отличие заключается в том, что я не очищаю закадровый буфер, а также нет привязки глубины. Но по какой-то причине закадровый фреймбуфер получается очень искаженным и маленьким, как будто он занимает всего около 1/4 размера текстуры, деформирован, а также происходит какое-то странное шумное обрезание.

Вот изображение, полученное с помощью NVIDIA Nsight Graphics: закадровый буфер слева, буфер кадров по умолчанию справа.

Любая помощь / идеи будут с благодарностью. Спасибо!

Мой класс FBO.

 namespace MaterialRelated
{
    public class FBO : ITypeID
    {
        public readonly int Handle = 0;
        public readonly CreateFBOs.FBOType Type;
        public Texture Texture { get; private set; }

        public FBO(CreateFBOs.FBOType type, int width, int height, FramebufferAttachment attachment, PixelInternalFormat internalFormat, TextureUnit textureUnit)
        {
            Handle = GL.GenFramebuffer();
            Type = type;
            Use();
            AssignTexture(Texture.Empty(width, height, internalFormat, textureUnit), attachment);
            var fboStatus = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
            if (fboStatus != FramebufferErrorCode.FramebufferComplete)
                throw new Exception($"Frame Buffer Exception! {fboStatus}");
        }

        /// <summary>
        /// Creates default fbo
        /// </summary>
        public FBO()
        {
            Type = CreateFBOs.FBOType.Defau<
            Handle = 0;
            Texture = null;
        }

        public void Use()
        {
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
        }

        public void SetDrawingStates()
        {
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
        }

        public static void UseDefaultBuffer()
        {
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
        }
    
        
        public void AssignTexture(Texture texture, FramebufferAttachment attachment)
        {
            Texture = texture;
             GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, attachment, TextureTarget.Texture2D, texture.Handle, 0); //todo fix assign tex
        }

        public int GetTypeID() => (int) Type;
    }
}
  

Мой класс текстуры (который содержит fbo)

 namespace MaterialRelated
{
    public class Texture
    {

        public TextureUnit TextureUnit { get; private set; }
        public PixelInternalFormat InternalPixelFormat { get; private set; }
        
        public readonly int Handle;
        
        public byte[] Colors; //todo make array for perf
        
        public Image<Rgba32> LaborsImage;
        public int Width { get; private set; }
        public int Height { get; private set; }
        
        public Texture()
        {
            Handle = GL.GenTexture();
        }
  
        /// <summary>
        /// loads file using sixlabors library... turn cookOnLoad off if you're going to manipulate image on cpu before uploading to openGL
        /// </summary>
        public static Texture FromFile(string fileName, TextureUnit textureUnit) //turn cookOnLoad off if you're going to manipulate image on cpu before uploading to openGL
        {
            var texture = new Texture();
            texture.InternalPixelFormat = PixelInternalFormat.Rgba;
            texture.TextureUnit = textureUnit;
            texture.Use();
            texture.ApplyTextureSettings();
            texture.LoadImage(fileName);
            texture.CookSixLaborsImageToByteArray();

            texture.UploadToShader();

            return texture;
        }
        
        public static Texture Empty(int width, int height, PixelInternalFormat internalFormat, TextureUnit textureUnit)
        {
            var texture = new Texture();

            texture.InternalPixelFormat = internalFormat;
            texture.TextureUnit = textureUnit;
            texture.Use();
            texture.ApplyTextureSettings();
            texture.Width = width;
            texture.Height = height;
            texture.CreateEmptyByteArray();

            texture.UploadToShader();
            
            return texture;
        }
        

        public void LoadImage(string fileName)
        {
            string path = SerializationManager.TexturePath   fileName;
            LaborsImage = Image.Load<Rgba32>(path);
            LaborsImage.Mutate(x => x.Flip(FlipMode.Vertical)); //ImageSharp loads from the top-left pixel, whereas OpenGL loads from the bottom-left, causing the texture to be flipped vertically.
            Width  = LaborsImage.Width;
            Height = LaborsImage.Height;
        }

        public void CreateEmptyByteArray()
        {
            int area = Width * Height;
            Colors = new byte[area * 4];
        }
        
        public void CookSixLaborsImageToByteArray()
        {
            if (!LaborsImage.TryGetSinglePixelSpan(out var tempPixels))
                throw new Exception("Image Loading Error: Is Texture Corrupt?");

            int area = Width * Height;
            if (Colors == null || Colors.Length != area * 4) //init colors to proper length if not already
                Colors = new byte[area * 4];

            for (int i = 0; i < tempPixels.Length; i  )
            {
                int indexStart = i * 4;
                Colors[indexStart   0] = tempPixels[i].R;
                Colors[indexStart   1] = tempPixels[i].G;
                Colors[indexStart   2] = tempPixels[i].B;
                Colors[indexStart   3] = tempPixels[i].A;
            }
        }

        public void Use()
        {
            GL.ActiveTexture(TextureUnit);
            GL.BindTexture(TextureTarget.Texture2D, Handle);
        }
        
        public void UploadToShader()
        {
            Use();
            GL.TexImage2D(TextureTarget.Texture2D, 0, InternalPixelFormat, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, Colors);
            GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
        }

        private void ApplyTextureSettings() //todo make it so doesn't always have to be rgba image
        {
            Use();
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) TextureWrapMode.Repeat); //tex wrap mode
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); //scaling up, tex interp
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear); //scaling down
        }


    }
}
  

где я настроил свои два fbo (ну … один — это скорее интерфейс)

 ShadowBuffer = new FBO(FBOType.Shadow, 2560,2560, FramebufferAttachment.ColorAttachment0, PixelInternalFormat.Rgba, TextureUnit.Texture3);
            var defaultBuffer = new FBO();
  

Мой цикл рисования:

 public void RenderFrame()
        {
            for (int fboIndex = 0; fboIndex < BatchHierachies.Count; fboIndex  )
            {
                FBOBatch fboBatch = BatchHierachies[fboIndex];
                fboBatch.FBO.SetDrawingStates();
                
                for (int materialIndex = 0; materialIndex < fboBatch.MaterialBatches.Count; materialIndex  )
                {
                    MaterialBatch materialBatch = fboBatch.MaterialBatches[materialIndex];
                    materialBatch.Material.SetDrawingStates();

                    for (int entityIndex = 0; entityIndex < materialBatch.Entities.Count; entityIndex  )
                    {
                        Entity entity = materialBatch.Entities[entityIndex];
                        
              
                         entity.SendUniformsPerObject(materialBatch.Material);

                        GL.DrawArrays(PrimitiveType.Triangles, 0, materialBatch.Material.VAO.VerticesCount);
                    }
                }

                if (fboBatch.FBO.Type != CreateFBOs.FBOType.Default)
                {
                    GL.BindTexture(TextureTarget.Texture2D, fboBatch.FBO.Texture.Handle);
                    GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
                }
            }
            
        }
  

Комментарии:

1. Адаптируете ли вы прямоугольник области просмотра к размеру текущего связанного фреймбуфера? ( glViewport )

2. Кстати, TextureParameterName.TextureMagFilter устанавливается дважды, но TextureParameterName.TextureMinFilter отсутствует. Из-за этого вам приходится создавать mip-карты. Функция минимизации по умолчанию NEAREST_MIPMAP_LINEAR . Следовательно, текстура является неполной, если не генерируются mipmaps. В любом случае достаточно сгенерировать mipmaps один раз ApplyTextureSettings .

3. Я изменил размер области просмотра, чтобы соответствовать размеру буфера кадров, я ошибочно связывал размер области просмотра с размером окна, не понимая, что это была проблема OpenGL, которая повлияла на шейдерные программы. Я также добавил TextureMinFiltering вот новое изображение, теперь для визуализации используется normals> uvs . Кажется, все еще происходит какая-то странность. Почему нормали постепенно не меняются по цвету, кажется, есть некоторые случайные изменения цвета, как будто я выбираю текстуру вместо нормалей для FragColor. Может ли это иметь какое-то отношение к отсутствию тестирования глубины?

Ответ №1:

В итоге проблемы были:

  • Я не изменил размер экрана GL.viewport в соответствии с буфером кадров.
  • Я не включил отбраковку лицевых поверхностей, поэтому, когда я выполнял затенение на основе uvs / нормалей (без привязки глубины), иногда вместо внешней оболочки просвечивала внутренняя оболочка полигонов сферы, что приводило к неоднородности цвета текстуры закадрового экрана.

Я также случайно неправильно настроил фильтрацию текстур, но это не было источником каких-либо серьезных графических сбоев, которые меня беспокоили.

Спасибо за помощь!

Комментарии:

1. Я не думаю, что проблема заключается в отбраковке лиц. Проблема в том, что в буфере кадров нет буфера глубины. Следовательно, проверка глубины не работает, когда вы рисуете в буфере кадров. Отбраковка лиц может решить проблему для одной сетки, но это не решит проблему, если несколько сеток перекрывают друг друга. Вы должны добавить вложение буфера глубины в буфер кадров.

2. Я понимаю это, но для меня это не проблема , если я знаю, почему это происходит. Я не понимал, что сочетание отсутствия отбраковки лиц И отсутствия тестирования глубины привело к странности, я подумал, что это могло быть какое-то искажение. При этом мне в конечном итоге потребуется присоединить буфер глубины для отображения теней