Linux environment qt5.9+opengl drawing graphics and rendering (25) detailed series

Hua Weiyun 2022-06-23 18:46:18 阅读数:268

linuxenvironmentqt5.9+openglqtopengl


This is a series of blogs ,OpenGL It is a branch based on computer graphics , It must be understood that “ vector ”, By mathematical vectors and C The combination of language has developed shader language,shader Packaging recombination C++ Namely UE4 and UE5 The graphics engine . vector vector( No C++ Inside vector Container , Don't confuse ), The most important thing is to introduce mathematical matrix Matrix, A matrix is a vector , That is, vector calculation becomes matrix transformation , At the same time, a vector vector Can be equal to the result of two matrix calculations .

This is a vector A, Converted into a matrix

The multiplication of matrix is satisfied with the following operation law :

Associative law :(AB)C = A(BC)

Left distribution law :(A + B)C = AC + BC

Right distribution law :C(A + B) = CA + CB
Matrix multiplication does not satisfy the commutative law :

So we understand , matrix Matrix And vector Vector The relationship between , But I still don't understand OpenGL And shader The relationship between .

OpenGL Yes vertex shader and fragment shader Etc , These are encapsulated shader stay OpenGL It uses .

On the problem of texture filtering
Linear interpolation filtering (GL_LINEAR)== Texture mapping , This requires a fairly high processing capacity of the machine , But it looks like it will work well ;

Nearest value filtering (GL_NEAREST), It takes only a small amount of processing power , It seems that the effect will be poor , But use it because it doesn't take up resources , The project can run normally on both fast and slow machines ; Linear interpolation filtering and nearest value filtering can also be mixed , The texture will look better ;

Mipmap, This is a new way to create textures ; You may notice that when the image becomes very small on the screen , Many details will be lost , The pattern that was good just now becomes ugly ; When you tell OPenGL Create a mipmaped Texture time ,OPenGL Will select the best looking texture it has created ( With many details ) To draw , Not just scaling the original image ( This will result in the loss of details ).

About light
(1) When the light is not turned on , Use vertex color to produce the color of the entire surface .

use glShadeModel You can set the way in which the pixel color inside the surface is generated .GL_FLAT/GL_SMOOTH.

(2) generally speaking , After turning on the light , At least one light source is required in the scene (GL_LIGHT0...GL_LIGHT7)

adopt glEnable(GL_LIGHT0) glDisable(GL_LIGHT0) To turn on and off the specified light source .

— Global ambient light —

GLfloat gAmbient[] = {0.6, 0,6, 0,6, 1.0};

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gAmbient);

(3) Set the light component of the light source – The ambient light / Diffuse light / Specular light

By default ,GL_LIGHT0...GL_LIGHT7 Of GL_AMBIENT The value is (0.0, 0.0, 0.0, 1.0);

GL_LIGHT0 Of GL_DIFFUSE and GL_SPECULAR The value is (1.0, 1.0, 1.0, 1.0),

GL_LIGHT1...GL_LIGHT7 Of GL_DIFFUSE and GL_SPECULAR The value is (0.0, 0.0, 0.0, 0.0).

GLfloat lightAmbient[] = {1.0, 1.0, 1.0, 1.0};

GLfloat lightDiffuse[] = {1.0, 1.0, 1.0, 1.0};

GLfloat lightSpecular[] = {0.5, 0.5, 0.5, 1.0};

glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);

glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);

glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);

(4) Set the position and direction of the light source

– Parallel light – No position, only direction

GLfloat lightPosiTIon[] = {8.5, 5.0, -2.0, 0.0}; // w=0.0

glLightfv(GL_LIGHT0, GL_POSITION, lightPosiTIon);

– Point source – There is a position but no direction

GLfloat lightPosiTIon[] = {8.5, 5.0, -2.0, 1.0}; // w Not for 0

glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);

– Spotlights – There is a position and a direction
GLfloat lightPosition[] = {-6.0, 1.0, 3.0, 1.0}; // w Not for 0

glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);

GLfloat lightDirection[] = {1.0, 1.0, 0.0};

glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightDirection); // The main axis direction of the spotlight spot direction

glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0); // cutoff angle spot cutoff

** Directional light does not change with distance d Increase and decrease , But point lights and spotlights decay .

attenuation Is the decay coefficient , The larger the value of the coefficient , The faster it decays .

By default ,c=1.0, l=0.0, q=0.0
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0); // c coefficient

glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0); // l coefficient

glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5); // q coefficient



 Specific programming code , Please check the attachment , Learning exchange only .


.h file

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QTextStream>
#include <QKeyEvent>
#include <GL/glu.h>
#include <QMessageBox>
#include <QApplication>

