Hey there everybody.
I’m new to OpenGL-Programming but I think, I’m slowly getting in. I’ve just messed around with Framebuffer Objects (That EXT-Extension) and finally got them working fine including depthbuffering. So, what I expected to get (and also needed to get - because that’s why I tried setting up FBOs) was a custom Framebuffer behaving just as the Backbuffer when drawing to it.
Unfortunately that’s not what I actually got.
I’ve experienced a slightly different behaviour in Blending when drawing to FBOs, making them completely unuseable for my purposes. It seems like RGB and Alpha-Values are treated seperately. I can (well… I would be glad if I could prevent it…) “overwrite” an 100%-Alpha by drawing a 50%-Alpha over it, making the texture again half-transparent when I’ve already completely filled out with a solid color.
Let’s say I’ve drawn a white rect on it, completely solid, 100% Alpha. Then I want to draw a black square in the middle of it with only 50% alpha. I expect to get a solid white square with a solid grey square in the middle of it. But the result is a solid white square with a 50%-Alpha grey square in the middle of it. And that’s just not the right behaviour for my purposes.
If it was just about drawing squares I could easily avoid it by doing my own calculations but when it comes to drawing textures on textures then I’m just unable to avoid anything. I need that FBO working as the backbuffer does.
What am I doing wrong? What can I do to get what I need?
If it helps, I’m adding my [C#-][Tao-]OpenGL-Code:
/// <summary>
/// Creates a RenderBuffer. A RenderBuffer is neccessary for Render2Texture-Operations.
/// The texture's content is being deleted as soon as a RenderBuffer is created.
/// </summary>
/// <param name="flags">
/// TexFlags-Bitmask. Use Bit-Operations on TexFlag-Enumerator or just specify this parameter as TexFlag.None.
/// </param>
public void CreateRenderBuffer(int flags)
{
if (this.framebufferID != -1 && this.renderbufferID != -1) return;
int oglWidth;
int oglHeight;
GetOGLTexSize(this.width, this.height, out oglWidth, out oglHeight);
this.curUVRatio.x = (float)this.width / (float)oglWidth;
this.curUVRatio.y = (float)this.height / (float)oglHeight;
// Create objects
Gl.glGenFramebuffersEXT(1, out this.framebufferID);
Gl.glGenRenderbuffersEXT(1, out this.renderbufferID);
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, this.framebufferID);
// Set-up texture
Bind(this);
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, oglWidth, oglHeight, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_INT, null);
this.texflags = flags;
if ((flags & (int)TexFlag.Smooth) != 0)
{
if ((flags & (int)TexFlag.MipMap) != 0)
{
Gl.glGenerateMipmapEXT(Gl.GL_TEXTURE_2D);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_NEAREST);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
}
else
{
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
}
}
else
{
if ((flags & (int)TexFlag.MipMap) != 0)
{
Gl.glGenerateMipmapEXT(Gl.GL_TEXTURE_2D);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST_MIPMAP_NEAREST);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
}
else
{
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
}
}
// Attach texture to FBO
Gl.glFramebufferTexture2DEXT(Gl.GL_FRAMEBUFFER_EXT, Gl.GL_COLOR_ATTACHMENT0_EXT, Gl.GL_TEXTURE_2D, this.textureID, 0);
// Initialize Renderbuffer (Depthbuffer)
Gl.glBindRenderbufferEXT(Gl.GL_RENDERBUFFER_EXT, this.renderbufferID);
Gl.glRenderbufferStorageEXT(Gl.GL_RENDERBUFFER_EXT, Gl.GL_DEPTH_COMPONENT24, oglWidth, oglHeight);
// Attach Renderbuffer to Framebuffer-Depthbuffer
Gl.glFramebufferRenderbufferEXT(Gl.GL_FRAMEBUFFER_EXT, Gl.GL_DEPTH_ATTACHMENT_EXT, Gl.GL_RENDERBUFFER_EXT, this.renderbufferID);
// Status check
int status = Gl.glCheckFramebufferStatusEXT(Gl.GL_FRAMEBUFFER_EXT);
if (status == Gl.GL_FRAMEBUFFER_COMPLETE_EXT) { /* void */ }
else if (status == Gl.GL_FRAMEBUFFER_UNSUPPORTED_EXT)
{
throw new ApplicationException("Can't create Framebuffer. Choose different format.");
}
else
{
throw new ApplicationException(String.Format("Can't create Framebuffer. Unknown error. Framebuffer-Status: {0}", status));
}
// Unbind all to regularily continue drawing operations
Gl.glBindRenderbufferEXT(Gl.GL_RENDERBUFFER_EXT, 0);
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, 0);
Bind(lastBound);
return;
}
/// <summary>
/// Deletes the texture's Renderbuffer.
/// </summary>
public void DeleteRenderBuffer()
{
if (CurrentFBBound != 0) UnbindRenderBuffer();
if (this.framebufferID != -1) { Gl.glDeleteFramebuffersEXT(1, ref this.framebufferID); }
if (this.renderbufferID != -1) { Gl.glDeleteRenderbuffersEXT(1, ref this.renderbufferID); }
this.framebufferID = -1;
this.renderbufferID = -1;
return;
}
/// <summary>
/// Binds the RenderBuffer if created. All following graphic commands are used on this texture.
/// </summary>
public void BindRenderBuffer()
{
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, this.framebufferID);
curFBBound = this.framebufferID;
Gl.glPushMatrix();
Gl.glTranslatef(0.0f, curWindowSize.y - this.height, 0.0f);
return;
}
/// <summary>
/// Unbinds the RenderBuffer.
/// </summary>
public void UnbindRenderBuffer()
{
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, 0);
curFBBound = 0;
Gl.glPopMatrix();
if ((this.texflags & (int)TexFlag.MipMap) != 0)
{
Bind(this);
Gl.glGenerateMipmapEXT(Gl.GL_TEXTURE_2D);
Bind(lastBound);
}
return;
}
Texture tex = Texture.Create((int)Math.Ceiling(Font.CurrentBound.TextWidth(text)), (int)Math.Ceiling(Font.CurrentBound.TextHeight(text)));
BlendMode oldBlend = DrawBlend;
tex.CreateRenderBuffer(flags);
tex.BindRenderBuffer();
Cls();
SetBlend(BlendMode.Alpha);
SetColor(255, 255, 255, 255);
DrawRect(0.0f, 0.0f, tex.Width, tex.Height);
SetColor(0, 0, 0, 128);
DrawRect(0.0f, 0.0f, tex.Width / 2.0f, tex.Height);
SetBlend(oldBlend);
tex.UnbindRenderBuffer();
tex.DeleteRenderBuffer();