1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-07 08:28:57 +00:00

Made an attempt to extract and consolidate the logic behind the intermediate shaders.

This commit is contained in:
Thomas Harte 2016-04-28 21:04:59 -04:00
parent ef3647f6cf
commit 8538b62ea2
3 changed files with 349 additions and 0 deletions

View File

@ -303,6 +303,7 @@
4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */; };
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */; };
4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; };
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */; };
4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99081C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp */; };
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; };
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; };
@ -656,6 +657,8 @@
4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clock_SignalUITests.swift; sourceTree = "<group>"; };
4BB73EC31B587A5100552FC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Clock Signal.entitlements"; sourceTree = "<group>"; };
4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = "<group>"; };
4BBB14301CD2CECE00BDB55C /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = "<group>"; };
4BBF99081C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTInputBufferBuilder.cpp; sourceTree = "<group>"; };
4BBF99091C8FBA6F0075DAFB /* CRTInputBufferBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTInputBufferBuilder.hpp; sourceTree = "<group>"; };
4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = "<group>"; };
@ -1227,6 +1230,8 @@
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */,
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */,
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */,
4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */,
4BBB14301CD2CECE00BDB55C /* IntermediateShader.hpp */,
);
path = Shaders;
sourceTree = "<group>";
@ -1664,6 +1669,7 @@
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */,
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */,
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */,
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,

View File