class MyGLWidget : public QOpenGLWidget
{
Q_OBJECT

public:
// The following structure defines a 3D vertex
typedef struct
{
float x, y, z;
}VERTEX;

// The following structure uses vertices to describe a three-dimensional object typedef struct // Object structure { int verts; // The number of vertices in an object VERTEX *points; // Pointer containing vertex data }OBJECT;MyGLWidget(QWidget *parent = nullptr);~MyGLWidget();

protected:
void resizeGL(int w, int h);
void initializeGL();
void paintGL();
void keyPressEvent(QKeyEvent *event);
void timerEvent(QTimerEvent *event);

private:
void objallocate(OBJECT *k, int n);
void objfree(OBJECT *k);
void readstr(QTextStream *stream, QString &string);
void objload(QString name, OBJECT *k);
VERTEX calculate(int i);
private:
bool m_show_full_screen;
// Add a few rotation variables , Used to record rotation information . And use cx,cy,cz Set the position of the object on the screen .
// Variable key Used to record the current model ,step Used to set intermediate steps between adjacent deformations . Such as step by 200, You need to 200 Time , To change one object into another .
// Finally, a variable is used to set whether to use deformation .
GLfloat m_xrot;
GLfloat m_yrot;
GLfloat m_zrot; // X, Y & Z The rotation Angle of the axis
GLfloat m_xspeed;
GLfloat m_yspeed;
GLfloat m_zspeed; // X, Y & Z The rotational speed of the shaft
GLfloat m_cx;
GLfloat m_cy;
GLfloat m_cz; // The position of the object

int m_key; // The identifier of the object int m_step;int m_steps; // The number of steps of the transformation bool m_morph; // Whether to use deformation //maxver It is used to record the maximum number of vertices in each object , Use as an object 5 vertices , Another object uses 20 vertices , Then the number of vertices of the object is 20.// The result defines four model objects we use , The intermediate state of deformation of adjacent models is saved in helper in ,sour Save the original model object ,dest Save the model object to be deformed .int m_maxver; // Maximum number of vertices OBJECT m_orph1;OBJECT m_morph1;OBJECT m_morph2;OBJECT m_morph3;OBJECT m_morph4; // Our four objects OBJECT m_helper;OBJECT *m_sour;OBJECT *m_dest; // Help objects , Original object , Target object 

};
#endif // MYGLWIDGET_H





.cpp file

#include “myglwidget.h”

MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent), m_show_full_screen(false),
m_xrot(0.0f), m_yrot(0.0f), m_zrot(0.0f), m_xspeed(0.0f), m_yspeed(0.0f), m_zspeed(0.0f),
m_cx(0.0f), m_cy(0.0f), m_cz(-15.0f), m_key(1), m_step(0), m_steps(200), m_morph(false)
{
showNormal();
startTimer(15);
}

MyGLWidget::~MyGLWidget()
{
objfree(&m_morph1); // Release model 1 Memory
objfree(&m_morph2); // Release model 2 Memory
objfree(&m_morph3); // Release model 3 Memory
objfree(&m_morph4); // Release model 4 Memory
objfree(&m_helper); // Release model 5 Memory
}

// The following code is used to reset OpenGL The size of the scene , Whether or not the size of the window has changed ( Suppose you don't use full screen mode ).
// You can't even change the window size ( For example, you are in full screen mode ), It will still run at least once – Set the perspective at the beginning of the program .
//OpenGL The size of the scene will be set to the size of the window in which it is displayed .
void MyGLWidget::resizeGL(int w, int h)
{
if(h == 0)
{
h = 1;
}
glViewport(0, 0, w, h); // Reset the current viewport
// The following lines are the perspective setting screen . It means that the farther away things look, the smaller . This creates a realistic looking scene .
// The perspective here is based on the window width and height 45 From the angle of view .0.1f,100.0f It's the start and end of the depth we can draw in the scene .
//glMatrixMode(GL_PROJECTION) Indicate that the next two lines of code will affect projection matrix( Projection matrix ).
// The projection matrix is responsible for adding perspective to our scene . glLoadIdentity() Approximate to reset . It restores the selected matrix state to its original state .
// call glLoadIdentity() Then we set the perspective for the scene .
//glMatrixMode(GL_MODELVIEW) Indicates that any new transformation will affect modelview matrix( Model observation matrix ).
// Our object information is stored in the model observation matrix . Finally, we reset the model observation matrix . If you don't understand the meaning of these terms , Please don't worry .
// In a later tutorial , I'll explain to you . Just know if you want to get a wonderful perspective scene , It has to be done .
glMatrixMode(GL_PROJECTION); // Choose the projection matrix
glLoadIdentity(); // Reset projection matrix
// Sets the size of the viewport
gluPerspective(45.0f, (GLfloat)w/(GLfloat)h, 0.1f, 100.0f);

glMatrixMode(GL_MODELVIEW); // Select the model observation matrix glLoadIdentity(); // Reset model observation matrix 

}

