mirror of
https://github.com/libretro/Libretro-Supermodel.git
synced 2026-11-10 11:11:26 -10:00
(cherry picked from commit 800e6dfe11, adapted)
Render the 3D scene at an integer multiple of the output resolution and
let SuperAA downsample it into the libretro framebuffer. SuperAA's output
now always reaches the frontend, which also makes CRT Colour take effect.
Adapted on dev:
- SuperAA keeps its own target handling; it gains SetOutputTarget() so
Draw() writes into the libretro resolved FBO, which is now always
created at the output size and returned by getSuperModelFBO().
- The option is shown on desktop OpenGL builds only (HAVE_SUPERSAMPLING
in CoreOptionsTypes.h, from the GLES defines) instead of a Makefile
platform list. No LibretroConfigProvider change was needed.
- The value is applied after the Supermodel.ini merge, like the other
core options, and changes reuse the existing "apply after restarting
the content" message.
- The 3D scissor is scaled by the supersampling factor.
- CRLF line endings kept in SuperAA.cpp/.h.
Tested: Linux: default output unchanged; CRT Colour now visibly changes
the colours (Japan D93 strongly, USA D65 subtly); Supersampling 2x and
4x give the full picture with anti-aliased edges in Virtua Fighter 3 and
Scud Race; 2x Resolution + 2x Supersampling and Wide Background + 2x
Supersampling fill the frame; Supersampling + CRT Colour both apply.
Windows build has 3D Renderer, CRT Colour and Supersampling, macOS CRT
Colour and Supersampling, RPi5 none.
235 lines
6.6 KiB
C++
235 lines
6.6 KiB
C++
#include "SuperAA.h"
|
|
#include <string>
|
|
#include "GLSLVersion.h"
|
|
|
|
SuperAA::SuperAA(int aaValue, CRTcolor CRTcolors) :
|
|
m_aa(aaValue),
|
|
m_crtcolors(CRTcolors),
|
|
m_vao(0),
|
|
m_outputTarget(0),
|
|
m_width(0),
|
|
m_height(0)
|
|
{
|
|
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
|
|
|
|
static const char* vertexShader = R"glsl(
|
|
|
|
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));
|
|
|
|
gl_Position = vertices[gl_VertexID % 4];
|
|
}
|
|
|
|
)glsl";
|
|
|
|
|
|
std::string vertexShaderVersion = Graphics::GLSLVersion::GetPostProcess();
|
|
std::string fragmentShaderVersion = Graphics::GLSLVersion::GetPostProcess();
|
|
|
|
std::string aaString = "const int aa = ";
|
|
aaString += std::to_string(m_aa);
|
|
aaString += ";\n";
|
|
std::string ccString = "#define CRTCOLORS ";
|
|
ccString += std::to_string((int)m_crtcolors);
|
|
ccString += '\n';
|
|
|
|
static const std::string fragmentShader = R"glsl(
|
|
|
|
// inputs
|
|
uniform sampler2D tex1; // base tex
|
|
|
|
// outputs
|
|
out vec4 fragColor;
|
|
|
|
#if (CRTCOLORS == 1)
|
|
const float cgamma = 2.5; // what is the 'real' gamma/EOTF of a japanese arcade CRT of that time? 2.4 or 2.5 is what the net so far agrees on
|
|
#elif (CRTCOLORS == 2)
|
|
const float cgamma = 2.25;
|
|
#elif (CRTCOLORS > 2)
|
|
const float cgamma = -1.0;
|
|
#endif
|
|
|
|
// see https://www.retrorgb.com/colour-malarkey.html
|
|
// and accompanying matrices https://github.com/danmons/colour_matrix_adaptations/tree/main/csv
|
|
#if (CRTCOLORS == 1)
|
|
const mat3 colmatrix = mat3(1.3272128334714093, // ARI(D93/9300K) (JP) -> sRGB(D65/6500K) // what can be found in some of the sega or corresponding CRT manuals of the time is a range of ~9700K-10600K, so even cooler than D93
|
|
-0.3802108879412366,
|
|
-0.1003607696463202,
|
|
-0.0241848600268417,
|
|
0.9544506228550125,
|
|
0.0807358585208939,
|
|
-0.0239585810555497,
|
|
-0.0409736005706461,
|
|
1.4076563728858242);
|
|
#elif (CRTCOLORS == 2)
|
|
const mat3 colmatrix = mat3(0.9241392201737613, // PVM_20M2U(D93/9300K) (JP) -> sRGB(D65/6500K), gamma 2.25
|
|
-0.0690701363506526,
|
|
-0.0084279079392561,
|
|
0.0231962420140721,
|
|
0.9729221778325590,
|
|
0.0148832015024337,
|
|
-0.0054875731998806,
|
|
0.0098220960740544,
|
|
1.3383896683854550);
|
|
#elif (CRTCOLORS == 3)
|
|
const mat3 colmatrix = mat3(0.7822490684754086, // BT601_525_D93 (JP) -> sRGB(D65/6500K), gamma sRGB
|
|
0.0506168576073398,
|
|
0.0137752498011040,
|
|
0.0147968947158740,
|
|
0.9741745313046067,
|
|
0.0220301953285839,
|
|
-0.0013501205469208,
|
|
-0.0044076726925543,
|
|
1.3484819844991036);
|
|
#elif (CRTCOLORS == 4)
|
|
const mat3 colmatrix = mat3(0.9395420637732393, // BT601_525 (US) -> sRGB(D65/6500K), gamma sRGB // extremely subtle
|
|
0.0501813568598678,
|
|
0.0102765793668928,
|
|
0.0177722231435608,
|
|
0.9657928624969044,
|
|
0.0164349143595347,
|
|
-0.0016215999431855,
|
|
-0.0043697496597356,
|
|
1.0059913496029214);
|
|
#elif (CRTCOLORS == 5)
|
|
const mat3 colmatrix = mat3(1.0440432087628346, // BT601_625 (EUR,AUS) -> sRGB(D65/6500K), gamma sRGB // barely noticeable at all
|
|
-0.0440432087628348,
|
|
0.0000000000000001,
|
|
0.0000000000000000,
|
|
1.0000000000000002,
|
|
0.0000000000000000,
|
|
0.0000000000000000,
|
|
0.0117933782840052,
|
|
0.9882066217159947);
|
|
#endif
|
|
|
|
vec3 GetTextureValue(sampler2D s)
|
|
{
|
|
ivec2 texPos = ivec2(gl_FragCoord.xy /*-vec2(0.5)*/) * aa;
|
|
vec3 texColour = vec3(0.0);
|
|
|
|
for(int i=0; i < aa; i++) {
|
|
for(int j=0; j < aa; j++) {
|
|
texColour += texelFetch(s,ivec2(texPos.x+i,texPos.y+j),0).rgb;
|
|
}
|
|
}
|
|
|
|
texColour *= 1.0/float(aa * aa);
|
|
|
|
return texColour;
|
|
}
|
|
|
|
float sRGB(float color)
|
|
{
|
|
if (color <= 0.0031308)
|
|
return 12.92 * color;
|
|
else
|
|
return 1.055 * pow(color, 1.0/2.4) - 0.055;
|
|
}
|
|
|
|
float invsRGB(float color)
|
|
{
|
|
if (color <= 0.04045) // 0.03928 ?
|
|
return color * (1.0/12.92);
|
|
else
|
|
return pow(color * (1.0/1.055) + (0.055/1.055), 2.4);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec3 finalColor = GetTextureValue(tex1);
|
|
|
|
#if (CRTCOLORS != 0)
|
|
{
|
|
// transform input space to linear space
|
|
if (cgamma != -1.0)
|
|
finalColor = pow(finalColor, vec3(cgamma));
|
|
else
|
|
finalColor = vec3(invsRGB(finalColor.r),invsRGB(finalColor.g),invsRGB(finalColor.b));
|
|
// do color transformation
|
|
finalColor *= colmatrix;
|
|
// transform from linear to sRGB output space
|
|
finalColor = vec3(sRGB(finalColor.r),sRGB(finalColor.g),sRGB(finalColor.b));
|
|
}
|
|
#endif
|
|
|
|
fragColor = vec4(finalColor, 1.0);
|
|
}
|
|
|
|
)glsl";
|
|
|
|
std::string vertexShaderString = vertexShaderVersion + vertexShader;
|
|
std::string fragmentShaderString = fragmentShaderVersion + aaString + ccString + fragmentShader;
|
|
|
|
// load shaders
|
|
m_shader.LoadShaders(vertexShaderString.c_str(), fragmentShaderString.c_str());
|
|
m_shader.GetUniformLocationMap("tex1");
|
|
|
|
// setup uniform memory
|
|
m_shader.EnableShader();
|
|
glUniform1i(m_shader.attribLocMap["tex1"], 0); // texture will be bound to unit zero
|
|
m_shader.DisableShader();
|
|
|
|
glGenVertexArrays(1, &m_vao);
|
|
glBindVertexArray(m_vao);
|
|
// no states needed since we do it in the shader
|
|
glBindVertexArray(0);
|
|
}
|
|
}
|
|
|
|
// need an active context bound to the current thread to destroy our objects
|
|
SuperAA::~SuperAA()
|
|
{
|
|
m_shader.UnloadShaders();
|
|
m_fbo.Destroy();
|
|
|
|
if (m_vao) {
|
|
glDeleteVertexArrays(1, &m_vao);
|
|
m_vao = 0;
|
|
}
|
|
}
|
|
|
|
void SuperAA::Init(int width, int height)
|
|
{
|
|
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
|
|
m_fbo.Destroy();
|
|
m_fbo.Create(width * m_aa, height * m_aa);
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
}
|
|
}
|
|
|
|
void SuperAA::Draw()
|
|
{
|
|
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_outputTarget);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glDisable(GL_BLEND);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_fbo.GetTextureID());
|
|
glBindVertexArray(m_vao);
|
|
glViewport(0, 0, m_width, m_height);
|
|
m_shader.EnableShader();
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
m_shader.DisableShader();
|
|
glBindVertexArray(0);
|
|
}
|
|
}
|
|
|
|
void SuperAA::SetOutputTarget(GLuint fbo)
|
|
{
|
|
m_outputTarget = fbo;
|
|
}
|
|
|
|
GLuint SuperAA::GetTargetID()
|
|
{
|
|
return m_fbo.GetFBOID(); // will return 0 if no render target which will be our default frame buffer (back buffer)
|
|
}
|