diff --git a/Resources/oscilloscope/vector_display.jpg b/Resources/oscilloscope/vector_display.jpg new file mode 100644 index 0000000..12f1c6a Binary files /dev/null and b/Resources/oscilloscope/vector_display.jpg differ diff --git a/Source/visualiser/LineFragmentShader.glsl b/Source/visualiser/LineFragmentShader.glsl index d2d900d..e87a414 100644 --- a/Source/visualiser/LineFragmentShader.glsl +++ b/Source/visualiser/LineFragmentShader.glsl @@ -7,8 +7,10 @@ std::string lineFragmentShader = R"( uniform float uSize; uniform float uIntensity; -uniform float uOffset; -uniform float uScale; +uniform vec2 uOffset; +uniform vec2 uScale; +uniform float uScreenType; +uniform float uFishEye; uniform sampler2D uScreen; varying float vSize; varying vec4 uvl; @@ -29,7 +31,15 @@ float erf(float x) { void main() { - vec2 texCoord = (vTexCoord - 0.5) / uScale + 0.5 + uOffset; + // fish eye distortion + vec2 uv = vTexCoord - vec2(0.5); + float uva = atan(uv.x, uv.y); + float uvd = sqrt(dot(uv, uv)); + uvd = uvd * (1.0 - uFishEye * uvd * uvd); + vec2 texCoord = vec2(0.5) + vec2(sin(uva), cos(uva)) * uvd; + + texCoord = (texCoord - uOffset - 0.5) * uScale + 0.5; + float len = uvl.z; vec2 xy = uvl.xy; float brightness; @@ -45,6 +55,7 @@ void main() { } brightness *= uvl.w; + gl_FragColor = 2.0 * texture2D(uScreen, texCoord) * brightness; gl_FragColor.a = 1.0; } diff --git a/Source/visualiser/OutputFragmentShader.glsl b/Source/visualiser/OutputFragmentShader.glsl index 01c1d32..e72be8b 100644 --- a/Source/visualiser/OutputFragmentShader.glsl +++ b/Source/visualiser/OutputFragmentShader.glsl @@ -10,6 +10,7 @@ uniform float uNoise; uniform float uTime; uniform float uGlow; uniform float uAmbient; +uniform float uFishEye; uniform float uRealScreen; uniform vec2 uOffset; uniform vec2 uScale; @@ -29,11 +30,19 @@ float noise(in vec2 uv, in float time) { void main() { vec2 linePos = (vTexCoordCanvas - 0.5) / uScale + 0.5 + uOffset; + + // fish eye distortion + vec2 uv = linePos - vec2(0.5); + float uva = atan(uv.x, uv.y); + float uvd = sqrt(dot(uv, uv)); + uvd = uvd * (1.0 + uFishEye * uvd * uvd); + linePos = vec2(0.5) + vec2(sin(uva), cos(uva)) * uvd; + vec4 line = texture2D(uTexture0, linePos); // r components have grid; g components do not. vec4 screen = texture2D(uTexture3, vTexCoord); vec4 tightGlow = texture2D(uTexture1, linePos); - vec4 scatter = texture2D(uTexture2, linePos) + (1.0 - uRealScreen) * max(uAmbient - 0.45, 0.0); + vec4 scatter = texture2D(uTexture2, linePos) + (1.0 - uRealScreen) * max(uAmbient - 0.35, 0.0); float light = line.r + uGlow * 1.5 * screen.g * screen.g * tightGlow.r; light += uGlow * 0.3 * scatter.g * (2.0 + 1.0 * screen.g + 0.5 * screen.r); float tlight = 1.0-pow(2.0, -uExposure*light); diff --git a/Source/visualiser/VisualiserComponent.cpp b/Source/visualiser/VisualiserComponent.cpp index bd6ed8f..2cf9e2c 100644 --- a/Source/visualiser/VisualiserComponent.cpp +++ b/Source/visualiser/VisualiserComponent.cpp @@ -783,10 +783,10 @@ void VisualiserComponent::drawLine(const std::vector& xPoints, const std: lineShader->setUniform("uFadeAmount", fadeAmount); lineShader->setUniform("uNEdges", (GLfloat) nEdges); - OsciPoint offset = settings.getScreenType() == ScreenType::Real ? REAL_SCREEN_OFFSET : OsciPoint(); - OsciPoint scale = settings.getScreenType() == ScreenType::Real ? REAL_SCREEN_SCALE : OsciPoint(1); - lineShader->setUniform("uOffset", (float) offset.x, (float) offset.y); - lineShader->setUniform("uScale", (float) scale.x, (float) scale.y); + lineShader->setUniform("uScreenType", (GLfloat) screenType); + lineShader->setUniform("uFishEye", screenType == ScreenType::VectorDisplay ? VECTOR_DISPLAY_FISH_EYE : 0.0f); + setOffsetAndScale(lineShader.get()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexIndexBuffer); int nEdgesThisTime = xPoints.size() - 1; @@ -860,17 +860,32 @@ void VisualiserComponent::drawCRT() { outputShader->setUniform("uTime", time); outputShader->setUniform("uGlow", (float) settings.getGlow()); outputShader->setUniform("uAmbient", (float) settings.getAmbient()); - OsciPoint offset = settings.getScreenType() == ScreenType::Real ? REAL_SCREEN_OFFSET : OsciPoint(); - OsciPoint scale = settings.getScreenType() == ScreenType::Real ? REAL_SCREEN_SCALE : OsciPoint(1); - outputShader->setUniform("uOffset", (float) offset.x, (float) offset.y); - outputShader->setUniform("uScale", (float) scale.x, (float) scale.y); - outputShader->setUniform("uRealScreen", settings.getScreenType() == ScreenType::Real ? 1.0f : 0.0f); + outputShader->setUniform("uFishEye", screenType == ScreenType::VectorDisplay ? VECTOR_DISPLAY_FISH_EYE : 0.0f); + setOffsetAndScale(outputShader.get()); + outputShader->setUniform("uRealScreen", settings.parameters.screenType->isRealisticDisplay() ? 1.0f : 0.0f); outputShader->setUniform("uResizeForCanvas", lineTexture.width / 1024.0f); juce::Colour colour = juce::Colour::fromHSV(settings.getHue() / 360.0f, 1.0, 1.0, 1.0); outputShader->setUniform("uColour", colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue()); drawTexture(lineTexture, blur1Texture, blur3Texture, screenTexture); } +void VisualiserComponent::setOffsetAndScale(juce::OpenGLShaderProgram* shader) { + OsciPoint offset; + OsciPoint scale; + if (settings.getScreenType() == ScreenType::Real) { + offset = REAL_SCREEN_OFFSET; + scale = REAL_SCREEN_SCALE; + } else if (settings.getScreenType() == ScreenType::VectorDisplay) { + offset = VECTOR_DISPLAY_OFFSET; + scale = VECTOR_DISPLAY_SCALE; + } else { + scale = { 1.0f }; + } + shader->setUniform("uOffset", (float) offset.x, (float) offset.y); + checkGLErrors("test 4"); + shader->setUniform("uScale", (float) scale.x, (float) scale.y); +} + Texture VisualiserComponent::createScreenTexture() { using namespace juce::gl; @@ -878,6 +893,8 @@ Texture VisualiserComponent::createScreenTexture() { screenOpenGLTexture.loadImage(screenTextureImage); } else if (screenType == ScreenType::Real) { screenOpenGLTexture.loadImage(oscilloscopeImage); + } else if (screenType == ScreenType::VectorDisplay) { + screenOpenGLTexture.loadImage(vectorDisplayImage); } else { screenOpenGLTexture.loadImage(emptyScreenImage); } diff --git a/Source/visualiser/VisualiserComponent.h b/Source/visualiser/VisualiserComponent.h index af27bb5..2e6f4d3 100644 --- a/Source/visualiser/VisualiserComponent.h +++ b/Source/visualiser/VisualiserComponent.h @@ -173,10 +173,15 @@ private: juce::Image screenTextureImage = juce::ImageFileFormat::loadFrom(BinaryData::noise_jpg, BinaryData::noise_jpgSize); juce::Image emptyScreenImage = juce::ImageFileFormat::loadFrom(BinaryData::empty_jpg, BinaryData::empty_jpgSize); juce::Image oscilloscopeImage = juce::ImageFileFormat::loadFrom(BinaryData::real_jpg, BinaryData::real_jpgSize); + juce::Image vectorDisplayImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_jpg, BinaryData::vector_display_jpgSize); OsciPoint REAL_SCREEN_OFFSET = { 0.02, -0.15 }; OsciPoint REAL_SCREEN_SCALE = { 0.6 }; + OsciPoint VECTOR_DISPLAY_OFFSET = { 0.075, -0.045 }; + OsciPoint VECTOR_DISPLAY_SCALE = { 0.6 }; + float VECTOR_DISPLAY_FISH_EYE = 0.5; + Texture screenTexture; std::optional targetTexture = std::nullopt; @@ -198,6 +203,7 @@ private: chowdsp::ResamplingTypes::LanczosResampler<2048, 8> yResampler; chowdsp::ResamplingTypes::LanczosResampler<2048, 8> zResampler; + void setOffsetAndScale(juce::OpenGLShaderProgram* shader); void initialiseSharedTexture(); void closeSharedTexture(); Texture makeTexture(int width, int height); diff --git a/Source/visualiser/VisualiserSettings.h b/Source/visualiser/VisualiserSettings.h index b3b437a..2c8e610 100644 --- a/Source/visualiser/VisualiserSettings.h +++ b/Source/visualiser/VisualiserSettings.h @@ -14,12 +14,13 @@ enum class ScreenType : int { Graticule = 2, Smudged = 3, SmudgedGraticule = 4, - Real = 5 + Real = 5, + VectorDisplay = 6, }; class ScreenTypeParameter : public IntParameter { public: - ScreenTypeParameter(juce::String name, juce::String id, int versionHint, ScreenType value) : IntParameter(name, id, versionHint, (int) value, 1, 5) {} + ScreenTypeParameter(juce::String name, juce::String id, int versionHint, ScreenType value) : IntParameter(name, id, versionHint, (int) value, 1, 6) {} juce::String getText(float value, int maximumStringLength = 100) const override { switch ((ScreenType)(int)getUnnormalisedValue(value)) { @@ -33,6 +34,8 @@ public: return "Smudged Graticule"; case ScreenType::Real: return "Real Oscilloscope"; + case ScreenType::VectorDisplay: + return "Vector Display"; default: return "Unknown"; } @@ -50,6 +53,8 @@ public: unnormalisedValue = (int)ScreenType::SmudgedGraticule; } else if (text == "Real Oscilloscope") { unnormalisedValue = (int)ScreenType::Real; + } else if (text == "Vector Display") { + unnormalisedValue = (int)ScreenType::VectorDisplay; } else { unnormalisedValue = (int)ScreenType::Empty; } @@ -63,6 +68,11 @@ public: void load(juce::XmlElement* xml) { setValueNotifyingHost(getValueForText(xml->getStringAttribute("screenType"))); } + + bool isRealisticDisplay() { + ScreenType type = (ScreenType)(int)getValueUnnormalised(); + return type == ScreenType::Real || type == ScreenType::VectorDisplay; + } }; class VisualiserParameters { @@ -133,7 +143,7 @@ public: "Ambient Light", "Controls how much ambient light is added to the oscilloscope display.", "ambient", - VERSION_HINT, 0.8, 0.0, 5.0 + VERSION_HINT, 0.7, 0.0, 5.0 ) ); std::shared_ptr smoothEffect = std::make_shared( diff --git a/osci-render.jucer b/osci-render.jucer index 6bda7a9..1bebd9b 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -35,6 +35,8 @@ + diff --git a/sosci.jucer b/sosci.jucer index e0c3111..8819217 100644 --- a/sosci.jucer +++ b/sosci.jucer @@ -23,6 +23,9 @@ + +