OpenTK 屏幕外帧缓冲区扭曲并使用相同的材料不正确地渲染?
Posted
技术标签:
【中文标题】OpenTK 屏幕外帧缓冲区扭曲并使用相同的材料不正确地渲染?【英文标题】:OpenTK offscreen framebuffer distorted and rendering improperly with identical material? 【发布时间】:2020-10-13 08:26:42 【问题描述】:我正在尝试实现阴影映射:将所有相关对象的深度渲染到屏幕外纹理,然后在我的主通道中使用该纹理来确定哪些片段被光源遮挡。
但在此之前,我只是尝试正确渲染任何类型的帧缓冲区。我创建了一个包含 2056x2056 4 通道图像的 FBO,然后创建了另一个没有纹理的 FBO,它只表示并绑定回默认帧缓冲区(opentk 窗口)。
我正在使用一个简单的着色器,它在屏幕外缓冲区上使用 uvs 作为 R、G 颜色,在主屏幕纹理上使用一个 phong 着色器,并且我正在发送相同的制服。唯一的区别是我没有清除屏幕外缓冲区,也没有深度附件。但是由于某种原因,屏幕外的帧缓冲区变得超级扭曲和小,就像它只占用纹理大小的 1/4 一样,扭曲了,而且还会出现一些奇怪的嘈杂剪辑。
这是一张图片 captured by NVIDIA Nsight Graphics: the offscreen buffer on the left, the default framebuffer on the right.
任何帮助/见解将不胜感激非常。 谢谢!
我的 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.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;
我的纹理类(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);
【问题讨论】:
您是否将视口矩形调整为当前绑定的帧缓冲区的大小? (glViewport
)
顺便说一下,TextureParameterName.TextureMagFilter
被设置了两次,但是TextureParameterName.TextureMinFilter
却不见了。因此,您必须生成 mipmap。默认的缩小函数是NEAREST_MIPMAP_LINEAR。因此,如果没有生成 mipmap,则纹理是不完整的。无论如何,在ApplyTextureSettings
中生成一次 mipmap 就足够了。
我更改了视口大小以匹配帧缓冲区的大小,我错误地将视口大小与窗口大小相关联,没有意识到这是影响着色器程序的 openGL 问题。我还添加了 TextureMinFiltering here's the new image, now using normals > uvs to visualize。似乎还有一些奇怪的事情发生。为什么法线的颜色没有逐渐变化,颜色似乎有一些随机变化,就像我从纹理而不是 fragColor 的法线采样一样。可能与深度测试无关?
【参考方案1】:
最终的问题是:
我没有更改 GL.viewport 大小以匹配帧缓冲区。 我没有启用面部剔除,因此当我基于 uvs/法线(没有深度附件)进行着色时,有时球体多边形的内壳会穿过而不是外壳,导致颜色不连续屏幕外的纹理。我也无意中没有正确设置纹理过滤,但这不是我担心的任何主要图形故障的根源。
谢谢你的帮助!
【讨论】:
我认为问题不在于面部剔除。问题是,帧缓冲区没有深度缓冲区。因此,当您绘制到帧缓冲区时,深度测试不起作用。面剔除可以解决一个网格的问题,但如果多个网格相互覆盖,则无法解决问题。您必须向帧缓冲区添加深度缓冲区附件。 我明白了,但对我来说这不是一个问题,只要我知道它为什么会发生。我不明白没有面部剔除和没有深度测试的组合会导致奇怪,我认为这可能是一些失真。话虽如此,我最终需要为阴影贴图附加一个深度缓冲区以上是关于OpenTK 屏幕外帧缓冲区扭曲并使用相同的材料不正确地渲染?的主要内容,如果未能解决你的问题,请参考以下文章
我正在尝试在 opentk 中实现索引缓冲区对象,但不知道在绘图时如何实际使用它