diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 349708700..d9bc03a70 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1369,10 +1369,6 @@ 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; 4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; - 4BD191E0219113B80042E144 /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = ""; }; - 4BD191E1219113B80042E144 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; - 4BD191E3219113B80042E144 /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; - 4BD191E4219113B80042E144 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; 4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; 4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = ""; }; 4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = ""; }; @@ -3031,23 +3027,11 @@ 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, 4BD424DC2193B5340097291A /* Primitives */, - 4BD191DF219113B80042E144 /* Shaders */, ); name = OpenGL; path = ../../Outputs/OpenGL; sourceTree = ""; }; - 4BD191DF219113B80042E144 /* Shaders */ = { - isa = PBXGroup; - children = ( - 4BD191E3219113B80042E144 /* IntermediateShader.cpp */, - 4BD191E1219113B80042E144 /* OutputShader.cpp */, - 4BD191E0219113B80042E144 /* IntermediateShader.hpp */, - 4BD191E4219113B80042E144 /* OutputShader.hpp */, - ); - path = Shaders; - sourceTree = ""; - }; 4BD388431FE34E060042B588 /* Implementation */ = { isa = PBXGroup; children = ( diff --git a/Outputs/OpenGL/Shaders/IntermediateShader.cpp b/Outputs/OpenGL/Shaders/IntermediateShader.cpp deleted file mode 100644 index af90e0b43..000000000 --- a/Outputs/OpenGL/Shaders/IntermediateShader.cpp +++ /dev/null @@ -1,435 +0,0 @@ -// -// IntermediateShader.cpp -// Clock Signal -// -// Created by Thomas Harte on 28/04/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#include "IntermediateShader.hpp" - -#include -#include -#include - -#include "../../../SignalProcessing/FIRFilter.hpp" - -using namespace OpenGL; - -std::string IntermediateShader::get_input_name(Input input) { - switch(input) { - case Input::InputStart: return "inputStart"; - case Input::OutputStart: return "outputStart"; - case Input::Ends: return "ends"; - case Input::PhaseTimeAndAmplitude: return "phaseTimeAndAmplitude"; - - // Intended to be unreachable. - default: assert(false); return ""; - } -} - -std::unique_ptr IntermediateShader::make_shader(const std::string &fragment_shader, bool use_usampler, bool input_is_inputPosition) { - std::ostringstream vertex_shader; - vertex_shader << - "#version 150\n" - - "in vec2 " << get_input_name(Input::InputStart) << ";" - "in vec2 " << get_input_name(Input::OutputStart) << ";" - "in vec2 " << get_input_name(Input::Ends) << ";" - "in vec3 " << get_input_name(Input::PhaseTimeAndAmplitude) << ";" - - "uniform ivec2 outputTextureSize;" - "uniform float extension;" - "uniform " << (use_usampler ? "usampler2D" : "sampler2D") << " texID;" - "uniform float offsets[5];" - "uniform vec2 widthScalers;" - "uniform float inputVerticalOffset;" - "uniform float outputVerticalOffset;" - "uniform float textureHeightDivisor;" - - "out vec3 phaseAndAmplitudeVarying;" - "out vec2 inputPositionsVarying[11];" - "out vec2 delayLinePositionVarying;" - - "void main(void)" - "{" - // odd vertices are on the left, even on the right - "float extent = float(gl_VertexID & 1);" - "float longitudinal = float((gl_VertexID & 2) >> 1);" - - // inputPosition.x is either inputStart.x or ends.x, depending on whether it is on the left or the right; - // outputPosition.x is either outputStart.x or ends.y; - // .ys are inputStart.y and outputStart.y respectively - "vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent)*widthScalers[0], inputStart.y + inputVerticalOffset);" - "vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent)*widthScalers[1], outputStart.y + outputVerticalOffset);" - - "inputPosition.y += longitudinal;" - "outputPosition.y += longitudinal;" - - // extension is the amount to extend both the input and output by to add a full colour cycle at each end - "vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (extent - 0.5);" - - // extended[Input/Output]Position are [input/output]Position with the necessary applied extension - "vec2 extendedInputPosition = " << (input_is_inputPosition ? "inputPosition" : "outputPosition") << " + extensionVector;" - "vec2 extendedOutputPosition = outputPosition + extensionVector;" - - // scale mappedInputPosition to the ordinary normalised range - "vec2 textureSize = vec2(textureSize(texID, 0));" - "vec2 mappedInputPosition = extendedInputPosition / textureSize;" - - // setup input positions spaced as per the supplied offsets; these are for filtering where required - "inputPositionsVarying[0] = mappedInputPosition - (vec2(5.0, 0.0) / textureSize);" - "inputPositionsVarying[1] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);" - "inputPositionsVarying[2] = mappedInputPosition - (vec2(3.0, 0.0) / textureSize);" - "inputPositionsVarying[3] = mappedInputPosition - (vec2(2.0, 0.0) / textureSize);" - "inputPositionsVarying[4] = mappedInputPosition - (vec2(1.0, 0.0) / textureSize);" - "inputPositionsVarying[5] = mappedInputPosition;" - "inputPositionsVarying[6] = mappedInputPosition + (vec2(1.0, 0.0) / textureSize);" - "inputPositionsVarying[7] = mappedInputPosition + (vec2(2.0, 0.0) / textureSize);" - "inputPositionsVarying[8] = mappedInputPosition + (vec2(3.0, 0.0) / textureSize);" - "inputPositionsVarying[9] = mappedInputPosition + (vec2(4.0, 0.0) / textureSize);" - "inputPositionsVarying[10] = mappedInputPosition + (vec2(5.0, 0.0) / textureSize);" - "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" - - // setup phaseAndAmplitudeVarying.x as colour burst subcarrier phase, in radians; - // setup phaseAndAmplitudeVarying.y as colour burst amplitude; - // setup phaseAndAmplitudeVarying.z as 1 / abs(colour burst amplitude), or 0.0 if amplitude is 0.0; - "phaseAndAmplitudeVarying.x = (extendedOutputPosition.x + (phaseTimeAndAmplitude.x / 64.0)) * 0.5 * 3.141592654;" - "phaseAndAmplitudeVarying.y = (phaseTimeAndAmplitude.y - 128) / 127.0;" - "phaseAndAmplitudeVarying.z = (abs(phaseAndAmplitudeVarying.y) > 0.05) ? 1.0 / abs(phaseAndAmplitudeVarying.y) : 0.0;" - - // determine output position by scaling the output position according to the texture size - "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0);" - "gl_Position = vec4(eyePosition, 0.0, 1.0);" - "}"; - - return std::unique_ptr(new IntermediateShader(vertex_shader.str(), fragment_shader, { - {get_input_name(Input::InputStart), 0}, - {get_input_name(Input::OutputStart), 1}, - {get_input_name(Input::Ends), 2}, - {get_input_name(Input::PhaseTimeAndAmplitude), 3} - })); -} - -std::unique_ptr IntermediateShader::make_composite_source_shader(const std::string &composite_shader, const std::string &svideo_shader, const std::string &rgb_shader) { - std::ostringstream fragment_shader; - fragment_shader << - "#version 150\n" - - "in vec2 inputPositionsVarying[11];" - "in vec3 phaseAndAmplitudeVarying;" - - "out vec4 fragColour;" - - "uniform usampler2D texID;" - << composite_shader; - - if(composite_shader.empty()) { - if(!svideo_shader.empty()) { - fragment_shader << - svideo_shader << - "float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "vec2 svideoColour = svideo_sample(texID, coordinate, phase, amplitude);" - "return mix(svideoColour.x, svideoColour.y, abs(amplitude));" - "}"; - } else { - fragment_shader << - rgb_shader << - "uniform mat3 rgbToLumaChroma;" - "float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "vec3 rgbColour = clamp(rgb_sample(texID, coordinate), vec3(0.0), vec3(1.0));" - "vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;" - "vec2 quadrature = vec2(cos(phase), sin(phase)) * vec2(abs(amplitude), amplitude);" - "return dot(lumaChromaColour, vec3(1.0 - abs(amplitude), quadrature));" - "}"; - } - } - - fragment_shader << - "void main(void)" - "{" - "fragColour = vec4(composite_sample(texID, inputPositionsVarying[5], phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y));" - "}"; - - return make_shader(fragment_shader.str(), true, true); -} - -std::unique_ptr IntermediateShader::make_svideo_source_shader(const std::string &svideo_shader, const std::string &rgb_shader) { - std::ostringstream fragment_shader; - fragment_shader << - "#version 150\n" - - "in vec2 inputPositionsVarying[11];" - "in vec3 phaseAndAmplitudeVarying;" - - "out vec3 fragColour;" - - "uniform usampler2D texID;" - << svideo_shader; - - if(svideo_shader.empty()) { - fragment_shader - << rgb_shader << - "uniform mat3 rgbToLumaChroma;" - "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "vec3 rgbColour = clamp(rgb_sample(texID, coordinate), vec3(0.0), vec3(1.0));" - "vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;" - "vec2 quadrature = vec2(cos(phase), sin(phase)) * vec2(1.0, sign(amplitude));" - "return vec2(lumaChromaColour.x, 0.5 + dot(quadrature, lumaChromaColour.yz) * 0.5);" - "}"; - } - - fragment_shader << - "void main(void)" - "{" - "vec2 sample = svideo_sample(texID, inputPositionsVarying[5], phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y);" - "vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), sin(phaseAndAmplitudeVarying.x)) * vec2(1.0, sign(phaseAndAmplitudeVarying.y)) * 0.5 * phaseAndAmplitudeVarying.z;" - "fragColour = vec3(sample.x, vec2(0.5) + (sample.y * quadrature));" - "}"; - - return make_shader(fragment_shader.str(), true, true); -} - -std::unique_ptr IntermediateShader::make_rgb_source_shader(const std::string &rgb_shader) { - std::ostringstream fragment_shader; - fragment_shader << - "#version 150\n" - - "in vec2 inputPositionsVarying[11];" - "in vec3 phaseAndAmplitudeVarying;" - - "out vec3 fragColour;" - - "uniform usampler2D texID;" - - << rgb_shader << - - "void main(void)" - "{" - "fragColour = rgb_sample(texID, inputPositionsVarying[5]);" - "}"; - - return make_shader(fragment_shader.str(), true, true); -} - -std::unique_ptr IntermediateShader::make_chroma_luma_separation_shader() { - return make_shader( - "#version 150\n" - - "in vec3 phaseAndAmplitudeVarying;" - "in vec2 inputPositionsVarying[11];" - - "out vec3 fragColour;" - - "uniform sampler2D texID;" - - "void main(void)" - "{" - "vec4 samples = vec4(" - "texture(texID, inputPositionsVarying[3]).r," - "texture(texID, inputPositionsVarying[4]).r," - "texture(texID, inputPositionsVarying[5]).r," - "texture(texID, inputPositionsVarying[6]).r" - ");" - // calculate luminance as either the straight average of the samples, if a colour subcarrier - // was present, or else a weighted sample around the third sample if not. - "float luminance = mix(dot(samples, vec4(0.25)), dot(samples, vec4(0.0, 0.16, 0.66, 0.16)), step(phaseAndAmplitudeVarying.z, 0.0));" - - // define chroma to be whatever was here, minus luma - "float chrominance = 0.5 * (samples.z - luminance) * phaseAndAmplitudeVarying.z;" - - // scale luminance up to the range [0, 1) - "luminance /= (1.0 - abs(phaseAndAmplitudeVarying.y));" - - // split choma colours here, as the most direct place, writing out - // RGB = (luma, chroma.x, chroma.y) - "vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), sin(phaseAndAmplitudeVarying.x)) * vec2(1.0, sign(phaseAndAmplitudeVarying.y));" - "fragColour = vec3(luminance, vec2(0.5) + (chrominance * quadrature));" - "}",false, false); -} - -std::unique_ptr IntermediateShader::make_chroma_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[3]).rgb," - "texture(texID, inputPositionsVarying[4]).rgb," - "texture(texID, inputPositionsVarying[5]).rgb," - "texture(texID, inputPositionsVarying[6]).rgb" - ");" - - "vec4 chromaChannel1 = vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g);" - "vec4 chromaChannel2 = vec4(samples[0].b, samples[1].b, samples[2].b, samples[3].b);" - - "vec3 lumaChromaColour = vec3(samples[2].r," - "dot(chromaChannel1, vec4(0.25))," - "dot(chromaChannel2, vec4(0.25))" - ");" - - "vec3 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 2.0, 2.0);" - "fragColour = lumaChromaToRGB * lumaChromaColourInRange;" - "}", false, false); -} - -std::unique_ptr IntermediateShader::make_rgb_filter_shader() { - return make_shader( - "#version 150\n" - - "in vec2 inputPositionsVarying[11];" - "uniform vec4 weights[3];" - - "out vec3 fragColour;" - - "uniform sampler2D texID;" - - "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 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(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)" - ");" - "vec4 channel3[] = 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)" - ");" - - "fragColour = vec3(" - "dot(vec3(" - "dot(channel1[0], weights[0])," - "dot(channel1[1], weights[1])," - "dot(channel1[2], weights[2])" - "), vec3(1.0))," - "dot(vec3(" - "dot(channel2[0], weights[0])," - "dot(channel2[1], weights[1])," - "dot(channel2[2], weights[2])" - "), vec3(1.0))," - "dot(vec3(" - "dot(channel3[0], weights[0])," - "dot(channel3[1], weights[1])," - "dot(channel3[2], weights[2])" - "), vec3(1.0))" - ");" - "}", false, false); -} - -void IntermediateShader::set_output_size(unsigned int output_width, unsigned int output_height) { - set_uniform("outputTextureSize", (GLint)output_width, (GLint)output_height); -} - -void IntermediateShader::set_source_texture_unit(GLenum unit) { - set_uniform("texID", (GLint)(unit - GL_TEXTURE0)); -} - -void IntermediateShader::set_filter_coefficients(float sampling_rate, float cutoff_frequency) { - // The process below: the source texture will have bilinear filtering enabled; so by - // sampling at non-integral offsets from the centre the shader can get a weighted sum - // of two source pixels, then scale that once, to do two taps per sample. However - // that works only if the two coefficients being joined have the same sign. So the - // number of usable taps is between 11 and 21 depending on the values that come out. - // Perform a linear search for the highest number of taps we can use with 11 samples. - GLfloat weights[12]; - GLfloat offsets[5]; - unsigned int taps = 11; -// unsigned int taps = 21; -// while(1) { -// float coefficients[21]; - SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation); - std::vector coefficients = luminance_filter.get_coefficients(); - -// int sample = 0; -// int c = 0; - std::memset(weights, 0, sizeof(float)*12); - std::memset(offsets, 0, sizeof(float)*5); - - unsigned int half_size = (taps >> 1); - for(unsigned int c = 0; c < taps; c++) { - if(c < 5) offsets[c] = (half_size - c); - weights[c] = coefficients[c]; - } -// break; - -// int halfSize = (taps >> 1); -// while(c < halfSize && sample < 5) { -// offsets[sample] = static_cast(halfSize - c); -// if((coefficients[c] < 0.0f) == (coefficients[c+1] < 0.0f) && c+1 < (taps >> 1)) { -// weights[sample] = coefficients[c] + coefficients[c+1]; -// offsets[sample] -= (coefficients[c+1] / weights[sample]); -// c += 2; -// } else { -// weights[sample] = coefficients[c]; -// c++; -// } -// sample ++; -// } -// if(c == halfSize) { // i.e. we finished combining inputs before we ran out of space -// weights[sample] = coefficients[c]; -// for(int c = 0; c < sample; c++) { -// weights[sample+c+1] = weights[sample-c-1]; -// } -// break; -// } -// taps -= 2; -// } - - set_uniform("weights", 4, 3, weights); - set_uniform("offsets", 1, 5, offsets); -} - -void IntermediateShader::set_separation_frequency(float sampling_rate, float colour_burst_frequency) { - set_filter_coefficients(sampling_rate, colour_burst_frequency); -} - -void IntermediateShader::set_extension(float extension) { - set_uniform("extension", extension); -} - -void IntermediateShader::set_colour_conversion_matrices(float *fromRGB, float *toRGB) { - set_uniform_matrix("lumaChromaToRGB", 3, false, toRGB); - set_uniform_matrix("rgbToLumaChroma", 3, false, fromRGB); -} - -void IntermediateShader::set_width_scalers(float input_scaler, float output_scaler) { - set_uniform("widthScalers", input_scaler, output_scaler); -} - -void IntermediateShader::set_is_double_height(bool is_double_height, float input_offset, float output_offset) { - set_uniform("textureHeightDivisor", is_double_height ? 2.0f : 1.0f); - set_uniform("inputVerticalOffset", input_offset); - set_uniform("outputVerticalOffset", output_offset); -} diff --git a/Outputs/OpenGL/Shaders/IntermediateShader.hpp b/Outputs/OpenGL/Shaders/IntermediateShader.hpp deleted file mode 100644 index d8c5e9699..000000000 --- a/Outputs/OpenGL/Shaders/IntermediateShader.hpp +++ /dev/null @@ -1,144 +0,0 @@ -// -// IntermediateShader.hpp -// Clock Signal -// -// Created by Thomas Harte on 28/04/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef IntermediateShader_hpp -#define IntermediateShader_hpp - -#include "Shader.hpp" - -#include -#include - -namespace OpenGL { - -class IntermediateShader: public Shader { -public: - using Shader::Shader; - - enum class Input { - /// Contains the 2d start position of this run's input data. - InputStart, - /// Contains the 2d start position of this run's output position. - OutputStart, - /// A 2d vector comprised of (the final x position for input, the final x position for output). - Ends, - /// A 3d vector recording the colour subcarrier's (phase, time, amplitude) at the start of this span of data. - PhaseTimeAndAmplitude - }; - - /*! - Obtains the name of a designated input. Designated inputs are guaranteed to have the same attribute location - across multiple instances of IntermediateShader. So binding a vertex array to these inputs for any instance of - IntermediateShader allows that array to work with all instances of IntermediateShader. - - @param input The input to query. - @returns The name used in this shader's source for the nominated input. - */ - static std::string get_input_name(Input input); - - /*! - Constructs and returns an intermediate shader that will take runs from the inputPositions, - converting them to single-channel composite values using @c composite_shader if non-empty - or a reference composite conversion of @c svideo_shader (first preference) or - @c rgb_shader (second preference) otherwise. - - [input format] => one-channel composite. - */ - static std::unique_ptr make_composite_source_shader(const std::string &composite_shader, const std::string &svideo_shader, const std::string &rgb_shader); - - /*! - Constructs and returns an intermediate shader that will take runs from the inputPositions, - converting them to two-channel svideo values using @c svideo_shader if non-empty - or a reference svideo conversion of @c rgb_shader otherwise. - - [input format] => three-channel Y, noisy (m, n). - */ - static std::unique_ptr make_svideo_source_shader(const std::string &svideo_shader, const std::string &rgb_shader); - - /*! - Constructs and returns an intermediate shader that will take runs from the inputPositions, - converting them to RGB values using @c rgb_shader. - - [input format] => three-channel RGB. - */ - static std::unique_ptr make_rgb_source_shader(const std::string &rgb_shader); - - /*! - Constructs and returns an intermediate shader that will read composite samples from the R channel, - filter then to obtain luminance, stored to R, and to separate out unfiltered chrominance, store to G and B. - - one-channel composite => three-channel Y, noisy (m, n). - */ - static std::unique_ptr make_chroma_luma_separation_shader(); - - /*! - Constructs and returns an intermediate shader that will pass R through unchanged while filtering G and B. - - three-channel Y, noisy (m, n) => three-channel RGB. - */ - static std::unique_ptr make_chroma_filter_shader(); - - /*! - Constructs and returns an intermediate shader that will filter R, G and B. - - three-channel RGB => frequency-limited three-channel RGB. - */ - static std::unique_ptr make_rgb_filter_shader(); - - /*! - Queues the configuration of this shader for output to an area of `output_width` and `output_height` pixels - to occur upon the next `bind`. - */ - void set_output_size(unsigned int output_width, unsigned int output_height); - - /*! - Queues setting the texture unit (as an enum, e.g. `GL_TEXTURE0`) for source data to occur upon the next `bind`. - */ - void set_source_texture_unit(GLenum unit); - - /*! - Queues setting filtering coefficients for a lowpass filter based on the cutoff frequency to occur upon the next `bind`. - */ - void set_filter_coefficients(float sampling_rate, float cutoff_frequency); - - /*! - Queues configuration of filtering to separate luminance and chrominance based on a colour - subcarrier of the given frequency to occur upon the next `bind`. - */ - void set_separation_frequency(float sampling_rate, float colour_burst_frequency); - - /*! - Queues setting of the number of colour phase cycles per sample, indicating whether output - geometry should be extended so that a complete colour cycle is included at both the beginning and end, - to occur upon the next `bind`. - */ - void set_extension(float extension); - - /*! - Queues setting the matrices that convert between RGB and chrominance/luminance to occur on the next `bind`. - */ - void set_colour_conversion_matrices(float *fromRGB, float *toRGB); - - /*! - Sets the proportions of the input and output areas that should be considered the whole width: 1.0 means use all available - space, 0.5 means use half, etc. - */ - void set_width_scalers(float input_scaler, float output_scaler); - - /*! - Sets source and target vertical offsets. - */ - void set_is_double_height(bool is_double_height, float input_offset = 0.0f, float output_offset = 0.0f); - -private: - static std::unique_ptr make_shader(const std::string &fragment_shader, bool use_usampler, bool input_is_inputPosition); -}; - -} - -#endif /* IntermediateShader_hpp */ diff --git a/Outputs/OpenGL/Shaders/OutputShader.cpp b/Outputs/OpenGL/Shaders/OutputShader.cpp deleted file mode 100644 index 2b48c04bf..000000000 --- a/Outputs/OpenGL/Shaders/OutputShader.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// -// OutputShader.cpp -// Clock Signal -// -// Created by Thomas Harte on 27/04/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#include "OutputShader.hpp" - -#include -#include -#include - -using namespace OpenGL; - -std::string OutputShader::get_input_name(Input input) { - switch(input) { - case Input::Horizontal: return "horizontal"; - case Input::Vertical: return "vertical"; - - // Intended to be unreachable. - default: assert(false); return ""; - } -} - -std::unique_ptr OutputShader::make_shader(const char *fragment_methods, const char *colour_expression, bool use_usampler) { - const std::string sampler_type = use_usampler ? "usampler2D" : "sampler2D"; - - std::ostringstream vertex_shader; - vertex_shader << - "#version 150\n" - - "in vec2 " << get_input_name(Input::Horizontal) << ";" - "in vec2 " << get_input_name(Input::Vertical) << ";" - - "uniform vec2 boundsOrigin;" - "uniform vec2 boundsSize;" - "uniform vec2 positionConversion;" - "uniform vec2 scanNormal;" - "uniform " << sampler_type << " texID;" - "uniform float inputScaler;" - "uniform int textureHeightDivisor;" - - "out float lateralVarying;" - "out vec2 srcCoordinatesVarying;" - "out vec2 iSrcCoordinatesVarying;" - - "void main(void)" - "{" - "float lateral = float(gl_VertexID & 1);" - "float longitudinal = float((gl_VertexID & 2) >> 1);" - "float x = mix(horizontal.x, horizontal.y, longitudinal);" - - "lateralVarying = lateral - 0.5;" - - "vec2 vSrcCoordinates = vec2(x, vertical.y);" - "ivec2 textureSize = textureSize(texID, 0) * ivec2(1, textureHeightDivisor);" - "iSrcCoordinatesVarying = vSrcCoordinates;" - "srcCoordinatesVarying = vec2(inputScaler * vSrcCoordinates.x / textureSize.x, (vSrcCoordinates.y + 0.5) / textureSize.y);" - "srcCoordinatesVarying.x = srcCoordinatesVarying.x - mod(srcCoordinatesVarying.x, 1.0 / textureSize.x);" - - "vec2 vPosition = vec2(x, vertical.x);" - "vec2 floatingPosition = (vPosition / positionConversion) + lateral * scanNormal;" - "vec2 mappedPosition = (floatingPosition - boundsOrigin) / boundsSize;" - "gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);" - "}"; - - std::ostringstream fragment_shader; - fragment_shader << - "#version 150\n" - - "in float lateralVarying;" - "in vec2 srcCoordinatesVarying;" - "in vec2 iSrcCoordinatesVarying;" - - "out vec4 fragColour;" - - "uniform " << sampler_type << " texID;" - "uniform float gamma;" - - << fragment_methods << - - "void main(void)" - "{" - "fragColour = vec4(pow(" << colour_expression << ", vec3(gamma)), 0.64);"//*cos(lateralVarying) - "}"; - - return std::unique_ptr(new OutputShader(vertex_shader.str(), fragment_shader.str(), { - {get_input_name(Input::Horizontal), 0}, - {get_input_name(Input::Vertical), 1} - })); -} - -void OutputShader::set_output_size(unsigned int output_width, unsigned int output_height, Outputs::Display::Rect visible_area) { - GLfloat outputAspectRatioMultiplier = (static_cast(output_width) / static_cast(output_height)) / (4.0f / 3.0f); - GLfloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * visible_area.size.width; - - left_extent_ = (-1.0f / outputAspectRatioMultiplier) / visible_area.size.width; - right_extent_ = (1.0f / outputAspectRatioMultiplier) / visible_area.size.width; - - visible_area.origin.x -= bonusWidth * 0.5f; - visible_area.size.width *= outputAspectRatioMultiplier; - - set_uniform("boundsOrigin", (GLfloat)visible_area.origin.x, (GLfloat)visible_area.origin.y); - set_uniform("boundsSize", (GLfloat)visible_area.size.width, (GLfloat)visible_area.size.height); -} - -float OutputShader::get_left_extent() { - return left_extent_; -} - -float OutputShader::get_right_extent() { - return right_extent_; -} - -void OutputShader::set_source_texture_unit(GLenum unit) { - set_uniform("texID", (GLint)(unit - GL_TEXTURE0)); -} - -void OutputShader::set_timing(unsigned int height_of_display, unsigned int cycles_per_line, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider) { - GLfloat scan_angle = atan2f(1.0f / static_cast(height_of_display), 1.0f); - GLfloat scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)}; - GLfloat multiplier = static_cast(cycles_per_line) / (static_cast(height_of_display) * static_cast(horizontal_scan_period)); - scan_normal[0] *= multiplier; - scan_normal[1] *= multiplier; - - set_uniform("scanNormal", scan_normal[0], scan_normal[1]); - set_uniform("positionConversion", (GLfloat)horizontal_scan_period, (GLfloat)vertical_scan_period / (GLfloat)vertical_period_divider); -} - -void OutputShader::set_gamma_ratio(float ratio) { - set_uniform("gamma", ratio); -} - -void OutputShader::set_input_width_scaler(float input_scaler) { - set_uniform("inputScaler", input_scaler); -} - -void OutputShader::set_origin_is_double_height(bool is_double_height) { - set_uniform("textureHeightDivisor", is_double_height ? 2 : 1); -} diff --git a/Outputs/OpenGL/Shaders/OutputShader.hpp b/Outputs/OpenGL/Shaders/OutputShader.hpp deleted file mode 100644 index 117f94638..000000000 --- a/Outputs/OpenGL/Shaders/OutputShader.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// -// OutputShader.hpp -// Clock Signal -// -// Created by Thomas Harte on 27/04/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef OutputShader_hpp -#define OutputShader_hpp - -#include "Shader.hpp" -#include "../../ScanTarget.hpp" -#include - -namespace OpenGL { - -class OutputShader: public Shader { -public: - enum class Input { - /// A 2d vector; the first element is the horizontal start of this scan, the second element is the end. - Horizontal, - /// A 2d vector; the first element is the vertical start of this scan, the second element is the end. - Vertical - }; - - /*! - Obtains the name of a designated input. Designated inputs are guaranteed to have the same attribute location - across multiple instances of OutputShader. So binding a vertex array to these inputs for any instance of - OutputShader allows that array to work with all instances of OutputShader. - - @param input The input to query. - @returns The name used in this shader's source for the nominated input. - */ - static std::string get_input_name(Input input); - - /*! - Constructs and returns an instance of OutputShader. OutputShaders are intended to read source data - from a texture and draw a single raster scan containing that data as output. - - Does not catch any of the exceptions potentially thrown by `Shader::Shader`. - - All instances of OutputShader are guaranteed to use the same attribute locations for their inputs. - - @param fragment_methods A block of code that will appear within the global area of the fragment shader. - - @param colour_expression An expression that should evaluate to a `vec3` indicating the colour at the current location. The - decision should be a function of the uniform `texID`, which will be either a `usampler2D` or a `sampler2D` as per the - `use_usampler` parameter, and the inputs `srcCoordinatesVarying` which is a location within the texture from which to - take the source value, and `iSrcCoordinatesVarying` which is a value proportional to `srcCoordinatesVarying` but scaled - so that one unit equals one source sample. - - @param use_usampler Dictates the type of the `texID` uniform; will be a `usampler2D` if this parameter is `true`, a - `sampler2D` otherwise. - - @returns an instance of OutputShader. - */ - static std::unique_ptr make_shader(const char *fragment_methods, const char *colour_expression, bool use_usampler); - using Shader::Shader; - - /*! - Queues configuration for output to an area of `output_width` and `output_height` pixels, ensuring - the largest possible drawing size that allows everything within `visible_area` to be visible, to - occur upon the next `bind`. - */ - void set_output_size(unsigned int output_width, unsigned int output_height, Outputs::Display::Rect visible_area); - - /*! - Queues setting of the texture unit (as an enum, e.g. `GL_TEXTURE0`) for source data upon the next `bind`. - */ - void set_source_texture_unit(GLenum unit); - - /*! - Queues configuring this shader's understanding of how to map from the source vertex stream to screen coordinates, - to occur upon the next `bind`. - */ - void set_timing(unsigned int height_of_display, unsigned int cycles_per_line, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider); - - /*! - */ - void set_origin_is_double_height(bool is_double_height); - - void set_gamma_ratio(float ratio); - - /*! - Sets the proportion of the input area that should be considered the whole width: 1.0 means use all available - space, 0.5 means use half, etc. - */ - void set_input_width_scaler(float input_scaler); - - /*! - @returns The location, in eye coordinates, of the left edge of the output area. - */ - float get_left_extent(); - - /*! - @returns The location, in eye coordinates, of the right edge of the output area. - */ - float get_right_extent(); - -private: - float left_extent_ = 0.0f; - float right_extent_ = 0.0f; -}; - -} - -#endif /* OutputShader_hpp */