mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 23:55:40 +00:00
cdf5e4b2b2
The two transparency layers, might not be separate layers at all. We believe the hardware is writing each layer to every other pixel (stipple alpha), then using an anti-aliasing filter to effectively blend the pixels. Because the pixels don't overlap they don't depth test against each other. We are using separate layers to emulate this, so the depth buffer must be saved and restored between the layers.
348 lines
9.1 KiB
C++
348 lines
9.1 KiB
C++
#include "R3DFrameBuffers.h"
|
|
|
|
namespace New3D {
|
|
|
|
R3DFrameBuffers::R3DFrameBuffers()
|
|
{
|
|
m_frameBufferID = 0;
|
|
m_renderBufferID = 0;
|
|
m_frameBufferIDCopy = 0;
|
|
m_renderBufferIDCopy = 0;
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_vao = 0;
|
|
|
|
for (auto &i : m_texIDs) {
|
|
i = 0;
|
|
}
|
|
|
|
m_lastLayer = Layer::none;
|
|
|
|
AllocShaderTrans();
|
|
AllocShaderBase();
|
|
|
|
glGenVertexArrays(1, &m_vao);
|
|
glBindVertexArray(m_vao);
|
|
// no states needed since we do it in the shader
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
R3DFrameBuffers::~R3DFrameBuffers()
|
|
{
|
|
DestroyFBO();
|
|
m_shaderTrans.UnloadShaders();
|
|
m_shaderBase.UnloadShaders();
|
|
|
|
if (m_vao) {
|
|
glDeleteVertexArrays(1, &m_vao);
|
|
m_vao = 0;
|
|
}
|
|
}
|
|
|
|
bool R3DFrameBuffers::CreateFBO(int width, int height)
|
|
{
|
|
m_width = width;
|
|
m_height = height;
|
|
|
|
m_texIDs[0] = CreateTexture(width, height); // colour buffer
|
|
m_texIDs[1] = CreateTexture(width, height); // trans layer1
|
|
m_texIDs[2] = CreateTexture(width, height); // trans layer2
|
|
|
|
glGenFramebuffers(1, &m_frameBufferID);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
|
|
// colour attachments
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texIDs[0], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_texIDs[1], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_texIDs[2], 0);
|
|
|
|
// depth/stencil attachment
|
|
glGenRenderbuffers(1, &m_renderBufferID);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferID);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferID);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferID);
|
|
|
|
// check setup was successful
|
|
auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0); //created R3DFrameBuffers now disable it
|
|
|
|
CreateFBODepthCopy(width, height);
|
|
|
|
return (fboStatus == GL_FRAMEBUFFER_COMPLETE);
|
|
}
|
|
|
|
bool R3DFrameBuffers::CreateFBODepthCopy(int width, int height)
|
|
{
|
|
glGenFramebuffers(1, &m_frameBufferIDCopy);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferIDCopy);
|
|
|
|
glGenRenderbuffers(1, &m_renderBufferIDCopy);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferIDCopy);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferIDCopy);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferIDCopy);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
// check setup was successful
|
|
auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
return (fboStatus == GL_FRAMEBUFFER_COMPLETE);
|
|
}
|
|
|
|
void R3DFrameBuffers::StoreDepth()
|
|
{
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferID);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferIDCopy);
|
|
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
|
|
void R3DFrameBuffers::RestoreDepth()
|
|
{
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferIDCopy);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferID);
|
|
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
|
|
void R3DFrameBuffers::DestroyFBO()
|
|
{
|
|
if (m_frameBufferID) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDeleteRenderbuffers(1, &m_renderBufferID);
|
|
glDeleteFramebuffers(1, &m_frameBufferID);
|
|
}
|
|
|
|
if (m_frameBufferIDCopy) {
|
|
glDeleteRenderbuffers(1, &m_renderBufferIDCopy);
|
|
glDeleteFramebuffers(1, &m_frameBufferIDCopy);
|
|
}
|
|
|
|
for (auto &i : m_texIDs) {
|
|
if (i) {
|
|
glDeleteTextures(1, &i);
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
m_frameBufferID = 0;
|
|
m_renderBufferID = 0;
|
|
m_frameBufferIDCopy = 0;
|
|
m_renderBufferIDCopy = 0;
|
|
m_width = 0;
|
|
m_height = 0;
|
|
}
|
|
|
|
GLuint R3DFrameBuffers::CreateTexture(int width, int height)
|
|
{
|
|
GLuint texId;
|
|
glGenTextures(1, &texId);
|
|
glBindTexture(GL_TEXTURE_2D, texId);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
return texId;
|
|
}
|
|
|
|
void R3DFrameBuffers::BindTexture(Layer layer)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, m_texIDs[(int)layer]);
|
|
}
|
|
|
|
void R3DFrameBuffers::SetFBO(Layer layer)
|
|
{
|
|
if (m_lastLayer == layer) {
|
|
return;
|
|
}
|
|
|
|
switch (layer)
|
|
{
|
|
case Layer::colour:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::trans1:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::trans2:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::none:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDrawBuffer(GL_BACK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_lastLayer = layer;
|
|
}
|
|
|
|
void R3DFrameBuffers::AllocShaderBase()
|
|
{
|
|
const char *vertexShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
// outputs
|
|
out vec2 fsTexCoord;
|
|
|
|
void main(void)
|
|
{
|
|
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
|
|
vec4(-1.0, 1.0, 0.0, 1.0),
|
|
vec4( 1.0, -1.0, 0.0, 1.0),
|
|
vec4( 1.0, 1.0, 0.0, 1.0));
|
|
|
|
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
|
|
gl_Position = vertices[gl_VertexID % 4];
|
|
}
|
|
|
|
)glsl";
|
|
|
|
const char *fragmentShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
// inputs
|
|
uniform sampler2D tex1; // base tex
|
|
in vec2 fsTexCoord;
|
|
|
|
// outputs
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
fragColor = texture(tex1, fsTexCoord);
|
|
}
|
|
|
|
)glsl";
|
|
|
|
m_shaderBase.LoadShaders(vertexShader, fragmentShader);
|
|
m_shaderBase.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
|
|
}
|
|
|
|
void R3DFrameBuffers::AllocShaderTrans()
|
|
{
|
|
const char *vertexShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
// outputs
|
|
out vec2 fsTexCoord;
|
|
|
|
void main(void)
|
|
{
|
|
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
|
|
vec4(-1.0, 1.0, 0.0, 1.0),
|
|
vec4( 1.0, -1.0, 0.0, 1.0),
|
|
vec4( 1.0, 1.0, 0.0, 1.0));
|
|
|
|
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
|
|
gl_Position = vertices[gl_VertexID % 4];
|
|
}
|
|
|
|
)glsl";
|
|
|
|
const char *fragmentShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
uniform sampler2D tex1; // trans layer 1
|
|
uniform sampler2D tex2; // trans layer 2
|
|
|
|
in vec2 fsTexCoord;
|
|
|
|
// outputs
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
vec4 colTrans1 = texture(tex1, fsTexCoord);
|
|
vec4 colTrans2 = texture(tex2, fsTexCoord);
|
|
|
|
// if both transparency layers overlap, the result is opaque
|
|
if (colTrans1.a * colTrans2.a > 0.0) {
|
|
|
|
// are the two lines functionally identical? Need to check.
|
|
//vec3 mixCol = mix(colTrans1.rgb, colTrans2.rgb, (colTrans2.a + (1.0 - colTrans1.a)) / 2.0);
|
|
vec3 mixCol = (((colTrans1.rgb * colTrans1.a) + (colTrans2.rgb * (1.0-colTrans1.a))) + ((colTrans2.rgb * colTrans2.a) + (colTrans1 .rgb * (1.0-colTrans2.a)))) / 2.0;
|
|
fragColor = vec4(mixCol, 1.0);
|
|
}
|
|
else if (colTrans1.a > 0.0) {
|
|
fragColor = colTrans1;
|
|
}
|
|
else {
|
|
fragColor = colTrans2; // if alpha is zero it will have no effect anyway
|
|
}
|
|
}
|
|
|
|
)glsl";
|
|
|
|
m_shaderTrans.LoadShaders(vertexShader, fragmentShader);
|
|
|
|
m_shaderTrans.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
|
|
m_shaderTrans.uniformLoc[1] = m_shaderTrans.GetUniformLocation("tex2");
|
|
}
|
|
|
|
void R3DFrameBuffers::Draw()
|
|
{
|
|
SetFBO (Layer::none); // make sure to draw on the back buffer
|
|
glViewport (0, 0, m_width, m_height); // cover the entire screen
|
|
glDisable (GL_DEPTH_TEST); // disable depth testing / writing
|
|
glDisable (GL_CULL_FACE);
|
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable (GL_BLEND);
|
|
|
|
for (int i = 0; i < (int)std::size(m_texIDs); i++) { // bind our textures to correct texture units
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
glBindTexture(GL_TEXTURE_2D, m_texIDs[i]);
|
|
}
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindVertexArray (m_vao);
|
|
|
|
DrawBaseLayer ();
|
|
DrawAlphaLayer ();
|
|
|
|
glDisable (GL_BLEND);
|
|
glBindVertexArray (0);
|
|
}
|
|
|
|
void R3DFrameBuffers::DrawBaseLayer()
|
|
{
|
|
m_shaderBase.EnableShader();
|
|
glUniform1i(m_shaderTrans.uniformLoc[0], 0);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
m_shaderBase.DisableShader();
|
|
}
|
|
|
|
void R3DFrameBuffers::DrawAlphaLayer()
|
|
{
|
|
m_shaderTrans.EnableShader();
|
|
glUniform1i(m_shaderTrans.uniformLoc[0], 1); // tex unit 1
|
|
glUniform1i(m_shaderTrans.uniformLoc[1], 2); // tex unit 2
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
m_shaderTrans.DisableShader();
|
|
}
|
|
|
|
}
|