OpenGL and shaders in wxGLCanvas Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
NoeMurr
Knows some wx things
Knows some wx things
Posts: 32
Joined: Sat Mar 31, 2018 2:26 pm

OpenGL and shaders in wxGLCanvas

Post by NoeMurr »

Hi all,

I'm trying to use modern OpenGL with wxGLCanvas.
To learn OpenGL I'm following this tutorial: https://www.youtube.com/watch?v=x0H--CL ... S2&index=5.
To start I'm only trying to write a simple triangle on the screen.
Header:

Code: Select all

#include <GL/glew.h>
#include <wx/glcanvas.h>

class Viewer3D : public wxGLCanvas{
private:
    struct ShaderSource {
        std::string vertex;
        std::string fragment;

        bool isOk() const noexcept{
            return not (vertex.empty() and fragment.empty());
        }
    };

    ShaderSource loadShader(const std::string &path);
    unsigned int compileShader(unsigned int type, const std::string &src);
    unsigned int compileGLProgram(const ShaderSource &src);

public:
    std::unique_ptr<wxGLContextAttrs> ctxAttr;
    std::unique_ptr<wxGLContext> ctx;

    Viewer3D(
        wxWindow *parent, wxWindowID id, wxGLAttributes attrList,
        const wxPoint &pos =wxDefaultPosition,
        const wxSize &size = wxDefaultSize, long style = 0,
        const wxString &name = wxGLCanvasName,
        const wxPalette &palette = wxNullPalette
    );

    void onPaint(wxPaintEvent &e);

    virtual ~Viewer3D(); // NOLINT(modernize-use-override)
};
Source:

Code: Select all

#include <wx/wx.h>
#include <fstream>
#include <sstream>
#include <array>
#include "../hdr/Viewer3D.hpp"

Viewer3D::ShaderSource Viewer3D::loadShader(const std::string &path) {
    enum ShaderType {
        VERTEX=0,
        FRAGMENT
    }mode;
    using namespace std;
    ifstream input(path);
    array<stringstream,2> shaders;
    string line;


    if (not input.good()){
        wxLogError(strerror(errno));
        return {};
    }

    // initialize mode
    if ((not getline(input, line)) or line.find("//#shader") == string::npos)
        return {};
    if (line.find("vertex") != string::npos)
        mode = ShaderType::VERTEX;
    else if(line.find("fragment"))
        mode = ShaderType::FRAGMENT;
    else
        return {};


    while (getline(input, line)){
        if (line.find("//#shader") != string::npos){
            if (line.find("vertex") != string::npos){
                // load vertex shader
                mode = ShaderType::VERTEX;
            } else if(line.find("fragment")){
                // load fragment shader
                mode = ShaderType::FRAGMENT;
            } else {
                // error
                return {};
            }
        } else {
            shaders[mode] << line << '\n';
        }
    }

    return ShaderSource{
        shaders[ShaderType::VERTEX].str(),
        shaders[ShaderType::FRAGMENT].str()
    };
}

unsigned int Viewer3D::compileShader(unsigned int type, const std::string &src){
    SetCurrent(*ctx);
    auto id = glCreateShader(type);
    const char *srcCStr = src.c_str();
    glShaderSource(id, 1, &srcCStr, nullptr);
    glCompileShader(id);

    // error handling
    int status;
    glGetShaderiv(id, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE){
        int length;
        std::string log;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        log.reserve(static_cast<unsigned long>(length));
        glGetShaderInfoLog(id, length, &length, &log[0]);
        std::puts(log.c_str());
        return 0;
    }

    return id;
}

unsigned int Viewer3D::compileGLProgram(const Viewer3D::ShaderSource &src) {
    SetCurrent(*ctx);

    if(not src.isOk()){
        wxLogError("invalid ShaderSource value");
        return 0;
    }
    unsigned int programId = glCreateProgram();
    auto vertexId = compileShader(GL_VERTEX_SHADER, src.vertex);
    auto fragmentId = compileShader(GL_FRAGMENT_SHADER, src.fragment);

    glAttachShader(programId, vertexId);
    glAttachShader(programId, fragmentId);
    glLinkProgram(programId);
    glValidateProgram(programId);

    // error handling
    int status;
    glGetProgramiv(programId, GL_VALIDATE_STATUS, &status);
    if (status == GL_FALSE){
        int length;
        std::string log;
        glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &length);
        log.reserve(static_cast<unsigned long>(length));
        glGetProgramInfoLog(programId, length, &length, &log[0]);
        std::puts(log.c_str());
        return 0;
    }

    glDetachShader(programId, vertexId);
    glDetachShader(programId, fragmentId);
    glDeleteShader(vertexId);
    glDeleteShader(fragmentId);
    return programId;
}

Viewer3D::Viewer3D(
    wxWindow *parent, wxWindowID id, wxGLAttributes attrList,
    const wxPoint &pos, const wxSize &size,
    long style, const wxString &name, const wxPalette &palette
) : wxGLCanvas(parent, attrList, id, pos, size, style, name, palette),
    ctxAttr(new wxGLContextAttrs)
{
    ctxAttr->CoreProfile().OGLVersion(4,3).EndList();
    ctx = std::make_unique<wxGLContext>(this, nullptr, ctxAttr.get());

    if (not ctx->IsOK()){
        wxLogFatalError("Cannot initialize wxGLContext");
        return;
    }

    SetCurrent(*ctx);

    // initializing glew
    GLenum res = glewInit();
    if (res != GLEW_OK) {
        wxLogFatalError("Error: '%s'\n", glewGetErrorString(res));
    }
    std::cout << (glGetString(GL_VERSION)) << std::endl;

    Bind(wxEVT_PAINT, &Viewer3D::onPaint, this);
}

void Viewer3D::onPaint(wxPaintEvent &e) {
    SetCurrent(*ctx);
    glClearColor(0.f, 0.f, 0.f, 1.f);
    float positions[] = {
        0.0f,  0.0f,
        -0.5f, -0.5f,
        0.5f, -0.5f
    };

    unsigned int buff;
    glGenBuffers(1, &buff);
    glBindBuffer(GL_ARRAY_BUFFER, buff);
    glBufferData(GL_ARRAY_BUFFER, 6*sizeof(float), positions, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0);
    auto shdrSrc = loadShader(
        "/Users/noemurr/git/NavTools/code/gui/3D-world-viewer/shaders"
        "/basicShader.glsl"
    );

    auto programID = compileGLProgram(shdrSrc);
    glUseProgram(programID);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glFlush();
    SwapBuffers();
}

Viewer3D::~Viewer3D() = default;
When I run this code I obtain a black screen with an error from the compilation of the shaders: Validation Failed: No vertex array object bound.
I've already read the pyramid example of opengl but it is too complex and I can't understand what I do wrong.
NoeMurr
Knows some wx things
Knows some wx things
Posts: 32
Joined: Sat Mar 31, 2018 2:26 pm

Re: OpenGL and shaders in wxGLCanvas

Post by NoeMurr »

I have solved the problem :D even if I don't why it works, it works.
I only need to create a Vertex Array Object and bind it with theese three lines in the init phase of my program:

Code: Select all

 unsigned int vao;
 glGenVertexArrays(1, &vao);
 glBindVertexArray(vao);
Post Reply