Simple OpenGL does not output anything

I was making something using JUCE and NanoVG. Though the NanoVG works well, I failed to produce any custom OpenGL drawings (actually, I’m trying to make some screen-space effects using shader). So I made this minimum test program to check my bugs, and it is still not drawing anything.

The program is simple: render a full-red rectangle. There are no transform matrix, no light calculation, and no texture sampling.

Some info:

  • Environment (GPU driver, hardware) is OK, as NanoVG draws correctly.
  • Debugged using AMD CodeXL, bindings for program, VAO, *VBO, FrameBuffer are all correct.
  • And face culling, depth test, stencil test are all off.
  • And VBO contains correct content.

Several years ago I could write much more complicated OpenGL rendering programs, but nowadays I cannot make a single rectangle. This really depressed me a lot. Could anybody help me out of this hell?

#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_opengl/juce_opengl.h>

#if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC
#include <GL/glext.h>
#endif

#define INIT_W 400
#define INIT_H 400

using juce::CharPointer_UTF8;
using juce::String;

const char* vertex_src = R"(
#version 130
in vec3 in_pos;
out vec3 frag_pos;
out vec2 frag_tex_pos;
void main()
{
    frag_pos = in_pos;
    frag_tex_pos = in_pos.xy;
}
)";

const char* frag_src = R"(
#version 130
in vec3 frag_pos;
in vec2 frag_tex_pos;
void main()
{
    gl_FragColor = vec4(1, 0, 0, 1);
}
)";

class MyPanel : public juce::OpenGLAppComponent
{
public:
    MyPanel()
    {
        setSize( INIT_W, INIT_H );
    }
    ~MyPanel() override
    {
        shutdownOpenGL();
    }

    void initialise() override
    {
        auto gl = openGLContext.extensions;

        // create shader program
        shader.reset( new juce::OpenGLShaderProgram( openGLContext ) );
        shader->addVertexShader( vertex_src );
        shader->addFragmentShader( frag_src );
        shader->link();

        // create vertex buffer
        struct PostEffectVertex
        {
            float x, y, z;
        };
        PostEffectVertex post_effect_rect[4] = {
            { 0, 0, 0 },
            { 0, 1, 0 },
            { 1, 0, 0 },
            { 1, 1, 0 }
        };

        gl.glGenBuffers( 1, &vbo );
        gl.glBindBuffer( GL_ARRAY_BUFFER, vbo );
        gl.glBufferData( GL_ARRAY_BUFFER, sizeof( post_effect_rect ), post_effect_rect, GL_STATIC_DRAW );

        // create vertex array object, bind attributes
        gl.glGenVertexArrays( 1, &vao );
        gl.glBindVertexArray( vao );

        juce::OpenGLShaderProgram::Attribute shader_input( *shader.get(), "in_pos" );
        gl.glEnableVertexAttribArray( shader_input.attributeID );
        gl.glVertexAttribPointer( shader_input.attributeID, sizeof(PostEffectVertex)/sizeof(float), GL_FLOAT, GL_FALSE, sizeof( PostEffectVertex ), 0 );
    }

    void render() override
    {
        auto gl = openGLContext.extensions;
        glClearColor( 0.7, 0.8, 1, 1 );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );

        shader->use();
        gl.glBindVertexArray( vao );
        gl.glBindBuffer( GL_ARRAY_BUFFER, vbo );

        glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
        glFinish();
    }

    void shutdown() override
    {
        auto gl = openGLContext.extensions;
        shader.reset();
        gl.glDeleteBuffers( 1, &vbo );
        gl.glDeleteVertexArrays( 1, &vao );
    }

    std::unique_ptr<juce::OpenGLShaderProgram> shader;

    GLuint vbo = 0;
    GLuint vao = 0;
};

class MyMW : public juce::DocumentWindow
{
public:
    MyMW( const String& name )
        : juce::DocumentWindow( name, juce::Colours::white, juce::DocumentWindow::allButtons )
    {
        setContentOwned( new MyPanel, true );
    }

    void closeButtonPressed() override
    {
        juce::JUCEApplicationBase::quit();
    }
};

class SimpleRenderingApp : public juce::JUCEApplication
{
public:
    const String getApplicationName() override { return "GL drawing"; }
    const String getApplicationVersion() override { return "0.0.0"; }
    void initialise( const String& ) override;
    void shutdown() override {}

    std::unique_ptr<MyMW> mw;
};

void SimpleRenderingApp::initialise( const String& )
{
    mw.reset( new MyMW( getApplicationName() ) );
    mw->addToDesktop();
    mw->setResizable( true, false );
    mw->setVisible( true );
}

START_JUCE_APPLICATION( SimpleRenderingApp );

An observation: Almost all JUCE OpenGL shader/vertex code I see uses separate concatenated strings to construct the shader program string.

It’s much more readable to write using a raw string literal: Start with R"( and end with )".
You’re welcome

I just don’t know this new C++ feature. I’ve modified it. Thanks a lot!

1 Like

I found the problem by line-by-line comparison of my old success program. The mandatory GLSL builtin variable gl_Position is not set.