I'm trying to implement shadow mapping: rendering the depth of all relevant objects to an offscreen texture, then using that texture in my main pass to figure out what fragments are obscured by a light source.
But before that, I'm just trying to get frame buffers of any sort rendering correctly. I've created an FBO containing a 2056x2056 4 channel image and then another which has no texture and just represents and binds back to the default framebuffer (the opentk window).
I'm using a simple shader which uses uvs as R,G color on the offscreen buffer, and a phong shader on the mainscreen texture, and I'm sending identical uniforms. The only difference is that I'm not clearing the offscreen buffer and there's also no depth attachment. But for some reason the offscreen framebuffer is coming super warped and small, like it only takes up about 1/4 the size of the texture, is warped, and there's also some weird noisy clipping going on.
Here's an image captured by NVIDIA Nsight Graphics: the offscreen buffer on the left, the default framebuffer on the right.
Any help/insights would be greatly appreciated. Thanks!
My FBO class.
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.Default;
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;
}
}
My Texture class (which an fbo contains)
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
}
}
}
where i set up my two fbos (well... one is more an interface)
ShadowBuffer = new FBO(FBOType.Shadow, 2560,2560, FramebufferAttachment.ColorAttachment0, PixelInternalFormat.Rgba, TextureUnit.Texture3);
var defaultBuffer = new FBO();
My Draw Loop:
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);
}
}
}
In the end the problems were:
I also was accidently not setting texture filtering properly, but that wasn't the source of any of the major graphical glitches which I was concerned with.
Thank-you the help!