Implements a very basic GLSL deinterlace that simply halves the vertical resolution and then interpolates between the fields. This can be toggled on or off. The reasons for being so basic is: - Speed, very quick code running in OpenGL - It would seem the highest quality deinterlacers are temporally based which doesn't make much sense for the viewer, particularly since we can't double the frame rate since our timecode is fixed to the frames. Higher quality interlacing/deinterlacing will be present in the actual renderer.
255 lines
9.6 KiB
C++
255 lines
9.6 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2019 Olive Team
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
#include "openglshader.h"
|
|
|
|
#include <QOpenGLExtraFunctions>
|
|
OLIVE_NAMESPACE_ENTER
|
|
|
|
OpenGLShaderPtr OpenGLShader::Create()
|
|
{
|
|
return std::make_shared<OpenGLShader>();
|
|
}
|
|
|
|
OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const QString &shader_code)
|
|
{
|
|
OpenGLShaderPtr program = Create();
|
|
|
|
// Add shaders to program
|
|
program->addShaderFromSourceCode(QOpenGLShader::Vertex, CodeDefaultVertex());
|
|
program->addShaderFromSourceCode(QOpenGLShader::Fragment, CodeDefaultFragment(function_name, shader_code));
|
|
program->link();
|
|
|
|
return program;
|
|
}
|
|
|
|
// copied from source code to OCIODisplay
|
|
const int OCIO_LUT3D_EDGE_SIZE = 64;
|
|
|
|
// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE
|
|
const int OCIO_NUM_3D_ENTRIES = 3*OCIO_LUT3D_EDGE_SIZE*OCIO_LUT3D_EDGE_SIZE*OCIO_LUT3D_EDGE_SIZE;
|
|
|
|
OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
|
GLuint& lut_texture,
|
|
OCIO::ConstProcessorRcPtr processor,
|
|
bool alpha_is_associated)
|
|
{
|
|
QOpenGLExtraFunctions* xf = ctx->extraFunctions();
|
|
|
|
// Set up shader description
|
|
OCIO::GpuShaderDesc shaderDesc;
|
|
const char* ocio_func_name = "OCIODisplay";
|
|
shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3);
|
|
shaderDesc.setFunctionName(ocio_func_name);
|
|
shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE);
|
|
|
|
// Compute LUT
|
|
std::vector<float> ocio_lut_data(OCIO_NUM_3D_ENTRIES);
|
|
processor->getGpuLut3D(&ocio_lut_data[0], shaderDesc);
|
|
|
|
// Create LUT texture
|
|
xf->glGenTextures(1, &lut_texture);
|
|
|
|
// Bind LUT
|
|
xf->glActiveTexture(GL_TEXTURE1);
|
|
xf->glBindTexture(GL_TEXTURE_3D, lut_texture);
|
|
|
|
// Set texture parameters
|
|
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
// Allocate storage for texture
|
|
xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F,
|
|
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
|
|
0, GL_RGB, GL_FLOAT, &ocio_lut_data[0]);
|
|
|
|
// Create OCIO shader code
|
|
QString shader_text;
|
|
|
|
// Workaround since OCIO doesn't support the GLSL version we use
|
|
shader_text.append(QStringLiteral("#define texture2D texture\n"
|
|
"#define texture3D texture\n"));
|
|
|
|
// Append OCIO shader code
|
|
shader_text.append(processor->getGpuShaderText(shaderDesc));
|
|
|
|
QString shader_call;
|
|
|
|
// Enforce alpha association
|
|
if (alpha_is_associated) {
|
|
|
|
// If alpha is already associated, we'll need to disassociate and reassociate
|
|
shader_text.append("\n");
|
|
|
|
QString disassociate_func_name = "disassoc";
|
|
shader_text.append(CodeAlphaDisassociate(disassociate_func_name));
|
|
|
|
QString reassociate_func_name = "reassoc";
|
|
shader_text.append(CodeAlphaReassociate(reassociate_func_name));
|
|
|
|
// Make OCIO call pass through disassociate and reassociate function
|
|
shader_call = QStringLiteral("%3(%1(%2(col), ove_ociolut));").arg(ocio_func_name,
|
|
disassociate_func_name,
|
|
reassociate_func_name);
|
|
|
|
} else {
|
|
|
|
// If alpha is not already associated, we can just associate after OCIO
|
|
|
|
// Add associate function
|
|
QString associate_func_name = "assoc";
|
|
shader_text.append(CodeAlphaAssociate(associate_func_name));
|
|
|
|
// Make OCIO call pass through associate function
|
|
shader_call = QStringLiteral("%2(%1(col, ove_ociolut));").arg(ocio_func_name, associate_func_name);
|
|
|
|
}
|
|
|
|
// Add process() function, which GetPipeline() will call if specified
|
|
QString process_function_name = "process";
|
|
shader_text.append(QStringLiteral("\n"
|
|
"uniform sampler3D ove_ociolut;\n"
|
|
"\n"
|
|
"vec4 %2(vec4 col) {\n"
|
|
" return %1\n"
|
|
"}\n").arg(shader_call, process_function_name));
|
|
|
|
|
|
// Get pipeline-based shader to inject OCIO shader into
|
|
OpenGLShaderPtr shader = OpenGLShader::CreateDefault(process_function_name, shader_text);
|
|
|
|
// Release LUT
|
|
xf->glBindTexture(GL_TEXTURE_3D, 0);
|
|
|
|
xf->glActiveTexture(GL_TEXTURE0);
|
|
|
|
return shader;
|
|
}
|
|
|
|
QString OpenGLShader::CodeDefaultFragment(QString function_name, const QString &shader_code)
|
|
{
|
|
// Create shader header
|
|
QString frag_code = QStringLiteral("#version 150\n"
|
|
"\n"
|
|
"#ifdef GL_ES\n"
|
|
"precision highp int;\n"
|
|
"precision highp float;\n"
|
|
"#endif\n"
|
|
"\n"
|
|
"uniform sampler2D ove_maintex;\n"
|
|
"uniform vec2 ove_resolution;\n"
|
|
"uniform bool ove_deinterlace;\n"
|
|
"\n"
|
|
"in vec2 ove_texcoord;\n"
|
|
"\n"
|
|
"out vec4 fragColor;\n"
|
|
"\n");
|
|
|
|
// Check if additional code was passed to this function, add it here
|
|
if (!function_name.isEmpty() && !shader_code.isEmpty()) {
|
|
|
|
// If additional code was passed, add it and reference it in main().
|
|
//
|
|
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`.
|
|
// The texture coordinate can be acquired through `ove_texcoord`.
|
|
|
|
frag_code.append(shader_code);
|
|
|
|
} else {
|
|
|
|
// No function to call
|
|
function_name = QString();
|
|
|
|
}
|
|
|
|
// Our function_name arg will either resolve to the function added to this or to nothing, in
|
|
// which case they'll just be benign brackets.
|
|
frag_code.append(QStringLiteral("\n"
|
|
"void main() {\n"
|
|
" vec2 using_texcoord = ove_texcoord;\n"
|
|
" if (ove_deinterlace) {\n"
|
|
" // A very basic deinterlace that halves the vertical\n"
|
|
" // resolution and linearly interpolates the two fields\n"
|
|
" // by reading the texture coord between them.\n"
|
|
" float half_vert = round(ove_resolution.y / 2.0);\n"
|
|
" using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert;\n"
|
|
" }\n"
|
|
" vec4 color = %1(texture(ove_maintex, using_texcoord));\n"
|
|
" fragColor = color;\n"
|
|
"}\n").arg(function_name));
|
|
|
|
return frag_code;
|
|
}
|
|
|
|
QString OpenGLShader::CodeDefaultVertex()
|
|
{
|
|
// Generate vertex shader
|
|
return QStringLiteral("#version 150\n"
|
|
"\n"
|
|
"#ifdef GL_ES\n"
|
|
"precision highp int;\n"
|
|
"precision highp float;\n"
|
|
"#endif\n"
|
|
"\n"
|
|
"uniform mat4 ove_mvpmat;\n"
|
|
"\n"
|
|
"in vec4 a_position;\n"
|
|
"in vec2 a_texcoord;\n"
|
|
"\n"
|
|
"out vec2 ove_texcoord;\n"
|
|
"\n"
|
|
"void main() {\n"
|
|
" gl_Position = ove_mvpmat * a_position;\n"
|
|
" ove_texcoord = a_texcoord;\n"
|
|
"}\n");
|
|
}
|
|
|
|
QString OpenGLShader::CodeAlphaDisassociate(const QString &function_name)
|
|
{
|
|
return QStringLiteral("vec4 %1(vec4 col) {\n"
|
|
" if (col.a > 0.0) {\n"
|
|
" return vec4(col.rgb / col.a, col.a);"
|
|
" }\n"
|
|
" return col;\n"
|
|
"}\n").arg(function_name);
|
|
}
|
|
|
|
QString OpenGLShader::CodeAlphaReassociate(const QString &function_name)
|
|
{
|
|
return QStringLiteral("vec4 %1(vec4 col) {\n"
|
|
" if (col.a > 0.0) {\n"
|
|
" return vec4(col.rgb * col.a, col.a);"
|
|
" }\n"
|
|
" return col;\n"
|
|
"}\n").arg(function_name);
|
|
}
|
|
|
|
QString OpenGLShader::CodeAlphaAssociate(const QString &function_name)
|
|
{
|
|
return QStringLiteral("vec4 %1(vec4 col) {\n"
|
|
" return vec4(col.rgb * col.a, col.a);\n"
|
|
"}\n").arg(function_name);
|
|
}
|
|
|
|
OLIVE_NAMESPACE_EXIT
|