OpenGL: Add multi-pass transparency

This commit is contained in:
LRFLEW 2017-10-21 21:04:24 -05:00 committed by Michał Janiszewski
parent d3d41ea724
commit aac1c59714
16 changed files with 347 additions and 60 deletions

View File

@ -17,7 +17,7 @@ void main()
uint transparent = texture(uTransparentTex, fTextureCoordinate).r;
float transparentDepth = texture(uTransparentDepth, fTextureCoordinate).r;
if (transparentDepth > opaqueDepth)
if (opaqueDepth <= transparentDepth)
{
transparent = 0u;
}

View File

@ -18,9 +18,8 @@ void main()
{
vec2 pos = clamp(vVertMat * vBounds, vClip.xy, vClip.zw);
// Transform screen coordinates to viewport
pos.x = (pos.x * (2.0 / uScreenSize.x)) - 1.0;
pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0;
// Transform screen coordinates to viewport coordinates
pos = (pos * (2.0 / uScreenSize)) - 1.0;
pos.y *= -1;
float depth = 1.0 - (vDepth + 1) * DEPTH_INCREMENT;

View File

@ -8,6 +8,9 @@ const int FLAG_CROSS_HATCH = (1 << 4);
uniform usampler2DArray uTexture;
uniform usampler2DRect uPaletteTex;
uniform sampler2D uPeelingTex;
uniform bool uPeeling;
flat in int fFlags;
flat in uint fColour;
in vec3 fTexColour;
@ -15,11 +18,21 @@ in vec3 fTexMask;
flat in vec3 fPalettes;
in vec2 fPosition;
in vec3 fPeelPos;
out uint oColour;
void main()
{
if (uPeeling)
{
float peel = texture(uPeelingTex, fPeelPos.xy).r;
if (peel == 0.0 || fPeelPos.z >= peel)
{
discard;
}
}
uint texel;
if ((fFlags & FLAG_NO_TEXTURE) == 0)
{

View File

@ -20,6 +20,7 @@ in mat4x2 vVertMat;
in vec2 vVertVec;
out vec2 fPosition;
out vec3 fPeelPos;
flat out int fFlags;
flat out uint fColour;
out vec3 fTexColour;
@ -36,15 +37,17 @@ void main()
fPosition = pos;
// Transform screen coordinates to viewport
pos.x = (pos.x * (2.0 / uScreenSize.x)) - 1.0;
pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0;
pos.y *= -1;
// Transform screen coordinates to texture coordinates
float depth = 1.0 - (vDepth + 1) * DEPTH_INCREMENT;
pos = pos / uScreenSize;
pos.y = pos.y * -1.0 + 1.0;
fPeelPos = vec3(pos, depth * 0.5 + 0.5);
fFlags = vFlags;
fColour = vColour;
fPalettes = vec3(vPalettes);
// Transform texture coordinates to viewport coordinates
pos = pos * 2.0 - 1.0;
gl_Position = vec4(pos, depth, 1.0);
}

View File

@ -75,7 +75,9 @@ void ApplyTransparencyShader::GetLocations()
vTextureCoordinate = GetAttributeLocation("vTextureCoordinate");
}
void ApplyTransparencyShader::SetTextures(GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex)
void ApplyTransparencyShader::SetTextures(GLuint opaqueTex, GLuint opaqueDepth,
GLuint transparentTex, GLuint transparentDepth,
GLuint paletteTex)
{
OpenGLAPI::SetTexture(0, GL_TEXTURE_2D, opaqueTex);
OpenGLAPI::SetTexture(1, GL_TEXTURE_2D, opaqueDepth);

View File

@ -38,7 +38,9 @@ public:
ApplyTransparencyShader();
~ApplyTransparencyShader() override;
void SetTextures(GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex);
void SetTextures(GLuint opaqueTex, GLuint opaqueDepth,
GLuint transparentTex, GLuint transparentDepth,
GLuint paletteTex);
void Draw();
private:

View File

@ -50,6 +50,14 @@ public:
}
return _instances[_numInstances++];
}
T& insert(const T &value)
{
if (_numInstances + 1 > _instances.size())
{
_instances.resize((_numInstances + 1) << 1);
}
return _instances[_numInstances++] = value;
}
size_t size() const
{
return _numInstances;
@ -62,6 +70,14 @@ public:
{
return _instances.at(idx);
}
typename std::vector<T>::iterator begin()
{
return _instances.begin();
}
typename std::vector<T>::iterator end()
{
return _instances.begin() + _numInstances;
}
};
struct DrawLineCommand

View File

