#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. Я понимаю это, но для меня это не проблема , если я знаю, почему это происходит. Я не понимал, что сочетание отсутствия отбраковки лиц И отсутствия тестирования глубины привело к странности, я подумал, что это могло быть какое-то искажение. При этом мне в конечном итоге потребуется присоединить буфер глубины для отображения теней