Let’s talk about what we want to do and what this little series is going to be about.
First of all, this series is very small, I think it is probably only a few sections, it is impossible to tell you all about 3D Opengl, so the reader should be a little basic.
In volume rendering, we sometimes only need to render part of the content, that is, we don’t need to render the entire space. In this case, we should create a window with a moving rectangle to adjust the content we want to render. Note that it should be rectangular.
Build a DockWidget on top of our existing project. Then define a concrete Opengl class:
Class My3DDisplayWidget: Public QOpenGLWidget,protected QOpenGLFunctions inherit from both Opengl classes, one for controls and one for functions. Declare these functions inside:
`void initializeGL(); void paintGL(); void resizeGL(int width, int height); void keyPressEvent(QKeyEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); `Copy the code
The first three are automatically called by the system for initialization, drawing, and resizing.
The keystroke event is temporarily unavailable. We mainly use mouse events.
Then define the private variable:
`private: QOpenGLShaderProgram *program; QOpenGLBuffer vbo; GLfloat xRot, yRot, zRot; GLfloat translateX, translateY, translateZ; `Copy the code
The first of these private variables is used to set the shader program, the second is the buffer, and then the rotation Angle of the x,y, and z axes, and the translation for the x, Y, and z directions.
In the constructor, we define:
` My3DDisplayWidget: : My3DDisplayWidget (QWidget * parent) : QOpenGLWidget (parent) {xRot = 0.0; YRot = 0.0; ZRot = 0.0; translateX = 0; translateY = 0; TranslateZ = 3.0; SetMinimumSize (240240); } `Copy the code
Some data initialization work has been done.
Then start doing Opengl initialization:
` void My3DDisplayWidget: : initializeGL () {/ / for the current environment to initialize OpenGL function initializeOpenGLFunctions (); QSurfaceFormat surfaceFormat; surfaceFormat.setSamples(4); // Multisample setFormat(surfaceFormat); //setFormat is a function of QOpenGLWidget //glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); //glClearColor(255, 25, 15, 255); glClearColor(0, 0, 0, 255); QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); const char *vsrc = "#version 330 \n" "in vec4 vPosition; \n" "in vec4 vColor; \n" "out vec4 color; \n" "uniform mat4 matrix; \n" "void main() { \n" " color = vColor; \n" " gl_Position = matrix * vPosition; \n" "} \n"; vshader->compileSourceCode(vsrc); // Create Fragment shader QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); const char *fsrc = "#version 330 \n" "in vec4 color; \n" "out vec4 fColor; \n" "void main() { \n" " fColor = color; \n" "} \n"; fshader->compileSourceCode(fsrc); // Create shader program program = new QOpenGLShaderProgram; program->addShader(vshader); program->addShader(fshader); program->link(); program->bind(); } `Copy the code
Note that if your program calls continue to fail, be sure to add:
“#version 330 \n” to emphasize the OpenGL version of the shader used.
ResizeGL and key response are not added and are of no use here.
Because mouse events have to do with rotation, we’ll talk about that later. Let’s take a look at the cube we defined:
` / / vertex positions GLfloat are [6] [4] [3] = {/ / {{0.8 f, f, 0.8 0.8 f}, {0.8-0.8 f to 0.8 f, f}, {0.8 f to 0.8 f, 0.8 f}, {0.8 0.8 f, f, 0.8 f}}, / / {{0.8 f to 0.8 f, 0.8 f}, {0.8 f to 0.8 f, 0.8 f}, {0.8 f to 0.8 f to 0.8 f}, {0.8 f, f, 0.8-0.8 f}}, {{0.5 f to 0.5 f, F - 0.5 f}, {0.5, 0.5 f, 0.5 f}, {0.5 f, f, 0.5-0.5 f}, {0.5 f, 0.5 f, 0.5 f}}, {{0.5 f, f, 0.5 0.5 f}, {0.5 f, 0.5 f, 0.5 f}, {0.5 f, 0.5 f, 0.5 f}, {0.5 f, f, 0.5-0.5 f}}, / / {{0.8 f to 0.8 f, 0.8 f}, {0.8 f, f, 0.8 to 0.8 f}, {0.5 0.8 f, f, 0.8 f}, {0.8 f, f, 0.8 0.8 f}}, {{0.5 f to 0.5 f, 0.5 f}, {0.5 f to 0.5 f to 0.5 f}, {0.5 f, 0.5 f, 0.5 f}, {0.5 f, 0.5 f, 0.5 f}}}; / / vertex color GLfloat colors [6] [4] [3] = {{{1.0 f, f, 0.0 to 0.0 f}, {0.0 f to 1.0 f, 0.0 f}, {0.0 f, f, 0.0 to 1.0 f}, {1.0 1.0 f, f, 1.0 f}}, {{1.0 f, f, 0.0 0.0 f}, {0.0 f to 1.0 f to 0.0 f}, {1.0 f to 1.0 f, 1.0 f}, {0.0 f, f, 0.0 to 1.0 f}}, {{0.0 1.0 f, f, 0.0 f}, {0.0 f, f, 1.0 0.0 f}, {1.0 f, f, 1.0 1.0 f}, {0.0 f, f, 0.0 to 1.0 f}},}; GLfloat RVS [6] [4] [3] = {{{0.4 f to 0.4 f to 0.4 f}, {0.4-0.4 f to 0.4 f, f}, {0.4 f to 0.4 f, 0.4 f}, {0.4 f, f, 0.4 to 0.4 f}}, 0.4 f f {{0.4, 0.4, f}, {0.4 f to 0.4 f, 0.4 f}, {f f 0.4, 0.4, 0.4 f}, {0.4 f, f, 0.4 to 0.4 f}}, {{0.4 0.4 f, f, 0.4 f}, {0.4 f, 0.4 f, 0.4 f}, {0.4 f to 0.4 f to 0.4 f}, {0.4 f, f, 0.4-0.4 f}}, {{0.4 f, f, 0.4 0.4 f}, {0.4 f, 0.4 f, 0.4 f}, {0.4 f, 0.4 f, 0.4 f}, {0.4 f, f, 0.4-0.4 f}}, {{0.4 f to 0.4 f, 0.4 f}, {0.4 f, f, 0.4 to 0.4 f}, {0.4 0.4 f, f, 0.4 f}, {0.4 f, f, 0.4 0.4 f}}, {{0.4 f to 0.4 f, 0.4 f}, {0.4 f to 0.4 f to 0.4 f}, {0.4 f, 0.4 f, 0.4 f}, {0.4 f, 0.4 f, 0.4 f}}}; GLfloat vertices2[6][4][3] = {}; GLfloat colors2 [6] [4] [3] = {{{1.0 f to 0.0 f to 0.0 f}, {0.0 f to 1.0 f, 0.0 f}, {0.0 f, f, 0.0 1.0 f}, {1.0 f, f, 1.0 to 1.0 f}}, 0.0 f f {{1.0, 0.0, f}, {0.0 f, f, 1.0 to 0.0 f}, {0.0 f, f, 0.0 to 1.0 f}, {1.0 f, f, 1.0 to 1.0 f}}, {{1.0 f, f 0.0, 0.0} f, {0.0 f, 1.0 0.0 f, f}, {0.0 f, f, 1.0 to 0.0 f}, {1.0 f, f, 1.0 to 1.0 f}}, {{1.0 f, f, 0.0 to 0.0 f}, {0.0 f, f, 1.0 to 0.0 f}, {0.0 0.0 f, f, 1.0 f}, {1.0 f, f, 1.0 1.0 f}}, {{1.0 f to 0.0 f, 0.0 f}, {0.0 f, f, 1.0 0.0 f}, {0.0 f, f, 0.0 to 1.0 f}, {1.0 f, f, 1.0 to 1.0 f}}, 0.0 f f {{1.0, 0.0, f}, {0.0 f, f, 1.0 to 0.0 f}, {0.0 f, f, 0.0 to 1.0 f}, {1.0 f, f, 1.0 to 1.0 f}}}; `Copy the code
Here are the vertices and color coordinates of the big cube and the small cube inside. Note that our inner cube can be resized, but why do we define constant assignment here? We use the initial value of the RVS to determine whether the vertices are the left or right boundary of the vertices.
Let’s define one in the DockWidget
'GLfloat centralRectPos[3] = {0,0,0}; GLfloat halfBoundaries[3] = {boundaries 0.5,0.5,0.5}; `Copy the code
The value of this variable is assigned an initial value, and the two arrays represent the positions and sizes of the smaller cubes inside the larger cube. Where the top is the center, we set it to 0,0,0, the center, and then the bottom is half the length. The total length is 1.0, so the initial value is exactly where the small cube and the large cube overlap.
Then we’ll write a function inside the OpenGL class that updates the boundary:
`void updateVetices() { for (int i = 0; i<6; i++) { for (int j = 0; j<4; j++) { if (rvs[i][j][0] < 0) { vertices2[i][j][0] = centralRectPos[0] - halfBoundaries[0]; } else { vertices2[i][j][0] = centralRectPos[0] + halfBoundaries[0]; } if (rvs[i][j][1] < 0) { vertices2[i][j][1] = centralRectPos[1] - halfBoundaries[1]; } else { vertices2[i][j][1] = centralRectPos[1] + halfBoundaries[1]; } if (rvs[i][j][2] < 0) { vertices2[i][j][2] = centralRectPos[2] - halfBoundaries[2]; } else { vertices2[i][j][2] = centralRectPos[2] + halfBoundaries[2]; `}}}}Copy the code
It can generate an array of vertices of a new cube based on the amount of changing center points.