OpenGLRenderer + demo confusion

I’ve been trying to get the 3D OpenGL demos (OpenGLDemo, OpenGLAppDemo) working for a while now, and while they do work with their default setup, I can’t seem to get them working with the newer processor-editor class separation of JUCE plugins. I think this is probably just because I don’t know how OpenGLAppComponents/OpenGLRenderers work. My goal is to have an OpenGL context that renders on the main graphics thread, since I shouldn’t need a separate thread just for rendering the part of the UI with the context on it. So:

  1. If i need to make my own vao’s, vbo’s, textures, etc. do I need an OpenGLRenderer / OpenGLAppComponent? If not, where do I initialize everything?

  2. If I do need a renderer, can I get it to run on the main graphics thread, i.e. is there a way to get around making a new thread just for running the render() call on it?

  3. Does anyone have a working example of something like the OpenGLDemo, but separated into a processor and editor class like modern JUCE plugins? (With an AudioProcessor and AudioProcessorEditor)

I feel like there’s something I’m fundamentally not understanding here, so if anyone has any idea where my confusion might be coming from I appreciate the help.

You shouldn’t need to do anything in the processor, it’s got nothing to do with your GUI.

You need a class that inherits from Component as well as OpenGLRenderer. You then need to make an OpenGLContext member variable in that class and use its attach() mathod to attach it to your Component/Renderer class.

The rendering method will be called for you so you don’t ever need to call it manually.

The reason for my confusion is that I tried attaching the OpenGLAppDemo example onto the editor class as a component, but its render() method never got called (i checked with apitrace), while its paint function did. Am I missing something that I need to do to attach it properly? OpenGLDemo2D works fine since it doesn’t have a render() method.

The OpenGLAppComponent class is designed for making desktop (and I assume mobile) apps, not plugins.

The OpenGLRenderer class has 3 methods you need to override: newOpenGLContextCreated(), renderOpenGL(), and openGLContextClosing(). These are equivalent initialise(), render() and shutdown() in the OpenGLAppComponent.

Everything works !! Thank you

I made a simple as possible working example that renders a triangle that can be easily attached as a component, so noone should ever need to go through this confusion again:

#pragma once

#include "JuceHeader.h"

#define GLM_FORCE_CTOR_INIT
#include "glm/glm.hpp"

#include <vector>


class JUCEOpenGLTemplate  : public Component,
                            public OpenGLRenderer
{
public:
    JUCEOpenGLTemplate()
    {
        setOpaque (true);

        if (auto* peer = getPeer())
            peer->setCurrentRenderingEngine (0);

        openGLContext.setRenderer(this);
        openGLContext.attachTo(*this);
        //openGLContext.setContinuousRepainting(true);

        addAndMakeVisible (statusLabel);
        statusLabel.setJustificationType (Justification::bottomLeft);
        statusLabel.setFont (Font (18.0f));
        statusLabel.setColour(juce::Label::textColourId, juce::Colour(255, 0, 0));

        setSize (500, 500);

        lastError = "Context initialized successfully.";
    }

    ~JUCEOpenGLTemplate() override
    {
        openGLContext.detach();
    }


    void newOpenGLContextCreated(){
        bool result = true;

        openGLContext.extensions.glGenBuffers (1, &vertexBuffer);
        openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
        std::vector<glm::vec2> tris = getTriangle();
        openGLContext.extensions.glBufferData (GL_ARRAY_BUFFER, tris.size() * (int) sizeof (glm::vec2),
                                                       tris.data(), GL_STATIC_DRAW);

        shaderTest.reset(new OpenGLShaderProgram(openGLContext));
        result &= shaderTest->addVertexShader(getVertexShader());
        result &= shaderTest->addFragmentShader(getFragmentShader());
        result &= shaderTest->link();

        if( !result ){
            lastError = shaderTest->getLastError();
            shaderTest.reset();
            return;
        }


    }

    void openGLContextClosing(){
        openGLContext.extensions.glDeleteBuffers (1, &vertexBuffer);
        shaderTest.reset();
    }


    // First thing to render on a new frame, should be using openGLContext
    void renderOpenGL(){
        jassert (OpenGLHelpers::isContextActive());

        glClear(GL_COLOR_BUFFER_BIT);

        openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);

        shaderTest->use();

        //VAO stuff (Not sure if we can make our own?)
        openGLContext.extensions.glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, sizeof (glm::vec2), nullptr);
        openGLContext.extensions.glEnableVertexAttribArray (0);

        glDrawArrays(GL_TRIANGLES,0,getTriangle().size());

        openGLContext.extensions.glDisableVertexAttribArray (0);
    }

    // Second thing to render
    void paint (Graphics& g) override
    {
        statusLabel.setText(lastError, dontSendNotification);
    }

    void resized() override
    {
        auto area = getLocalBounds();

        statusLabel.setBounds (area.removeFromBottom (200));
    }


private:
    Label statusLabel;
    String lastError;
    OpenGLContext openGLContext;
    
    GLuint vertexBuffer;
    std::unique_ptr<OpenGLShaderProgram> shaderTest;

    static char* getVertexShader(){
        return "#version 330 core\n"
            "layout (location = 0) in vec2 aPos;"
            "void main(){"
            "   gl_Position = vec4(aPos,0,1);"
            "}";
    }

    static char* getFragmentShader(){
        return "#version 330 core\n"
            "void main(){"
            "   gl_FragColor = vec4(1);"
            "}";
    }

    static std::vector<glm::vec2> getTriangle(){
        return std::vector<glm::vec2>({
            {0.f,0.f},
            {1.f,0.f},
            {1.f,1.f}
        });
    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JUCEOpenGLTemplate)
};