mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-19 14:30:53 +00:00
Merge pull request #3 from TomHarte/SeparateSeparation
Distinguishes the luminance filtering stage from the chroma separation stage, improving both
This commit is contained in:
commit
43951ac359
@ -74,11 +74,12 @@ static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length,
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
namespace {
|
||||
static const GLenum composite_texture_unit = GL_TEXTURE0;
|
||||
static const GLenum filtered_y_texture_unit = GL_TEXTURE1;
|
||||
static const GLenum filtered_texture_unit = GL_TEXTURE2;
|
||||
static const GLenum source_data_texture_unit = GL_TEXTURE3;
|
||||
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE4;
|
||||
static const GLenum composite_texture_unit = GL_TEXTURE0;
|
||||
static const GLenum separated_texture_unit = GL_TEXTURE1;
|
||||
static const GLenum filtered_y_texture_unit = GL_TEXTURE2;
|
||||
static const GLenum filtered_texture_unit = GL_TEXTURE3;
|
||||
static const GLenum source_data_texture_unit = GL_TEXTURE4;
|
||||
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE5;
|
||||
}
|
||||
|
||||
OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
||||
@ -105,6 +106,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
||||
|
||||
// Create intermediate textures and bind to slots 0, 1 and 2
|
||||
compositeTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, composite_texture_unit));
|
||||
separatedTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, separated_texture_unit));
|
||||
filteredYTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_y_texture_unit));
|
||||
filteredTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit));
|
||||
|
||||
@ -250,6 +252,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
RenderStage composite_render_stages[] =
|
||||
{
|
||||
{compositeTexture.get(), composite_input_shader_program.get(), {0.0, 0.0, 0.0}},
|
||||
{separatedTexture.get(), composite_separation_filter_program.get(), {0.0, 0.5, 0.5}},
|
||||
{filteredYTexture.get(), composite_y_filter_shader_program.get(), {0.0, 0.5, 0.5}},
|
||||
{filteredTexture.get(), composite_chrominance_filter_shader_program.get(), {0.0, 0.0, 0.0}},
|
||||
{nullptr}
|
||||
@ -377,13 +380,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader)
|
||||
{
|
||||
if(shader)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_openGL_context_will_change(bool should_delete_resources)
|
||||
{
|
||||
}
|
||||
@ -406,8 +402,12 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders()
|
||||
composite_input_shader_program->set_source_texture_unit(source_data_texture_unit);
|
||||
composite_input_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||
|
||||
composite_y_filter_shader_program = OpenGL::IntermediateShader::make_chroma_luma_separation_shader();
|
||||
composite_y_filter_shader_program->set_source_texture_unit(composite_texture_unit);
|
||||
composite_separation_filter_program = OpenGL::IntermediateShader::make_chroma_luma_separation_shader();
|
||||
composite_separation_filter_program->set_source_texture_unit(composite_texture_unit);
|
||||
composite_separation_filter_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||
|
||||
composite_y_filter_shader_program = OpenGL::IntermediateShader::make_luma_filter_shader();
|
||||
composite_y_filter_shader_program->set_source_texture_unit(separated_texture_unit);
|
||||
composite_y_filter_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||
|
||||
composite_chrominance_filter_shader_program = OpenGL::IntermediateShader::make_chroma_filter_shader();
|
||||
@ -533,7 +533,7 @@ void OpenGLOutputBuilder::set_colour_space_uniforms()
|
||||
break;
|
||||
}
|
||||
|
||||
if(composite_input_shader_program) composite_input_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
if(composite_input_shader_program) composite_input_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
@ -544,6 +544,7 @@ void OpenGLOutputBuilder::set_timing_uniforms()
|
||||
|
||||
OpenGL::IntermediateShader *intermediate_shaders[] = {
|
||||
composite_input_shader_program.get(),
|
||||
composite_separation_filter_program.get(),
|
||||
composite_y_filter_shader_program.get(),
|
||||
composite_chrominance_filter_shader_program.get()
|
||||
};
|
||||
@ -558,7 +559,8 @@ void OpenGLOutputBuilder::set_timing_uniforms()
|
||||
if(output_shader_program) output_shader_program->set_timing(_height_of_display, _cycles_per_line, _horizontal_scan_period, _vertical_scan_period, _vertical_period_divider);
|
||||
|
||||
float colour_subcarrier_frequency = (float)_colour_cycle_numerator / (float)_colour_cycle_denominator;
|
||||
if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_separation_frequency(_cycles_per_line, colour_subcarrier_frequency);
|
||||
if(composite_separation_filter_program) composite_separation_filter_program->set_separation_frequency(_cycles_per_line, colour_subcarrier_frequency);
|
||||
if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.66f);
|
||||
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f);
|
||||
if(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f);
|
||||
|
||||
|
@ -63,9 +63,16 @@ class OpenGLOutputBuilder {
|
||||
uint16_t _composite_src_output_y, _cleared_composite_output_y;
|
||||
|
||||
std::unique_ptr<OpenGL::OutputShader> output_shader_program;
|
||||
std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program;
|
||||
std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program, composite_separation_filter_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program;
|
||||
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program, rgb_filter_shader_program;
|
||||
|
||||
std::unique_ptr<OpenGL::TextureTarget> compositeTexture; // receives raw composite levels
|
||||
std::unique_ptr<OpenGL::TextureTarget> separatedTexture; // receives unfiltered Y in the R channel plus unfiltered but demodulated chrominance in G and B
|
||||
std::unique_ptr<OpenGL::TextureTarget> filteredYTexture; // receives filtered Y in the R channel plus unfiltered chrominance in G and B
|
||||
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
|
||||
|
||||
std::unique_ptr<OpenGL::TextureTarget> framebuffer; // the current pixel output
|
||||
|
||||
GLuint output_array_buffer, output_vertex_array;
|
||||
GLuint source_array_buffer, source_vertex_array;
|
||||
|
||||
@ -75,13 +82,6 @@ class OpenGLOutputBuilder {
|
||||
|
||||
GLuint defaultFramebuffer;
|
||||
|
||||
std::unique_ptr<OpenGL::TextureTarget> compositeTexture; // receives raw composite levels
|
||||
std::unique_ptr<OpenGL::TextureTarget> filteredYTexture; // receives filtered Y in the R channel plus unfiltered I/U and Q/V in G and B
|
||||
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
|
||||
|
||||
std::unique_ptr<OpenGL::TextureTarget> framebuffer; // the current pixel output
|
||||
|
||||
void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader);
|
||||
void set_timing_uniforms();
|
||||
void set_colour_space_uniforms();
|
||||
|
||||
|
@ -210,7 +210,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separat
|
||||
"dot(samples[0], weights[0]),"
|
||||
"dot(samples[1], weights[1]),"
|
||||
"dot(samples[2], weights[2])"
|
||||
"), vec3(1.0)) / (1.0 - phaseAndAmplitudeVarying.y);"
|
||||
"), vec3(1.0));"
|
||||
|
||||
"float chrominance = 0.5 * (samples[1].y - luminance) / phaseAndAmplitudeVarying.y;"
|
||||
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), -sin(phaseAndAmplitudeVarying.x));"
|
||||
@ -234,50 +234,95 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec3 centreSample = texture(texID, inputPositionsVarying[5]).rgb;"
|
||||
"vec2 samples[] = vec2[]("
|
||||
"texture(texID, inputPositionsVarying[0]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[1]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[2]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[3]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[4]).gb - vec2(0.5),"
|
||||
"centreSample.gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[6]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[7]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[8]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[9]).gb - vec2(0.5),"
|
||||
"texture(texID, inputPositionsVarying[10]).gb - vec2(0.5)"
|
||||
"vec3 samples[] = vec3[]("
|
||||
"texture(texID, inputPositionsVarying[0]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[1]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[2]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[3]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[4]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[5]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[6]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[7]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[8]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[9]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[10]).rgb"
|
||||
");"
|
||||
|
||||
"vec4 channel1[] = vec4[]("
|
||||
"vec4(samples[0].r, samples[1].r, samples[2].r, samples[3].r),"
|
||||
"vec4(samples[4].r, samples[5].r, samples[6].r, samples[7].r),"
|
||||
"vec4(samples[8].r, samples[9].r, samples[10].r, 0.0)"
|
||||
");"
|
||||
"vec4 channel2[] = vec4[]("
|
||||
"vec4 chromaChannel1[] = vec4[]("
|
||||
"vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g),"
|
||||
"vec4(samples[4].g, samples[5].g, samples[6].g, samples[7].g),"
|
||||
"vec4(samples[8].g, samples[9].g, samples[10].g, 0.0)"
|
||||
");"
|
||||
|
||||
"vec3 lumaChromaColour = vec3(centreSample.r,"
|
||||
"dot(vec3("
|
||||
"dot(channel1[0], weights[0]),"
|
||||
"dot(channel1[1], weights[1]),"
|
||||
"dot(channel1[2], weights[2])"
|
||||
"), vec3(1.0)) + 0.5,"
|
||||
"dot(vec3("
|
||||
"dot(channel2[0], weights[0]),"
|
||||
"dot(channel2[1], weights[1]),"
|
||||
"dot(channel2[2], weights[2])"
|
||||
"), vec3(1.0)) + 0.5"
|
||||
"vec4 chromaChannel2[] = vec4[]("
|
||||
"vec4(samples[0].b, samples[1].b, samples[2].b, samples[3].b),"
|
||||
"vec4(samples[4].b, samples[5].b, samples[6].b, samples[7].b),"
|
||||
"vec4(samples[8].b, samples[9].b, samples[10].b, 0.0)"
|
||||
");"
|
||||
|
||||
"vec3 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 2.0, 2.0);"
|
||||
"vec3 lumaChromaColour = vec3(samples[5].r,"
|
||||
"dot(vec3("
|
||||
"dot(chromaChannel1[0], weights[0]),"
|
||||
"dot(chromaChannel1[1], weights[1]),"
|
||||
"dot(chromaChannel1[2], weights[2])"
|
||||
"), vec3(1.0)),"
|
||||
"dot(vec3("
|
||||
"dot(chromaChannel2[0], weights[0]),"
|
||||
"dot(chromaChannel2[1], weights[1]),"
|
||||
"dot(chromaChannel2[2], weights[2])"
|
||||
"), vec3(1.0))"
|
||||
");"
|
||||
|
||||
"vec3 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 3.0, 3.0);"
|
||||
"fragColour = lumaChromaToRGB * lumaChromaColourInRange;"
|
||||
"}", false, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_luma_filter_shader()
|
||||
{
|
||||
return make_shader(
|
||||
"#version 150\n"
|
||||
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"uniform vec4 weights[3];"
|
||||
|
||||
"out vec3 fragColour;"
|
||||
|
||||
"uniform sampler2D texID;"
|
||||
"uniform mat3 lumaChromaToRGB;"
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec3 samples[] = vec3[]("
|
||||
"texture(texID, inputPositionsVarying[0]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[1]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[2]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[3]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[4]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[5]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[6]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[7]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[8]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[9]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[10]).rgb"
|
||||
");"
|
||||
|
||||
"vec4 luminance[] = vec4[]("
|
||||
"vec4(samples[0].r, samples[1].r, samples[2].r, samples[3].r),"
|
||||
"vec4(samples[4].r, samples[5].r, samples[6].r, samples[7].r),"
|
||||
"vec4(samples[8].r, samples[9].r, samples[10].r, 0.0)"
|
||||
");"
|
||||
|
||||
"fragColour = vec3("
|
||||
"dot(vec3("
|
||||
"dot(luminance[0], weights[0]),"
|
||||
"dot(luminance[1], weights[1]),"
|
||||
"dot(luminance[2], weights[2])"
|
||||
"), vec3(1.0)),"
|
||||
"samples[5].gb"
|
||||
");"
|
||||
"}", false, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_filter_shader()
|
||||
{
|
||||
return make_shader(
|
||||
@ -414,7 +459,7 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
|
||||
void IntermediateShader::set_separation_frequency(float sampling_rate, float colour_burst_frequency)
|
||||
{
|
||||
// TODO: apply separately-formed filters for luminance and chrominance
|
||||
set_filter_coefficients(sampling_rate, colour_burst_frequency - 50.0f);
|
||||
set_filter_coefficients(sampling_rate, colour_burst_frequency);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle)
|
||||
|
@ -44,6 +44,11 @@ public:
|
||||
*/
|
||||
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
|
||||
|
||||
/*!
|
||||
Constructs and returns an intermediate shader that will filter R while passing through G and B unchanged.
|
||||
*/
|
||||
static std::unique_ptr<IntermediateShader> make_luma_filter_shader();
|
||||
|
||||
/*!
|
||||
Constructs and returns an intermediate shader that will filter R, G and B.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user