// The following function completes the initialization function , It sets the blend mode to translucent
void MyGLWidget::initializeGL()
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Set translucent blending mode
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set the clear color to black
glClearDepth(1.0); // Set the depth cache value to 1
glDepthFunc(GL_LESS); // Set depth test function
glEnable(GL_DEPTH_TEST); // Enable depth testing
glShadeModel(GL_SMOOTH); // Set the shading mode to smooth shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

// The following code is used to load our model object m_maxver = 0; // The maximum number of initialized vertices is 0objload(":/data/Sphere.txt",&m_morph1);// Load the ball model objload(":/data/Torus.txt",&m_morph2);// Load torus model objload(":/data/Tube.txt",&m_morph3);// Load cube model // The fourth model does not read from the file , We are (-7,-7,-7)-(7,7,7) Randomly generate model points , The models all have the same 486 vertices .objallocate(&m_morph4, 486);for(int i=0; i<486; i++){ m_morph4.points[i].x = ((float)(qrand()%14000)/1000)-7; m_morph4.points[i].y = ((float)(qrand()%14000)/1000)-7; m_morph4.points[i].z = ((float)(qrand()%14000)/1000)-7;}// Initialize the intermediate model as a sphere , And set the original and target models as balls objload(":/data/Sphere.txt",&m_helper);m_sour = m_dest = &m_morph1;

}

// The following is the specific drawing code , Let's set the model change first , So that we can better observe .
void MyGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Empty cache
glLoadIdentity(); // Reset model transformation matrix
glTranslatef(m_cx, m_cy, m_cz); // Translation and rotation
glRotatef(m_xrot, 1, 0, 0);
glRotatef(m_yrot, 0, 1, 0);
glRotatef(m_zrot, 0, 0, 1);
m_xrot += m_xspeed;
m_yrot += m_yspeed;
m_zrot += m_zspeed; // According to the rotation speed , Increase the rotation angle

GLfloat tx, ty, tz; // Vertex temporary variable VERTEX q; // Save temporary vertices for intermediate calculations // Next, draw the points in the model , If deformation is enabled , Then calculate the intermediate process point of deformation .glBegin(GL_POINTS); // Point drawing starts for(int i=0; i<m_morph1.verts; i++) // Cycle through the model 1 Every vertex in { if(m_morph) // If deformation is enabled , Then calculate the intermediate model { q = calculate(i); } else { q.x = q.y = q.z = 0; } m_helper.points[i].x -= q.x; m_helper.points[i].y -= q.y; m_helper.points[i].z -= q.z; tx = m_helper.points[i].x; // Save the calculation results to x,y,z variable ty = m_helper.points[i].y; tz = m_helper.points[i].z; // In order to make the animation run smoothly , We have drawn a total of three intermediate state points . Let the deformation process change from blue-green to blue . glColor3f(0, 0.9f, 1); // Set the color glVertex3f(tx, ty, tz); // Draw the vertices glColor3f(0, 0.8f, 1); // Turn the color blue tx -= 2*q.x; ty -= 2*q.y; tz -= 2*q.z; // If deformation is enabled , Then draw 2 Vertex after step glVertex3f(tx, ty, tz); glColor3f(0, 0.7f, 1); // Turn the color blue tx -= 2*q.x; ty -= 2*q.y; tz -= 2*q.z; // If deformation is enabled , Then draw 2 Vertex after step glVertex3f(tx, ty, tz);}glEnd();// Finally, if deformation is enabled , Then increase the incremental step parameter , Then draw the next point .// If deformation is enabled, the number of deformation steps is increased if(m_morph && m_step <= m_steps){ m_step++;}else{ m_morph = false; m_sour = m_dest; m_step = 0;}

}