@ -94,6 +94,9 @@ DrawRectShader::DrawRectShader() : OpenGLShaderProgram("drawrect")
Use();
glUniform1i(uTexture, 0);
glUniform1i(uPaletteTex, 1);
glUniform1i(uPeelingTex, 2);
glUniform1i(uPeeling, 0);
}
DrawRectShader::~DrawRectShader()
@ -109,6 +112,9 @@ void DrawRectShader::GetLocations()
uTexture = GetUniformLocation("uTexture");
uPaletteTex = GetUniformLocation("uPaletteTex");
uPeelingTex = GetUniformLocation("uPeelingTex");
uPeeling = GetUniformLocation("uPeeling");
vClip = GetAttributeLocation("vClip");
vTexColourAtlas = GetAttributeLocation("vTexColourAtlas");
vTexColourBounds = GetAttributeLocation("vTexColourBounds");
@ -129,14 +135,31 @@ void DrawRectShader::SetScreenSize(sint32 width, sint32 height)
glUniform2i(uScreenSize, width, height);
}
void DrawRectShader::DrawInstances(const RectCommandBatch& instances)
void DrawRectShader::EnablePeeling(GLuint peelingTex)
{
OpenGLAPI::SetTexture(2, GL_TEXTURE_2D, peelingTex);
glUniform1i(uPeeling, 1);
}
void DrawRectShader::DisablePeeling()
{
glUniform1i(uPeeling, 0);
}
void DrawRectShader::SetInstances(const RectCommandBatch &instances)
{
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vboInstances);
glBufferData(GL_ARRAY_BUFFER, sizeof(DrawRectCommand) * instances.size(), instances.data(), GL_STREAM_DRAW);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)instances.size());
_instanceCount = (GLsizei)instances.size();
}
void DrawRectShader::DrawInstances()
{
glBindVertexArray(_vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, _instanceCount);
}
#endif /* DISABLE_OPENGL */

View File

@ -29,6 +29,9 @@ private:
GLuint uTexture;
GLuint uPaletteTex;
GLuint uPeelingTex;
GLuint uPeeling;
GLuint vVertMat;
GLuint vVertVec;
@ -47,12 +50,18 @@ private:
GLuint _vboInstances;
GLuint _vao;
GLsizei _instanceCount;
public:
DrawRectShader();
~DrawRectShader() override;
void SetScreenSize(sint32 width, sint32 height);
void DrawInstances(const RectCommandBatch& instances);
void EnablePeeling(GLuint peelingTex);
void DisablePeeling();
void SetInstances(const RectCommandBatch& instances);
void DrawInstances();
private:
void GetLocations();

View File

@ -29,6 +29,7 @@
#define glClearColor __static__glClearColor
#define glCullFace __static__glCullFace
#define glDeleteTextures __static__glDeleteTextures
#define glDepthFunc __static__glDepthFunc
#define glDisable __static__glDisable
#define glDrawArrays __static__glDrawArrays
#define glEnable __static__glEnable
@ -60,6 +61,7 @@
#undef glClearColor
#undef glCullFace
#undef glDeleteTextures
#undef glDepthFunc
#undef glDisable
#undef glDrawArrays
#undef glEnable
@ -84,6 +86,7 @@ typedef void (APIENTRYP PFNGLCLEARPROC )(GLbitfield mask);
typedef void (APIENTRYP PFNGLCLEARCOLORPROC )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
typedef void (APIENTRYP PFNGLCULLFACEPROC )(GLenum mode);
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);
typedef void (APIENTRYP PFNGLDEPTHFUNCPROC )(GLenum func);
typedef void (APIENTRYP PFNGLDISABLEPROC )(GLenum cap);
typedef void (APIENTRYP PFNGLDRAWARRAYSPROC )(GLenum mode, GLint first, GLsizei count);
typedef void (APIENTRYP PFNGLENABLEPROC )(GLenum cap);

View File

@ -27,6 +27,7 @@ OPENGL_PROC(PFNGLCLEARPROC, glClear)
OPENGL_PROC(PFNGLCLEARCOLORPROC, glClearColor)
OPENGL_PROC(PFNGLCULLFACEPROC, glCullFace)
OPENGL_PROC(PFNGLDELETETEXTURESPROC, glDeleteTextures)
OPENGL_PROC(PFNGLDEPTHFUNCPROC, glDepthFunc)
OPENGL_PROC(PFNGLDISABLEPROC, glDisable)
OPENGL_PROC(PFNGLDRAWARRAYSPROC, glDrawArrays)
OPENGL_PROC(PFNGLENABLEPROC, glEnable)

View File

