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 );
