Framebuffer-Object (FBO)

Framebuffer objects are a collection of attachments, like textures or render-buffers. You can attach multiple textures and setup those as render targets, which means you can write data to them while rendering.

Caution: Multiple render targets are only supported since OpenGLES v3+.

Framebuffer objects are very important and are used by the TRenderer component in render-passes. The following code shows how to create your own framebuffer object at runtime.

uses Gorilla.Context.Buffers;
// create the buffer and link to a context
FFBO := TFrameBufferObject.Create(AContext);
// in case you need a depth and stencil buffer
FFBO.CreateDepthBuffer(Width, Height);

When working with framebuffer objects, you have to bind them before and to unbind afterwards.

    FFBO.Clear(TRenderTarget.Color_0, TAlphaColorF.Create(0, 0, 0, 0), 1, 0);

Multiple Rendering-Targets

As said above, we can use multiple render targets since OpenGLES v3.

Multiple render targets are useful to store values in the shader to different output textures. So you possibly could store the depth, diffuse, specular, normals or alpha values in separated textures. Afterwards you could combine those together in some kind of way. For example this is how deferred rendering works.

In Gorilla3D we create those targets as TGorillaTextureBitmap instances. Afterwards we attach those to the framebuffer object (FBO).

Notice: Gorilla3D provides an extended TClearTarget enumeration type called TRenderTarget, where the first 3 values are compatible with the Delphi type (and OpenGLES v2).

TRenderTarget = (Color_0, Depth, Stencil
    Color_1, Color_2, Color_3, Color_4, Color_5, Color_6, Color_7,
    Color_8, Color_9, Color_10, Color_11, Color_12, Color_13, Color_14,

Creating multiple targets

To create a render target, we have to setup a TGorillaTextureBitmap instance and attach it to the framebuffer object afterwards:

FMyRenderTargetTexture := TGorillaTextureBitmap.Create(LSize.X, LSize.Y);
  with FMyRenderTargetTexture do
    DataType := TGorillaTextureDataType.dtFloat;
    Components := TPixelFormatEx.RGBA32F;
    Format := TPixelFormatEx.RGBA;
    MinFilter := TTextureFilter.Linear;
    MagFilter := TTextureFilter.Linear;
    WrapS := TGorillaTextureWrap.ClampToBorder;
    WrapT := TGorillaTextureWrap.ClampToBorder;

Attaching multiple targets

To attach a texture to the framebuffer object we call the AttachTexture() method of the FBO and supply the intended rendertarget (one of the values above).

Caution: Not all GPU's support all render targets. Some GPU's only support up to 8 targets or less.

The following snippet shows how to attach the previously created texture to our framebuffer object.

procedure TMyRenderPass.DoSetupTexturesByViewport(const AContext : TContext3D;
  const AWidth, AHeight : Integer);
  FFBO.AttachTexture(AContext, FMyRenderTargetTexture.GorillaTexture, TRenderTarget.Color_0);

Using multiple targets

To use the framebuffer object with its attached render-targets, we need to bind, clear and prepare it on each rendering step. If you forget to execute Prepare() only the default target Color_0 will be available for computation. Only color attachments need to be prepared, not stencil or depth buffers.

Notice: when working with render-passes and the embedded TRenderer, this is already done for you. But in case you established your own FBO, you enable it the following way.

procedure TMyRenderPass.DoOnBeginRenderPassSetup(
  const ACount : Integer; const APass : TRenderPass);
  // bind your framebuffer to use in shaders
  // clear the specific render target buffer
  FFBO.Clear([TRenderTarget.Color_0, TRenderTarget.Depth], 
    TAlphaColorF.Create(0, 0, 0, 1), 1, 0);
  // activate the specific render target buffer for shader
procedure TMyRenderPass.DoOnEndRenderPassSetup(
  const ACount : Integer; const APass : TRenderPass);

In Shader

The usage in your material shader depends on your target setup and the activated targets. For example: Preparing 3 render targets will be used like this:

FFBO.Prepare([TRenderTarget.Color0, TRenderTarget.Color2, TRenderTarget.Color3]);

In Fragment-Shader you'll see, that layout location index do not correspond with the render target index, but with the array index of supplied render targets.

layout(location = 0) out vec4 outColor_0;
layout(location = 1) out vec4 outColor_2;
layout(location = 2) out vec4 outColor_3;
// output one with red color
outColor_0 = vec4(1.0, 0.0, 0.0, 1.0);
// output two with green color
outColor_2 = vec4(0.0, 1.0, 0.0, 1.0);
// output three with blue color
outColor_3 = vec4(0.0, 0.0, 1.0, 1.0);