framebuffer
FreeBodyEngine.graphics.gl33.framebuffer
#
GL_ATTACHMENT_FORMAT = {AttachmentFormat.R8: GL_R8, AttachmentFormat.RGBA8: GL_RGBA8, AttachmentFormat.RGBA16F: GL_RGBA16F, AttachmentFormat.RGBA32F: GL_RGBA32F, AttachmentFormat.RGB10_A2: GL_RGB10_A2, AttachmentFormat.R32F: GL_R32F, AttachmentFormat.RG32F: GL_RG32F, AttachmentFormat.DEPTH24: GL_DEPTH_COMPONENT24, AttachmentFormat.DEPTH32F: GL_DEPTH_COMPONENT32F, AttachmentFormat.STENCIL8: GL_STENCIL_INDEX8, AttachmentFormat.DEPTH24_STENCIL8: GL_DEPTH24_STENCIL8}
module-attribute
#
GL_ATTACHMENT_TYPE = {AttachmentFormat.R8: (GL_RED, GL_UNSIGNED_BYTE), AttachmentFormat.RGBA8: (GL_RGBA, GL_UNSIGNED_BYTE), AttachmentFormat.RGBA16F: (GL_RGBA, GL_FLOAT), AttachmentFormat.RGBA32F: (GL_RGBA, GL_FLOAT), AttachmentFormat.RGB10_A2: (GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV), AttachmentFormat.R32F: (GL_RED, GL_FLOAT), AttachmentFormat.RG32F: (GL_RG, GL_FLOAT), AttachmentFormat.DEPTH24: (GL_DEPTH_COMPONENT, GL_UNSIGNED_INT), AttachmentFormat.DEPTH32F: (GL_DEPTH_COMPONENT, GL_FLOAT), AttachmentFormat.STENCIL8: (GL_STENCIL_INDEX, GL_UNSIGNED_BYTE), AttachmentFormat.DEPTH24_STENCIL8: (GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8)}
module-attribute
#
GL_CHANNEL_COUNT = {GL_RED: 1, GL_RG: 2, GL_RGB: 3, GL_RGBA: 4}
module-attribute
#
GLFramebuffer(width, height, attachments, transparent=False)
#
Bases: Framebuffer
The GL 3.3 implementation of Framebuffer: a real glGenFramebuffers
object with one GL_TEXTURE_2D per color attachment (so it can also be
sampled from later, e.g. a G-buffer channel) and a single shared
renderbuffer for whichever depth/stencil/depth-stencil attachment was
requested. self.attachments[name] (inherited from the base class) is
repurposed here to hold each color attachment's actual
GL_COLOR_ATTACHMENT0 + n enum rather than the (AttachmentType,
AttachmentFormat) pair the constructor received - that original pair is
kept separately in self._attachments since resize() needs it again to
recreate storage at the new size.
Creates the FBO and, for every requested attachment, the backing
GL object: a mipmapless linear-filtered GL_TEXTURE_2D for each COLOR
attachment (bound to consecutive GL_COLOR_ATTACHMENTn slots), or one
shared renderbuffer for a DEPTH/STENCIL/DEPTH_STENCIL attachment.
Color attachments are also collected into draw_buffers and wired up
via glDrawBuffers so a shader with multiple @output fields
actually renders to all of them; with no color attachments at all,
glDrawBuffer(GL_NONE)/glReadBuffer(GL_NONE) are set instead
(a depth-only FBO, e.g. a shadow map). Raises RuntimeError if the
finished FBO fails glCheckFramebufferStatus. transparent enables
standard alpha blending for subsequent draws into this FBO.
depth_renderbuffer = glGenRenderbuffers(1)
instance-attribute
#
depth_texture_name = name
instance-attribute
#
fbo = glGenFramebuffers(1)
instance-attribute
#
num_color_attachments = color_attachment_index
instance-attribute
#
textures = {}
instance-attribute
#
bind()
#
Binds this FBO as the current GL_FRAMEBUFFER and sets the GL viewport to its full size, so subsequent draws render into it at the correct resolution instead of whatever viewport the previously-bound target left set.
clear_color_attachment(name, value=(0.0, 0.0, 0.0, 0.0))
#
Clears the named color attachment to value via
glClearBufferfv(GL_COLOR, draw_buffer_index, ...), targeting only
that attachment's own draw-buffer index rather than every bound draw
buffer at once (the effect a plain glClear(GL_COLOR_BUFFER_BIT)
would have) - see the abstract method's docstring for why that
distinction matters for a multi-attachment G-buffer.
draw(attachment, size=None)
#
Draw a named attachment to the screen.
get_attachment_texture(attachment_name)
#
Returns the raw GL texture id backing the named color attachment
(there's nothing to return for a depth/stencil attachment - those are
renderbuffers, not textures - so only entries in self.textures
apply).
read(attachment_name)
#
Reads back the named color attachment's pixels via
glReadPixels, always as GL_FLOAT regardless of the attachment's own
storage type, and reshapes the raw buffer into a (height, width,
channels) float32 array (channels derived from the attachment's GL
format via GL_CHANNEL_COUNT). This is a synchronous GPU->CPU stall -
see the abstract method's docstring for when that's acceptable.
resize(size)
#
Recreates every attachment's storage at the new size, mirroring
init's attachment loop: each color texture is deleted and
regenerated at the new dimensions (a GL texture's storage can't be
resized in place), and the shared depth/stencil renderbuffer is
likewise deleted and regenerated if one exists. Also re-runs the
draw-buffers wiring and completeness check init does, and updates
the GL viewport to match. Raises RuntimeError if the resized FBO is
incomplete.
set_draw_buffers(names)
#
See Framebuffer.set_draw_buffers. Assumes this FBO is already bound.
Builds the same full-width, position-equals-attachment-index array
WebGL2Framebuffer.set_draw_buffers() is forced to use (GL_NONE at
every color attachment not in names) rather than the more
compact [self.attachments[n] for n in names] this used to be -
desktop GL doesn't require that shape (it can remap an arbitrary
subset onto sequential fragment-output locations starting at 0),
but PBRPipeline's shaders (see graphics/pbr/shaders.py's
LIGHTING_COMPOSITE_FRAG/default_forward.fbfrag) declare their real
@output field at whatever location its physical attachment index
is - padded with unused leading fields to get there - specifically
so the same FBUSL source compiles correctly on WebGL2, which has
no remapping at all (see WebGL2Framebuffer.set_draw_buffers()'s
own docstring). Matching that convention here means one shared
assumption ("output location N always means physical attachment
N") holds on both backends instead of desktop silently tolerating
a mismatch WebGL2 can't.
unbind()
#
Rebinds the default framebuffer (0), i.e. the window's own backbuffer.