@ -0,0 +1,285 @@
//
// IntermediateShader.cpp
// Clock Signal
//
// Created by Thomas Harte on 28/04/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "IntermediateShader.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "../../../../SignalProcessing/FIRFilter.hpp"
using namespace OpenGL;
namespace {
const OpenGL::Shader::AttributeBinding bindings[] =
{
{"inputPosition", 0},
{"outputPosition", 1},
{"phaseAmplitudeAndOffset", 2},
{"phaseTime", 3},
{nullptr}
};
}
std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition)
{
const char *sampler_type = use_usampler ? "usampler2D" : "sampler2D";
const char *input_variable = input_is_inputPosition ? "inputPosition" : "outputPosition";
char *vertex_shader;
asprintf(&vertex_shader,
"#version 150\n"
"in vec2 inputPosition;"
"in vec2 outputPosition;"
"in vec3 phaseAmplitudeAndOffset;"
"in float phaseTime;"
"uniform float phaseCyclesPerTick;"
"uniform ivec2 outputTextureSize;"
"uniform float extension;"
"uniform %s texID;"
"out vec2 phaseAndAmplitudeVarying;"
"out vec2 inputPositionsVarying[11];"
"out vec2 iInputPositionVarying;"
"out vec2 delayLinePositionVarying;"
"void main(void)"
"{"
"vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (phaseAmplitudeAndOffset.z - 0.5);"
"vec2 extendedInputPosition = %s + extensionVector;"
"vec2 extendedOutputPosition = outputPosition + extensionVector;"
"vec2 textureSize = vec2(textureSize(texID, 0));"
"iInputPositionVarying = extendedInputPosition;"
"vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;"
"inputPositionsVarying[0] = mappedInputPosition - (vec2(10.0, 0.0) / textureSize);"
"inputPositionsVarying[1] = mappedInputPosition - (vec2(8.0, 0.0) / textureSize);"
"inputPositionsVarying[2] = mappedInputPosition - (vec2(6.0, 0.0) / textureSize);"
"inputPositionsVarying[3] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);"
"inputPositionsVarying[4] = mappedInputPosition - (vec2(2.0, 0.0) / textureSize);"
"inputPositionsVarying[5] = mappedInputPosition;"
"inputPositionsVarying[6] = mappedInputPosition + (vec2(2.0, 0.0) / textureSize);"
"inputPositionsVarying[7] = mappedInputPosition + (vec2(4.0, 0.0) / textureSize);"
"inputPositionsVarying[8] = mappedInputPosition + (vec2(6.0, 0.0) / textureSize);"
"inputPositionsVarying[9] = mappedInputPosition + (vec2(8.0, 0.0) / textureSize);"
"inputPositionsVarying[10] = mappedInputPosition + (vec2(10.0, 0.0) / textureSize);"
"delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);"
"phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAmplitudeAndOffset.x) * 2.0 * 3.141592654;"
"phaseAndAmplitudeVarying.y = 0.33;"
"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/textureSize;"
"gl_Position = vec4(eyePosition, 0.0, 1.0);"
"}", sampler_type, input_variable);
std::unique_ptr<IntermediateShader> shader = std::unique_ptr<IntermediateShader>(new IntermediateShader(vertex_shader, fragment_shader, bindings));
free(vertex_shader);
shader->texIDUniform = shader->get_uniform_location("texID");
shader->outputTextureSizeUniform = shader->get_uniform_location("outputTextureSize");
shader->phaseCyclesPerTickUniform = shader->get_uniform_location("phaseCyclesPerTick");
shader->extensionUniform = shader->get_uniform_location("extension");
shader->weightsUniform = shader->get_uniform_location("weights");
shader->rgbToLumaChromaUniform = shader->get_uniform_location("rgbToLumaChroma");
shader->lumaChromaToRGBUniform = shader->get_uniform_location("lumaChromaToRGB");
return shader;
}
std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_shader(const char *composite_shader, const char *rgb_shader)
{
char *composite_sample = (char *)composite_shader;
if(!composite_sample)
{
asprintf(&composite_sample,
"%s\n"
"uniform mat3 rgbToLumaChroma;"
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
"{"
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
"vec2 quadrature = vec2(cos(phase), -sin(phase)) * amplitude;"
"return dot(lumaChromaColour, vec3(1.0 - amplitude, quadrature));"
"}",
rgb_shader);
}
char *fragment_shader;
asprintf(&fragment_shader,
"#version 150\n"
"in vec2 inputPositionsVarying[11];"
"in vec2 iInputPositionVarying;"
"in vec2 phaseAndAmplitudeVarying;"
"out vec4 fragColour;"
"uniform usampler2D texID;"
"\n%s\n"
"void main(void)"
"{"
"fragColour = vec4(composite_sample(texID, inputPositionsVarying[5], iInputPositionVarying, phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y));"
"}"
, composite_sample);
if(!composite_shader) free(composite_sample);
std::unique_ptr<IntermediateShader> shader = make_shader(fragment_shader, true, true);
free(fragment_shader);
return shader;
}
std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separation_shader()
{
return make_shader(
"#version 150\n"
"in vec2 phaseAndAmplitudeVarying;"
"in vec2 inputPositionsVarying[11];"
"uniform vec4 weights[3];"
"out vec3 fragColour;"
"uniform sampler2D texID;"
"void main(void)"
"{"
"vec4 samples[3] = vec4[]("
"vec4("
"texture(texID, inputPositionsVarying[0]).r,"
"texture(texID, inputPositionsVarying[1]).r,"
"texture(texID, inputPositionsVarying[2]).r,"
"texture(texID, inputPositionsVarying[3]).r"
"),"
"vec4("
"texture(texID, inputPositionsVarying[4]).r,"
"texture(texID, inputPositionsVarying[5]).r,"
"texture(texID, inputPositionsVarying[6]).r,"
"texture(texID, inputPositionsVarying[7]).r"
"),"
"vec4("
"texture(texID, inputPositionsVarying[8]).r,"
"texture(texID, inputPositionsVarying[9]).r,"
"texture(texID, inputPositionsVarying[10]).r,"
"0.0"
")"
");"
"float luminance = "
"dot(vec3("
"dot(samples[0], weights[0]),"
"dot(samples[1], weights[1]),"
"dot(samples[2], weights[2])"
"), vec3(1.0)) / (1.0 - phaseAndAmplitudeVarying.y);"
"float chrominance = 0.5 * (samples[1].y - luminance) / phaseAndAmplitudeVarying.y;"
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), -sin(phaseAndAmplitudeVarying.x));"
"fragColour = vec3(luminance, vec2(0.5) + (chrominance * quadrature));"
"}",false, false);
}
std::unique_ptr<IntermediateShader> 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 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)"
");"
"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)"
");"
"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"
");"
"vec3 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 2.0, 2.0);"
"fragColour = lumaChromaToRGB * lumaChromaColourInRange;"
"}", false, false);
}
void IntermediateShader::set_output_size(unsigned int output_width, unsigned int output_height)
{
bind();
glUniform2i(outputTextureSizeUniform, (GLint)output_width, (GLint)output_height);
}
void IntermediateShader::set_source_texture_unit(GLenum unit)
{
bind();
glUniform1i(texIDUniform, (GLint)(unit - GL_TEXTURE0));
}
void IntermediateShader::set_filter_coefficients(float sampling_rate, float cutoff_frequency)
{
bind();
float weights[12];
SignalProcessing::FIRFilter luminance_filter(11, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
luminance_filter.get_coefficients(weights);
glUniform4fv(weightsUniform, 3, weights);
}
void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle)
{
bind();
glUniform1f(phaseCyclesPerTickUniform, phase_cycles_per_sample);
glUniform1f(extensionUniform, extend_runs_to_full_cycle ? ceilf(1.0f / phase_cycles_per_sample) : 0.0f);
}
void IntermediateShader::set_colour_conversion_matrices(float *fromRGB, float *toRGB)
{
bind();
glUniformMatrix3fv(lumaChromaToRGBUniform, 1, GL_FALSE, toRGB);
glUniformMatrix3fv(rgbToLumaChromaUniform, 1, GL_FALSE, fromRGB);
}

View File

@ -0,0 +1,58 @@
//
// 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 <stdio.h>
#include "Shader.hpp"
#include <memory>
namespace OpenGL {
class IntermediateShader: public Shader {
public:
using Shader::Shader;
static std::unique_ptr<IntermediateShader> make_source_conversion_shader(const char *composite_shader, const char *rgb_shader);
static std::unique_ptr<IntermediateShader> make_chroma_luma_separation_shader();
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
/*!
Binds this shader and configures it 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.
*/
void set_output_size(unsigned int output_width, unsigned int output_height);
/*!
Binds this shader and sets the texture unit (as an enum, e.g. `GL_TEXTURE0`) to sample as source data.
*/
void set_source_texture_unit(GLenum unit);
void set_filter_coefficients(float sampling_rate, float cutoff_frequency);
void set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle);
void set_colour_conversion_matrices(float *fromRGB, float *toRGB);
private:
static std::unique_ptr<IntermediateShader> make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition);
GLint texIDUniform;
GLint outputTextureSizeUniform;
GLint weightsUniform;
GLint phaseCyclesPerTickUniform;
GLint extensionUniform;
GLint rgbToLumaChromaUniform;
GLint lumaChromaToRGBUniform;
};
}
#endif /* IntermediateShader_hpp */