How to render graphics components in the separate classes in Qt OpenGL?
-
I have subclassed QOpenGLWidget to myOpenGLWidget and I can easily use paintGL function to render whatever I want but as the complexity of my project increases I want to define graphics components(circle, rectangle, curves etc) in separate classes and then render them in their own classes by providing a method like draw() in those components classes. But I dont understand how glDrawArrays() will know to draw in myOpenGLWidget?
Can somebody help me out in this regard? Any sample code or any external links to helpful resources or a sample code would also be much appreciated.
-
I found the solution and it was simple!
Let suppose we want to draw a circle from another class than the subclass of the QOpenGLWidget. Let the circle class be named as Circle. All you have to do is to inherit it from QOpenGLFunctions/QOpenGLExtraFunctions. Now remember that you can't use anything related to OpenGL before the call of QOpenGLWidget subclass's initialize() method. So you have to think about a way to initialize the Circle when QOpenGLWidget subclass's initialize() is called. The easiest way is to define your own an init() method in Circle class and call it from QOpenGLWidget subclass's initialize(). And remember that you must have to call initializeOpenGLFunctions() in the beginning of the Circle init() method. That's it!
Similarly define a method draw() in Circle class and call it from paintGL() method of QOpenGLWidget subclass. In draw() method, you don't have to call initializeOpenGLFunctions().
Circle.h
#ifndef CIRCLE_H #define CIRCLE_H #include <QOpenGLExtraFunctions> class Circle : protected QOpenGLExtraFunctions { public: Circle(); GLuint m_circle_shaderProgramID; GLuint m_circle_VAO; GLuint m_circle_VBO; void init(); void draw(); }; #endif // CIRCLE_H
Circle.cpp
#include "Circle.h" #include <QVector> Circle::Circle() { } void Circle::init() { initializeOpenGLFunctions(); static const char * vs_source[] = { "#version 430 core \n" " \n" " layout (location = 0) in vec2 aPos; \n" " \n" "void main(void) \n" "{ \n" " gl_Position = vec4(aPos , 0.0, 1.0); \n" "} \n" }; static const char * fs_source[] = { "#version 430 core \n" "out vec4 color; \n" " \n" "void main(void) \n" "{ \n" " color = vec4(1.0, 0.0, 0.0, 1.0); \n" "} \n" }; m_circle_shaderProgramID = glCreateProgram(); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fs, 1, fs_source, NULL); glCompileShader(fs); GLuint vs = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vs, 1, vs_source, NULL); glCompileShader(vs); glAttachShader(m_circle_shaderProgramID, vs); glAttachShader(m_circle_shaderProgramID, fs); glLinkProgram(m_circle_shaderProgramID); glDeleteShader(vs); glDeleteShader(fs); glGenVertexArrays(1, &m_circle_VAO); glGenBuffers(1, &m_circle_VBO); } void Circle::draw() { float cx = 0.5f; float cy = 0.5f; float r = 0.1f; int num_segments = 100; float theta = 2 * M_PI / float(num_segments); float c = cosf(theta); //precalculate the sine and cosine float s = sinf(theta); float t; float x = r; //we start at angle = 0 float y = 0; QVector <float> vector; vector.clear(); for(int i = 0; i < num_segments; i++) { //apply translation vector.append(x+cx); vector.append(y+cy); //apply the rotation matrix t = x; x = c * x - s * y; y = s * t + c * y; } glBindVertexArray(m_circle_VAO); glBindBuffer(GL_ARRAY_BUFFER, m_circle_VBO); glBufferData(GL_ARRAY_BUFFER, vector.size()*sizeof(float), vector.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(m_circle_shaderProgramID); glDrawArrays(GL_LINE_LOOP, 0, num_segments); // Simple Circle glDrawArrays(GL_TRIANGLE_FAN, 0, num_segments); // Filled Circle }
Again, call init() from initialize() while call draw() from paintGL() method of QOpenGLWidget subclass like myOpenGLWidget.
-
Hi,
Take a look at the OpenGL Function Calls, Headers and QOpenGLFunctions chapter in QOpenGLWidget's documentation.
You can grab the
QOpenGLFunctions
allocated to the current context line shown in the code samples of that chapter and you pass that to your helper class to do the drawing.It's the same as when you pass a QPainter instance to helper classes in order to draw on something.
Hope it helps
-
I found the solution and it was simple!
Let suppose we want to draw a circle from another class than the subclass of the QOpenGLWidget. Let the circle class be named as Circle. All you have to do is to inherit it from QOpenGLFunctions/QOpenGLExtraFunctions. Now remember that you can't use anything related to OpenGL before the call of QOpenGLWidget subclass's initialize() method. So you have to think about a way to initialize the Circle when QOpenGLWidget subclass's initialize() is called. The easiest way is to define your own an init() method in Circle class and call it from QOpenGLWidget subclass's initialize(). And remember that you must have to call initializeOpenGLFunctions() in the beginning of the Circle init() method. That's it!
Similarly define a method draw() in Circle class and call it from paintGL() method of QOpenGLWidget subclass. In draw() method, you don't have to call initializeOpenGLFunctions().
Circle.h
#ifndef CIRCLE_H #define CIRCLE_H #include <QOpenGLExtraFunctions> class Circle : protected QOpenGLExtraFunctions { public: Circle(); GLuint m_circle_shaderProgramID; GLuint m_circle_VAO; GLuint m_circle_VBO; void init(); void draw(); }; #endif // CIRCLE_H
Circle.cpp
#include "Circle.h" #include <QVector> Circle::Circle() { } void Circle::init() { initializeOpenGLFunctions(); static const char * vs_source[] = { "#version 430 core \n" " \n" " layout (location = 0) in vec2 aPos; \n" " \n" "void main(void) \n" "{ \n" " gl_Position = vec4(aPos , 0.0, 1.0); \n" "} \n" }; static const char * fs_source[] = { "#version 430 core \n" "out vec4 color; \n" " \n" "void main(void) \n" "{ \n" " color = vec4(1.0, 0.0, 0.0, 1.0); \n" "} \n" }; m_circle_shaderProgramID = glCreateProgram(); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fs, 1, fs_source, NULL); glCompileShader(fs); GLuint vs = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vs, 1, vs_source, NULL); glCompileShader(vs); glAttachShader(m_circle_shaderProgramID, vs); glAttachShader(m_circle_shaderProgramID, fs); glLinkProgram(m_circle_shaderProgramID); glDeleteShader(vs); glDeleteShader(fs); glGenVertexArrays(1, &m_circle_VAO); glGenBuffers(1, &m_circle_VBO); } void Circle::draw() { float cx = 0.5f; float cy = 0.5f; float r = 0.1f; int num_segments = 100; float theta = 2 * M_PI / float(num_segments); float c = cosf(theta); //precalculate the sine and cosine float s = sinf(theta); float t; float x = r; //we start at angle = 0 float y = 0; QVector <float> vector; vector.clear(); for(int i = 0; i < num_segments; i++) { //apply translation vector.append(x+cx); vector.append(y+cy); //apply the rotation matrix t = x; x = c * x - s * y; y = s * t + c * y; } glBindVertexArray(m_circle_VAO); glBindBuffer(GL_ARRAY_BUFFER, m_circle_VBO); glBufferData(GL_ARRAY_BUFFER, vector.size()*sizeof(float), vector.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(m_circle_shaderProgramID); glDrawArrays(GL_LINE_LOOP, 0, num_segments); // Simple Circle glDrawArrays(GL_TRIANGLE_FAN, 0, num_segments); // Filled Circle }
Again, call init() from initialize() while call draw() from paintGL() method of QOpenGLWidget subclass like myOpenGLWidget.