void MyGLWidget::keyPressEvent(QKeyEvent *event)
{
switch(event->key())
{
case Qt::Key_F2:
{
m_show_full_screen = !m_show_full_screen;
if(m_show_full_screen)
{
showFullScreen();
}
else
{
showNormal();
}
update();
break;
}
case Qt::Key_Escape:
{
qApp->exit();
break;
}
case Qt::Key_PageUp:
{
m_zspeed+=0.1f;// Press to increase the winding z The speed at which the axis rotates
break;
}
case Qt::Key_PageDown:
{
m_zspeed-=0.1f;// Press to decrease the winding z The speed at which the axis rotates
break;
}
case Qt::Key_Up:
{
m_xspeed-=0.1f;// Press to decrease the winding x The speed at which the axis rotates
break;
}
case Qt::Key_Down:
{
m_xspeed+=0.1f;// Press to increase the winding x The speed at which the axis rotates
break;
}
case Qt::Key_Left:
{
m_yspeed-=0.1f;// Press the decrease edge y The speed at which the axis rotates
break;
}
case Qt::Key_Right:
{
m_yspeed+=0.1f;// Press the increase edge y The speed at which the axis rotates
break;
}
case Qt::Key_Q:
{
m_cz-=0.1f; // If yes, move to the screen
break;
}
case Qt::Key_Z:
{
m_cz+=0.1f;// If yes, move out of the screen
break;
}
case Qt::Key_W:
{
m_cy+=0.1f;// If yes, move up
break;
}
case Qt::Key_S:
{
m_cy-=0.1f;// If yes, move down
break;
}
case Qt::Key_D:
{
m_cx+=0.1f;// If yes, move to the right
break;
}
case Qt::Key_A:
{
m_cx-=0.1f;// If yes, move to the left
break;
}
case Qt::Key_1:
{
if(m_key != 1 && !m_morph)
{
// If 1 Pressed , Then deform to the model 1
m_key=1;
m_morph=true;
m_dest=&m_morph1;
}
break;
}
case Qt::Key_2:
{
if(m_key != 2 && !m_morph)
{
// If 2 Pressed , Then deform to the model 2
m_key=2;
m_morph=true;
m_dest=&m_morph2;
}
break;
}
case Qt::Key_3:
{
if(m_key != 3 && !m_morph)
{
// If 3 Pressed , Then deform to the model 3
m_key=3;
m_morph=true;
m_dest=&m_morph3;
}
break;
}
case Qt::Key_4:
{
if(m_key != 4 && !m_morph)
{
// If 4 Pressed , Then deform to the model 4
m_key=4;
m_morph=true;
m_dest=&m_morph4;
}
break;
}
}
QOpenGLWidget::keyPressEvent(event);
}

void MyGLWidget::timerEvent(QTimerEvent *event)
{
update();
QOpenGLWidget::timerEvent(event);
}

// The following function is used to allocate memory space for saving vertex data for the model
void MyGLWidget::objallocate(OBJECT k, int n)
{
k->points = (VERTEX
)malloc(sizeof(VERTEX)*n); // Distribute n Memory space of vertices
}

// The following function is used to free the memory space allocated for the model
void MyGLWidget::objfree(OBJECT *k)
{
free(k->points);
}

// The following code is used to read a line in the file .
// We use a loop to read characters , Read at most 255 Characters , When you meet ’\n’ When I get back , Stop reading and return immediately .
void MyGLWidget::readstr(QTextStream *stream, QString &string)
{
do
{
string = stream->readLine(255); // Read at most 255 Characters
}while((string[0] == ‘/’) || (string[0] == ‘\n’)); // Stop reading in case of carriage return
}

// The following code is used to load a model file , And allocate memory for the model , Store the data .
void MyGLWidget::objload(QString name, OBJECT *k)
{
int ver; // Save the number of vertices
float rx, ry, rz; // Save model location
QFile file(name); // Open file
QString oneline; // preservation 255 Characters
file.open(QIODevice::ReadOnly); // open the text file , For reading
QTextStream stream(&file);
readstr(&stream, oneline); // Read in a line of text
sscanf(oneline.toLatin1().data(), “Vertices:%d\n”, &ver); // Search string "Vertices: ", And keep the following vertex number ver variable
k->verts = ver; // Set the number of vertices of the model
objallocate(k, ver); // Allocate memory for model data
// The following loop , Read every line ( That is, each vertex ) The data of , And save it in memory
for(int i=0; i<ver; i++) // Loop all vertices
{
readstr(&stream, oneline); // Read a row of data
sscanf(oneline.toLatin1().data(), “%f %f %f”, &rx, &ry, &rz); // Save vertex data in rx,ry,rz in
k->points[i].x = rx; // Save the current vertex x coordinate
k->points[i].y = ry; // Save the current vertex y coordinate
k->points[i].z = rz; // Save the current vertex z coordinate
}
file.close(); // Close file
if(ver>m_maxver)
{
m_maxver = ver; // Record the maximum number of vertices
}
}

// The following function depends on the set interval , Computation first i The displacement of each transformation of vertices
MyGLWidget::VERTEX MyGLWidget::calculate(int i)
{
VERTEX a;
a.x = (m_sour->points[i].x-m_dest->points[i].x)/m_steps;
a.y = (m_sour->points[i].y-m_dest->points[i].y)/m_steps;
a.z = (m_sour->points[i].z-m_dest->points[i].z)/m_steps;
return a;
}

//Piotr Cieslak The code is very novel , You can know how to load a 3D model from a file .



版权声明:本文为[Hua Weiyun]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/174/202206231742474114.html