@ -16,6 +16,8 @@
#ifndef DISABLE_OPENGL
#include <algorithm>
#include <map>
#include <unordered_map>
#include <vector>
#include <SDL.h>
@ -97,7 +99,6 @@ public:
IDrawingEngine * GetEngine() override;
TextureCache * GetTextureCache() const { return _textureCache; }
SwapFramebuffer * GetSwapFramebuffer() const { return _swapFramebuffer; }
const OpenGLFramebuffer & GetFinalFramebuffer() const { return _swapFramebuffer->GetFinalFramebuffer(); }
void Initialise();
@ -116,10 +117,12 @@ public:
void FlushCommandBuffers();
void FlushLines(LineCommandBatch &batch);
void FlushRectangles(RectCommandBatch &batch);
void FlushLines();
void FlushRectangles();
void HandleTransparency();
void SetDPI(rct_drawpixelinfo * dpi);
sint32 MaxTransparencyDepth();
};
class OpenGLDrawingEngine : public IDrawingEngine
@ -810,35 +813,75 @@ void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * p
void OpenGLDrawingContext::FlushCommandBuffers()
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
_swapFramebuffer->BindOpaque();
FlushLines(_commandBuffers.lines);
FlushRectangles(_commandBuffers.rects);
_swapFramebuffer->BindTransparent();
FlushRectangles(_commandBuffers.transparent);
_swapFramebuffer->ApplyTransparency(*_applyTransparencyShader, _textureCache->GetPaletteTexture());
_drawRectShader->Use();
_drawRectShader->DisablePeeling();
FlushLines();
FlushRectangles();
HandleTransparency();
}
void OpenGLDrawingContext::FlushLines(LineCommandBatch &batch)
void OpenGLDrawingContext::FlushLines()
{
if (batch.size() == 0) return;
if (_commandBuffers.lines.size() == 0) return;
_drawLineShader->Use();
_drawLineShader->DrawInstances(batch);
_drawLineShader->DrawInstances(_commandBuffers.lines);
batch.clear();
_commandBuffers.lines.clear();
}
void OpenGLDrawingContext::FlushRectangles(RectCommandBatch &batch)
void OpenGLDrawingContext::FlushRectangles()
{
if (batch.size() == 0) return;
if (_commandBuffers.rects.size() == 0) return;
OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetAtlasesTexture());
OpenGLAPI::SetTexture(1, GL_TEXTURE_RECTANGLE, _textureCache->GetPaletteTexture());
_drawRectShader->Use();
_drawRectShader->DrawInstances(batch);
_drawRectShader->SetInstances(_commandBuffers.rects);
_drawRectShader->DrawInstances();
batch.clear();
_commandBuffers.rects.clear();
}
void OpenGLDrawingContext::HandleTransparency()
{
if (_commandBuffers.transparent.empty())
{
return;
}
_drawRectShader->Use();
_drawRectShader->SetInstances(_commandBuffers.transparent);
sint32 max_depth = MaxTransparencyDepth();
for (sint32 i=0; i < max_depth; ++i)
{
_swapFramebuffer->BindTransparent();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
_drawRectShader->Use();
if (i > 0)
{
_drawRectShader->EnablePeeling(_swapFramebuffer->GetBackDepthTexture());
}
OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetAtlasesTexture());
OpenGLAPI::SetTexture(1, GL_TEXTURE_RECTANGLE, _textureCache->GetPaletteTexture());
_drawRectShader->Use();
_drawRectShader->DrawInstances();
_swapFramebuffer->ApplyTransparency(*_applyTransparencyShader, _textureCache->GetPaletteTexture());
}
_commandBuffers.transparent.clear();
}
void OpenGLDrawingContext::SetDPI(rct_drawpixelinfo * dpi)
@ -862,4 +905,142 @@ void OpenGLDrawingContext::SetDPI(rct_drawpixelinfo * dpi)
_dpi = dpi;
}
sint32 OpenGLDrawingContext::MaxTransparencyDepth()
{
sint32 max_depth = 1;
struct xdata
{
sint32 xposition;
bool begin;
sint32 top, bottom;
};
std::vector<xdata> x_sweep;
x_sweep.reserve(_commandBuffers.transparent.size() * 2);
for (DrawRectCommand &command : _commandBuffers.transparent)
{
sint32 left = std::min(std::max(command.bounds.x, command.clip.x), command.clip.z);
sint32 top = std::min(std::max(command.bounds.y, command.clip.y), command.clip.w);
sint32 right = std::min(std::max(command.bounds.z, command.clip.x), command.clip.z);
sint32 bottom = std::min(std::max(command.bounds.w, command.clip.y), command.clip.w);
assert(left <= right);
assert(top <= bottom);
if (left == right) continue;
if (top == bottom) continue;
x_sweep.push_back({left, true, top, bottom});
x_sweep.push_back({right, false, top, bottom});
}
std::sort(x_sweep.begin(), x_sweep.end(), [](const xdata &a, const xdata &b) -> bool {
if (a.xposition != b.xposition) return a.xposition < b.xposition;
else return !a.begin && b.begin;
});
struct ydata
{
sint32 count, depth;
};
std::map<sint32, ydata> y_intersect;
for (const xdata &x : x_sweep)
{
assert(y_intersect.size() == 0 || y_intersect.begin()->second.depth == 0);
if (x.begin)
{
auto top_in = y_intersect.insert({x.top, {1, 0}});
auto top_it = top_in.first;
if (top_in.second)
{
auto top_next = std::next(top_it);
if (top_next != y_intersect.end())
{
top_it->second.depth = top_next->second.depth;
}
}
else
{
assert(top_it->second.count > 0);
++top_it->second.count;
}
auto bottom_in = y_intersect.insert({x.bottom, {1, 1}});
auto bottom_it = bottom_in.first;
if (bottom_in.second)
{
auto bottom_next = std::next(bottom_it);
if (bottom_next != y_intersect.end())
{
bottom_it->second.depth = bottom_next->second.depth + 1;
}
}
else
{
assert(bottom_it->second.count > 0);
++bottom_it->second.count;
max_depth = std::max(max_depth, ++bottom_it->second.depth);
}
for (auto it = std::next(top_it); it != bottom_it; ++it)
{
max_depth = std::max(max_depth, ++it->second.depth);
}
}
else
{
auto top_it = y_intersect.find(x.top);
assert(top_it != y_intersect.end());
assert(top_it->second.count > 0);
auto bottom_it = y_intersect.find(x.bottom);
assert(bottom_it != y_intersect.end());
assert(bottom_it->second.count > 0);
#ifndef NDEBUG
if (top_it->second.count == 1)
{
auto top_next = std::next(top_it);
assert(top_next == y_intersect.end() ?
top_it->second.depth == 0 :
top_it->second.depth == top_next->second.depth - 1);
}
if (bottom_it->second.count == 1)
{
auto bottom_next = std::next(bottom_it);
assert(bottom_next == y_intersect.end() ?
bottom_it->second.depth == 1 :
bottom_it->second.depth == bottom_next->second.depth + 1);
}
#endif /* NDEBUG */
for (auto it = std::next(top_it); it != bottom_it; ++it)
{
assert(it->second.depth > 0);
--it->second.depth;
}
if (top_it->second.count == 1)
{
y_intersect.erase(top_it);
}
else
{
--top_it->second.count;
}
if (bottom_it->second.count == 1)
{
y_intersect.erase(bottom_it);
}
else
{
assert(bottom_it->second.depth > 0);
--bottom_it->second.count;
--bottom_it->second.depth;
}
}
}
return max_depth;
}
#endif /* DISABLE_OPENGL */

