Qoopenglshaderprogram encapsulates the compilation process of ShaderProgram. Whether it is loading SourceCode or SourceFile, Qt encapsulation is very good. If it is not encapsulated, the code will be written into the string as shown in the figure below. There is no color identification, and there must be a newline character on each line, which is very troublesome. Therefore, we hope to write Shader like ordinary C + + code. Fortunately, Qt has encapsulated it for us.
In order to load and edit the shader file normally in QT: set the encoding rule to UTF-8 in tools - > options.
- Step 1: create a new txt text in the pro file directory and modify the names to shapes.vert (vertex shader) and shapes.frag (fragment shader)
- Step 2: load Qt through resource file.
After reading the code, continue to write GLSL:
//Class. h file #ifndef MYOPENGLWIDGET_H #define MYOPENGLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLFunctions_3_3_Core> #include <QOpenGLShaderProgram> class myopenglwidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core { Q_OBJECT public: enum Shape{None,Rect,Circle,Triangle}; explicit myopenglwidget(QWidget *parent = nullptr); ~myopenglwidget(); void drawShape(Shape shape); void setWirefame(bool wireframe); protected: virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); private: Shape m_shape; QOpenGLShaderProgram sharedprogram; }; #endif // MYOPENGLWIDGET_H
//Class. cpp file #include "myopenglwidget.h" //Create VAO and VBO objects and assign ID s unsigned int VBO,VAO,EBO; float vertices[] = { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f, }; unsigned int indices[] = { 0, 1, 3, 1, 2, 3 }; myopenglwidget::myopenglwidget(QWidget *parent) : QOpenGLWidget(parent) { } myopenglwidget::~myopenglwidget() { makeCurrent();//Call the current state, no matter why, just add it glDeleteBuffers(1,&VBO); glDeleteBuffers(1,&EBO); glDeleteVertexArrays(1,&VAO); doneCurrent();//Change the current state, no matter why, just add it } void myopenglwidget::drawShape(myopenglwidget::Shape shape) { m_shape = shape; update(); } void myopenglwidget::setWirefame(bool wireframe) { makeCurrent(); if(wireframe) glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); update(); doneCurrent(); } void myopenglwidget::initializeGL() { initializeOpenGLFunctions(); glGenVertexArrays(1,&VAO); glGenBuffers(1,&VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER,VBO); glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER,0); sharedprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert"); sharedprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag"); bool success = sharedprogram.link(); if (!success) qDebug()<<"error"; glGenBuffers(1,&EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW); glBindVertexArray(0); } void myopenglwidget::resizeGL(int w, int h) { } void myopenglwidget::paintGL() { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); sharedprogram.bind(); glBindVertexArray(VAO); switch(m_shape){ case Rect: glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); break; default: break; } }
//shapes.frag #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0, 0.5, 0.2, 1.0); }
//shapes.vert #version 330 core\n layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f); }
GLSL: OpenGL Shading Language
A typical shader program should look like this:
- Version number.
- In or out or uniform keyword. In is the data flowing in from the pipeline, which needs to be accepted with a variable. Out is the data flowing out of the pipeline, which needs to be passed out with variables. If the CPU needs to give values to geometry shaders or fragment shaders, it does not need to pass them down from vertex shaders completely according to the pipeline, but can also use uniform to pass them directly from the CPU to where they need to be.
- Main function. And c language main is the same thing, sequential execution.
- Note: vertex shaders are special, and the input variable is vertex attribute. There is an upper limit on the number of vertex attributes we can declare, which can be obtained through the following code. OpenGL ensures that at least 16 vertex attributes containing 4 components are available, and some hardware allows more vertex attributes. For general application development, 16 attributes are sufficient.
nt nrAttributes; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);//nrAttributesā„16
Basics of GLSL:
-
GLSL contains most of the default data types in languages such as C: int, float, double, uint, and bool
-
GLSL also has two container types:
1: Vector: vecn represents n float data; bvecn represents n bool data; ivecn represents n int data; uvecn represents n unsigned int data; dvecn - represents n double data
2: Matrix. -
Vectors allow some interesting and flexible component selection methods, called swizzling, as follows:
vec2 vect = vec2(0.1, 0.2); vec2 res = vec4(vect, 0.3, 0.0);//(0.1, 0.2, 0.3, 0.0) vec2 res2 = vec4(res.xyz, 0.5);//(0.1, 0.2, 0.3, 0.5) vec2 res3 = vec4(res.xxx, 0.7);//(0.1, 0.1, 0.1, 0.7)
How to use GLSL:
- Declare an output in the sender shader.
- Declare a similar input in the receiver shader.
- When the type and name are consistent, OpenGL can link them together.
Vertex shaders receive a special form of input that would otherwise be inefficient
- In order to define how to manage vertex data, we use the metadata of location (higher-level data, similar to metacode) to specify input variables, so that we can configure vertex attributes on the CPU. For example: layout (location = 0). The logo layout allows us to link it to vertex data.
- Of course, we can ask the location value of a variable in the GPU through the glGetAttribLocation() function without using the layout (location = 0) statement, and then the glVertexAttribPointer in the CPU memory is the number, so we can match it. You can also bind the location value location through the glBindAttribLocation() function.
- If the program has only one attribute, the write or write position value location is 0.
The code is as follows:
//myopenglwidget.cpp #include "myopenglwidget.h" //Create VAO and VBO objects and assign ID s unsigned int VBO,VAO,EBO; float vertices[] = { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f, }; unsigned int indices[] = { 0, 1, 3, 1, 2, 3 }; myopenglwidget::myopenglwidget(QWidget *parent) : QOpenGLWidget(parent) { } myopenglwidget::~myopenglwidget() { makeCurrent();//Call the current state, no matter why, just add it glDeleteBuffers(1,&VBO); glDeleteBuffers(1,&EBO); glDeleteVertexArrays(1,&VAO); doneCurrent();//Change the current state, no matter why, just add it } void myopenglwidget::drawShape(myopenglwidget::Shape shape) { m_shape = shape; update(); } void myopenglwidget::setWirefame(bool wireframe) { makeCurrent();//Call current state if(wireframe) glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); update(); doneCurrent();//Change current status } void myopenglwidget::initializeGL() { initializeOpenGLFunctions(); sharedprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert"); sharedprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag"); bool success = sharedprogram.link(); if (!success) qDebug()<<"error"; glGenVertexArrays(1,&VAO);//A structure for storing data in a vertex array glGenBuffers(1,&VBO);//The buffer is used to store vertex data //Binding VBO and VAO objects glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER,VBO); //Open up a new data space for the buffer object currently bound to data. If the data is not empty, the data will be transferred from memory to video memory glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); sharedprogram.bind(); GLint posLocation = 2; sharedprogram.bindAttributeLocation("aPos",posLocation);//The location of the bound variable aPos //Tell the graphics card how to parse the passed data, and the process will be secretly recorded by VAO glVertexAttribPointer(posLocation,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);//Attribute 0, three values, floating-point, no standardization required, step size, offset //Enable the first attribute value of VAO management, because all attributes are closed by default glEnableVertexAttribArray(posLocation); //Small assistants can rest and form good habits, because there will be many small assistants in the future glBindBuffer(GL_ARRAY_BUFFER,0); //Bind VEO object glGenBuffers(1,&EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW); glBindVertexArray(0); } void myopenglwidget::resizeGL(int w, int h) { } void myopenglwidget::paintGL()//All the drawings are here, and other places are just triggered { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); sharedprogram.bind(); glBindVertexArray(VAO); switch(m_shape){ case Rect: glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); break; default: break; } }
//shapes.vert #version 330 core in vec3 aPos; out vec4 vertexColor; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f); vertexColor = vec4(1.0, 0.5, 0.2, 1.0); }
- Uniform is a way to send data from CPU code to shaders in GPU. It is global and can be accessed by any shader program at any stage. It can be understood that the uniform data of GPU and GPU are consistent