View File

@ -44,12 +44,7 @@ OpenGLFramebuffer::OpenGLFramebuffer(sint32 width, sint32 height, bool depth)
if (depth)
{
glGenTextures(1, &_depth);
glBindTexture(GL_TEXTURE_2D, _depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
_depth = CreateDepthTexture(width, height);
}
else
{
@ -93,7 +88,9 @@ void OpenGLFramebuffer::GetPixels(rct_drawpixelinfo &dpi) const
assert(dpi.width == _width && dpi.height == _height);
uint8 * pixels = Memory::Allocate<uint8>(_width * _height);
glReadPixels(0, 0, _width, _height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, pixels);
glBindTexture(GL_TEXTURE_2D, _texture);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, pixels);
// Flip pixels vertically on copy
uint8 * src = pixels + ((_height - 1) * _width);
@ -107,4 +104,37 @@ void OpenGLFramebuffer::GetPixels(rct_drawpixelinfo &dpi) const
Memory::Free(pixels);
}
void OpenGLFramebuffer::SwapColourBuffer(OpenGLFramebuffer &other)
{
std::swap(_texture, other._texture);
glBindFramebuffer(GL_FRAMEBUFFER, _id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, other._id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, other._texture, 0);
}
GLuint OpenGLFramebuffer::SwapDepthTexture(GLuint depth)
{
std::swap(_depth, depth);
glBindFramebuffer(GL_FRAMEBUFFER, _id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depth, 0);
return depth;
}
GLuint OpenGLFramebuffer::CreateDepthTexture(sint32 width, sint32 height)
{
GLuint depth;
glGenTextures(1, &depth);
glBindTexture(GL_TEXTURE_2D, depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
return depth;
}
#endif /* DISABLE_OPENGL */

View File

@ -38,6 +38,9 @@ public:
OpenGLFramebuffer(sint32 width, sint32 height, bool depth = true);
~OpenGLFramebuffer();
OpenGLFramebuffer(const OpenGLFramebuffer &) = delete;
OpenGLFramebuffer &operator=(const OpenGLFramebuffer &) = delete;
GLuint GetWidth() const { return _width; }
GLuint GetHeight() const { return _height; }
GLuint GetTexture() const { return _texture; }
@ -47,4 +50,9 @@ public:
void BindDraw() const;
void BindRead() const;
void GetPixels(rct_drawpixelinfo &dpi) const;
void SwapColourBuffer(OpenGLFramebuffer &other);
GLuint SwapDepthTexture(GLuint depth);
static GLuint CreateDepthTexture(sint32 width, sint32 height);
};

View File

@ -20,24 +20,21 @@
#include "OpenGLFramebuffer.h"
#include "SwapFramebuffer.h"
constexpr GLfloat depthValue[1] = { 1.0f };
constexpr GLfloat depthValueTransparent[1] = { 0.0f };
constexpr GLuint indexValue[4] = { 0, 0, 0, 0 };
SwapFramebuffer::SwapFramebuffer(sint32 width, sint32 height) :
_opaqueFramebuffer(width, height), _transparentFramebuffer(width, height),
_finalFramebuffer(width, height, false)
{ }
void SwapFramebuffer::BindOpaque()
{
_opaqueFramebuffer.Bind();
}
void SwapFramebuffer::BindTransparent()
_mixFramebuffer(width, height, false), _backDepth(OpenGLFramebuffer::CreateDepthTexture(width, height))
{
_transparentFramebuffer.Bind();
glClearBufferfv(GL_DEPTH, 0, depthValueTransparent);
}
void SwapFramebuffer::ApplyTransparency(ApplyTransparencyShader &shader, GLuint paletteTex)
{
_finalFramebuffer.Bind();
_mixFramebuffer.Bind();
glDisable(GL_DEPTH_TEST);
shader.Use();
shader.SetTextures(
@ -49,21 +46,22 @@ void SwapFramebuffer::ApplyTransparency(ApplyTransparencyShader &shader, GLuint
);
shader.Draw();
_backDepth = _transparentFramebuffer.SwapDepthTexture(_backDepth);
// Clear transparency buffers
_transparentFramebuffer.Bind();
glClearBufferuiv(GL_COLOR, 0, indexValue);
glClearBufferfv(GL_DEPTH, 0, depthValueTransparent);
_opaqueFramebuffer.SwapColourBuffer(_mixFramebuffer);
//Change binding to guaruntee no undefined behavior
_opaqueFramebuffer.Bind();
}
void SwapFramebuffer::Clear()
{
static const GLfloat depthValue[1] = { 1.0f };
static const GLuint indexValue[4] = { 0, 0, 0, 0 };
_opaqueFramebuffer.Bind();
glClearBufferfv(GL_DEPTH, 0, depthValue);
_transparentFramebuffer.Bind();
glClearBufferuiv(GL_COLOR, 0, indexValue);
glClearBufferfv(GL_DEPTH, 0, depthValue);
}
#endif /* DISABLE_OPENGL */

View File

@ -36,18 +36,17 @@ class SwapFramebuffer final
private:
OpenGLFramebuffer _opaqueFramebuffer;
OpenGLFramebuffer _transparentFramebuffer;
OpenGLFramebuffer _finalFramebuffer;
OpenGLFramebuffer _mixFramebuffer;
GLuint _backDepth;
public:
SwapFramebuffer(sint32 width, sint32 height);
const OpenGLFramebuffer &GetOpaqueFramebuffer() const { return _opaqueFramebuffer; }
const OpenGLFramebuffer &GetTransparentFramebuffer() const { return _transparentFramebuffer; }
const OpenGLFramebuffer &GetFinalFramebuffer() const { return _finalFramebuffer; }
GLuint GetFinalTexture() const { return _finalFramebuffer.GetTexture(); }
const OpenGLFramebuffer &GetFinalFramebuffer() const { return _opaqueFramebuffer; }
GLuint GetBackDepthTexture() const { return _backDepth; }
void BindOpaque() { _opaqueFramebuffer.Bind(); }
void BindTransparent() { _transparentFramebuffer.Bind(); }
void BindOpaque();
void BindTransparent();
void ApplyTransparency(ApplyTransparencyShader &shader, GLuint paletteTex);
void Clear();
};