Matrix state management class - MatrixState3D
The Matrix tool class for mathematical calculation of Matrix can be directly used to realize various transformations in practical development. But the procedure will be cumbersome and inconvenient to maintain. Therefore, the author further developed Matrix state management class MatrixState3D, which further encapsulates the Matrix state and transformation functions required in 3D development.
In this way, it is no longer necessary to directly use various matrix operations in actual development, but to operate various functions based on MatrixState3D class. The same is true in the cases in the previous chapters. The MatrixState3D class is introduced in detail below. The specific contents are as follows.
(1) Firstly, the basic structure of MatrixState3D class is given, which declares various member variables and functional methods required in the process of program development, mainly including current member variables such as basic transformation matrix, projection matrix, camera observation matrix and total transformation matrix, as well as functional methods required for various transformation operations. The specific codes are as follows.
#ifndef MatrixState_hpp / / prevent duplicate definitions #define MatrixState_hpp #include "Matrix.h" / / import the required header file class MatrixState3D { public: //Several member variables needed in the matrix state management class are defined //It mainly includes one-dimensional array for storing various transformation matrices and two-dimensional array for storing basic transformation matrix stack. static float currMatrix[16];//Current transformation matrix static float mProjMatrix[16];//Projection matrix static float mVMatrix[16];//Camera matrix static float mMVPMatrix[16];//Total matrix static float vulkanClipMatrix[16];//Clipping space matrix X invariant Y reversed Z halved static float mStack[10][16];//Stack of protection transformation matrix static int stackTop;//Stack top position static float cx, cy, cz;//Camera position static void setInitStack();//Initialization matrix static void pushMatrix();//Protection transformation matrix static void popMatrix();//Recovery transformation matrix //Each function method required in the matrix state management class is defined, static void translate(float x, float y, float z);//Translate along the x, y, z axes static void rotate(float angle, float x, float y, float z);//Rotates about the specified axis static void scale(float x, float y, float z);//Matrix scaling static void setCamera//Set up camera ( float cx, float cy, float cz, float tx, float ty, float tz, float upx, float upy, float upz ); static void setProjectFrustum//Set perspective projection parameters ( float left, float right, float bottom, float top, float near, float far ); static float* getFinalMatrix();//Get final matrix static float* getMMatrix();//Gets the current transformation matrix }; #endif
MatrixState3D.cpp
#include "MatrixState3D.h" #include <math.h> float MatrixState3D::vulkanClipMatrix[16]; float MatrixState3D::currMatrix[16]; float MatrixState3D::mProjMatrix[16]; float MatrixState3D::mVMatrix[16]; float MatrixState3D::mMVPMatrix[16]; float MatrixState3D::mStack[10][16]; int MatrixState3D::stackTop = -1; float MatrixState3D::cx, MatrixState3D::cy, MatrixState3D::cz; //setInitStack method for initializing basic transformation matrix void MatrixState3D::setInitStack() { //First, call the setIdentityM method of the Matrix class to set the basic transformation Matrix as the identity Matrix Matrix::setIdentityM(currMatrix, 0); //Then the specific values of each element of Vulkan special standard equipment space adjustment matrix are given one by one //Initializes the matrix of transformation from OpenGL standard device space to Vulkan device space //The three axes of XYZ in OpenGL standard equipment space range from - 1.0 to + 1.0 //The range of three axes XYZ in Vulkan equipment space is - 1.0 ~ + 1.0, + 1.0 ~ - 1.0 and 0.0 ~ + 1.0 respectively //In essence, the scaling and translation matrix is used in the transformation //The X axis remains unchanged, the Y axis is reversed, and the Z axis is scaled by 0.5 //After zooming, the Z axis translates forward by 0.5 vulkanClipMatrix[0] = 1.0f; vulkanClipMatrix[1] = 0.0f; vulkanClipMatrix[2] = 0.0f; vulkanClipMatrix[3] = 0.0f; vulkanClipMatrix[4] = 0.0f; vulkanClipMatrix[5] = -1.0f; vulkanClipMatrix[6] = 0.0f; vulkanClipMatrix[7] = 0.0f; vulkanClipMatrix[8] = 0.0f; vulkanClipMatrix[9] = 0.0f; vulkanClipMatrix[10] = 0.5f; vulkanClipMatrix[11] = 0.0f; vulkanClipMatrix[12] = 0.0f; vulkanClipMatrix[13] = 0.0f; vulkanClipMatrix[14] = 0.5f; vulkanClipMatrix[15] = 1.0f; } //There are two methods to protect the site and restore the site. Both methods complete the established functions based on the stack realized by two-dimensional array. //In actual development, these two methods are often called in the process of scene rendering, //With the gradual in-depth study of this book, readers should be able to realize this. void MatrixState3D::pushMatrix() { stackTop++; for (int i = 0; i<16; i++) { mStack[stackTop][i] = currMatrix[i]; } } void MatrixState3D::popMatrix() { for (int i = 0; i<16; i++) { currMatrix[i] = mStack[stackTop][i]; } stackTop--; } //Three functional methods of superimposing translation, rotation and scaling transformation in the current basic transformation matrix. void MatrixState3D::translate(float x, float y, float z) { Matrix::translateM(currMatrix, 0, x, y, z); } void MatrixState3D::rotate(float angle, float x, float y, float z) { Matrix::rotateM(currMatrix, 0, angle, x, y, z); } void MatrixState3D::scale(float x, float y, float z) { Matrix::scaleM(currMatrix, 0, x, y, z); } //The function of the method of setting the camera is to generate the camera observation matrix according to the 9 parameters of the received camera, and store the elements of the generated matrix in the array mVMatrix. void MatrixState3D::setCamera ( float cx, float cy, float cz, float tx, float ty, float tz, float upx, float upy, float upz ) { MatrixState3D::cx = cx; MatrixState3D::cy = cy; MatrixState3D::cz = cz; Matrix::setLookAtM ( mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz ); \ } void MatrixState3D::setProjectFrustum ( float left, float right, float bottom, float top, float near, float far ) { Matrix::frustumM(mProjMatrix, 0, left, right, bottom, top, near, far); } //Two methods for setting perspective projection and orthogonal projection parameters. Its function is to generate Perspective (orthogonal) projection matrix according to the received 6 parameters, and store the elements of the generated matrix in the array mProjMatri. //At present, the basic transformation matrix camera observation matrix projection matrix is a method to generate the final transformation matrix. This method multiplies the camera observation matrix, projection matrix, Vulkan standard equipment space adjustment matrix and the current basic transformation matrix to generate the total transformation matrix float* MatrixState3D::getFinalMatrix() { Matrix::multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0); Matrix::multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); Matrix::multiplyMM(mMVPMatrix, 0, vulkanClipMatrix, 0, mMVPMatrix, 0); return mMVPMatrix; } float* MatrixState3D::getMMatrix() { return currMatrix; }
stay
MyVulkanManager.cpp MyVulkanManager.h
float MyVulkanManager::xAngle = 0; float MyVulkanManager::yAngle = 0; float MyVulkanManager::zAngle = 0; //add to int MyVulkanManager::vpCenterX; int MyVulkanManager::vpCenterY;
stay
util.cpp mouse events
void handle_xcb_event(const xcb_generic_event_t *event) { uint8_t event_code = event->response_type & ~0x80; switch (event_code) { case XCB_EXPOSE: // TODO: Resize window break; case XCB_CLIENT_MESSAGE: if ((*(xcb_client_message_event_t *)event).data.data32[0] == (*atom_wm_delete_window).atom) { MyVulkanManager::loopDrawFlag = false; } break; case XCB_KEY_RELEASE: { const xcb_key_release_event_t *key = (const xcb_key_release_event_t *)event; switch (key->detail) { case 0x9: // Escape MyVulkanManager::loopDrawFlag = false; break; } } case XCB_BUTTON_PRESS: { xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event; print_modifiers(ev->state); printf("XCB_BUTTON_PRESS"); switch (ev->detail) { case 3: printf("Press the left mouse button\n"); //Press the left mouse button preX = ev->event_x; //Abscissa of mouse preY = ev->event_y; //Ordinate of mouse MyVulkanManager::vpCenterX = preX; //Set vpCenterX as the x coordinate of the touch point MyVulkanManager::vpCenterY = preY; //Set vpCenterY as the y coordinate of the touch point if (preX > MyVulkanManager::screenWidth / 4 * 3) //Judge whether the x coordinate of the touch point is greater than the maximum allowable value MyVulkanManager::vpCenterX = MyVulkanManager::screenWidth / 4 * 3; if (preX < MyVulkanManager::screenWidth / 4) //Judge whether the x coordinate of the touch point is less than the allowable minimum value MyVulkanManager::vpCenterX = MyVulkanManager::screenWidth / 4; if (preY > MyVulkanManager::screenHeight / 4 * 3) //Judge whether the y coordinate of the touch point is greater than the maximum allowable value MyVulkanManager::vpCenterY = MyVulkanManager::screenHeight / 4 * 3; if (preY < MyVulkanManager::screenHeight / 4) //Judge whether the y coordinate of the touch point is less than the allowable minimum value MyVulkanManager::vpCenterY = MyVulkanManager::screenHeight / 4; case 4: printf("Wheel Button up in window %ld, at coordinates (%d,%d)\n", ev->event, ev->event_x, ev->event_y); break; case 5: printf("Wheel Button down in window %ld, at coordinates (%d,%d)\n", ev->event, ev->event_x, ev->event_y); break; default: printf("Button %d pressed in window %ld, at coordinates (%d,%d)\n", ev->detail, ev->event, ev->event_x, ev->event_y); } break; } case XCB_BUTTON_RELEASE: { xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event; print_modifiers(ev->state); switch (ev->detail) { case 3: mouseLeftDown = false; printf("mouseLeftDown\n"); break; default: break; } break; } case XCB_MOTION_NOTIFY: { xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event; if (mouseLeftDown) { int x = ev->event_x; //Abscissa of mouse int y = ev->event_y; //Ordinate of mouse float xDis = (float)(x - preX); float yDis = (float)(y - preY); MyVulkanManager::yAngle = MyVulkanManager::yAngle + xDis * 180 / 200; MyVulkanManager::zAngle = MyVulkanManager::zAngle + yDis * 180 / 200; preX = x; preY = y; } break; } default: break; } }
MyVulkanManager::drawObject() modify viewport parameters
Call the setProjectOrtho method of MatrixState3D class to set the orthogonal projection parameters. As mentioned earlier, after the objects in the scene are projected to the near plane, they will eventually be mapped to the viewport on the device screen. The so-called viewport, that is, the rectangular area specified on the device screen, is set through the following code.
//Center of initial status screen vpCenterX = screenWidth / 2; vpCenterY = screenHeight / 2; VkViewport viewportList[1]; //Viewport information sequence viewportList[0].minDepth = 0.0f; //Viewport minimum depth viewportList[0].maxDepth = 1.0f; //Viewport maximum depth //viviewports.x viewports.y is the coordinate of the upper left side point of the viewport rectangle in the screen coordinate system used by the viewport viewportList[0].x = vpCenterX - screenWidth / 4; //Viewport X coordinates viewportList[0].y = vpCenterY - screenHeight / 4; //Viewport Y coordinates //viewports.width,viewports.height is the width and height of the viewport. viewportList[0].width = screenWidth / 2; //Viewport width viewportList[0].height = screenHeight / 2; //Viewport height vkCmdSetViewport( //Setting Viewports cmdBuffer, //Command buffer 0, //Index of the first viewport 1, //Number of viewports viewportList //Viewport information sequence );
The viewport is half the height and width of the screen
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-6ZoqCjCx-1628421849284)(/home/xz / picture / untitled window _018.png)]
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-TxVaLtsX-1628421849289)(/home/xz / picture / Peek 2021-08-08 14-05.gif)]
Second example
The scene of this case is composed of a group of hexagonal stars of the same size farther and farther away from the observation point. Because the projection method adopted is orthogonal projection, the size of each hexagonal star finally displayed on the screen is the same
Remove triangledata h TriangleData. CPP add sixpointedstar cpp SixPointedStar. h
#ifndef VULKANEXBASE_STARDATA_H #define VULKANEXBASE_STARDATA_H. prevent duplicate definitions class SixPointedStar { public: static float* vdata; Vertex data pointer static int dataByteCount; //Number of vertex data bytes static int vCount; //Number of vertices static void genStarData(float R, float r, float z); //Method for calculating hexagonal data } #endif
#include "SixPointedStar.h" #include <vector> #include <math.h> #include <string.h> float* SixPointedStar::vdata; int SixPointedStar::dataByteCount; int SixPointedStar::vCount; float toRadians(float degree) { return degree*3.1415926535898 / 180; } void SixPointedStar::genStarData(float R, float r, float z) { std::vector<float> alVertix;//A list of vertex coordinates float tempAngle = 360 / 6; //Angular spacing of hexagonal stars float UNIT_SIZE = 1;//Unit size //The vertex coordinates of each triangle used to form a hexagonal star are calculated in batches //Calculate the vertex coordinates of two triangles at a time (6 vertices in total) //After the calculation, the calculated vertex coordinates are stored in the vertex coordinate data list alVertix. for (float angle = 0; angle<360; angle += tempAngle) { //Each angle of a hexagonal star is calculated once alVertix.push_back(0); //x coordinate of the first point alVertix.push_back(0); //y coordinate of the first point alVertix.push_back(z); //z coordinate of the first point alVertix.push_back((float)(R*UNIT_SIZE*cos(toRadians(angle)))); //x coordinate of the second point alVertix.push_back((float)(R*UNIT_SIZE*sin(toRadians(angle)))); //y coordinate of the second point alVertix.push_back(z); //z coordinate of the second point alVertix.push_back((float)(r*UNIT_SIZE*cos(toRadians(angle + tempAngle / 2)))); //x coordinate of the third point alVertix.push_back((float)(r*UNIT_SIZE*sin(toRadians(angle + tempAngle / 2))));//y coordinate of the third point alVertix.push_back(z); //z coordinate of the third point alVertix.push_back(0); //x coordinate of the fourth point alVertix.push_back(0); //y coordinate of the fourth point alVertix.push_back(z); //z coordinate of the fourth point alVertix.push_back((float)(r*UNIT_SIZE*cos(toRadians(angle + tempAngle / 2)))); //x coordinate of the fifth point alVertix.push_back((float)(r*UNIT_SIZE*sin(toRadians(angle + tempAngle / 2)))); //y coordinate of the fifth point alVertix.push_back(z); //z coordinate of the fifth point alVertix.push_back((float)(R*UNIT_SIZE*cos(toRadians(angle + tempAngle)))); //x coordinate of the sixth point alVertix.push_back((float)(R*UNIT_SIZE*sin(toRadians(angle + tempAngle)))); //y coordinate of the sixth point alVertix.push_back(z); //z coordinate of the sixth point } vCount = alVertix.size() / 3; //Calculate the number of vertices dataByteCount = alVertix.size() * 2 * sizeof(float); //Total bytes of vertex data vdata = new float[alVertix.size() * 2]; //Create a vertex data array int index = 0; //Auxiliary array index //Store vertex coordinate data and color data into vertex data array for (int i = 0; i<vCount; i++) { //Fills the vertex data array with data vdata[index++] = alVertix[i * 3 + 0]; //Save the x coordinate of the current vertex vdata[index++] = alVertix[i * 3 + 1]; //Save the current vertex y coordinate vdata[index++] = alVertix[i * 3 + 2]; //Save current vertex z coordinate //Note that the color of the center point is white (RGB is [1,1,1]) if (i % 3 == 0) { //If center point vdata[index++] = 1; //Center point color R channel value vdata[index++] = 1; //Center point color G channel value vdata[index++] = 1; //Center point color B channel value } else //The color of non center points is blue (RGB is [0.45,0.75,0.75]) { //If not the center point vdata[index++] = 0.45f; //Non center point color R channel value vdata[index++] = 0.75f; //Non center point color G channel value vdata[index++] = 0.75f; //Non center point color B channel value } } }
Open myvulkanmanager cpp
Change triForDraw in MyVulkanManager to objForDraw
Modify the code in void MyVulkanManager::createDrawableObject()
oid MyVulkanManager::createDrawableObject() { SixPointedStar::genStarData(0.2, 0.5, 0); objForDraw = new DrawableObjectCommonLight(SixPointedStar::vdata, SixPointedStar::dataByteCount, SixPointedStar::vCount, device, memoryroperties); }
Set camera parameters and projection matrix
MatrixState3D.h add
static void setProjectOrtho ( float left, float right, float bottom, float top, float near, float far );
MatrixState3D.cpp
void MatrixState3D::setProjectOrtho ( float left, float right, float bottom, float top, float near, float far ) { Matrix::orthoM(mProjMatrix, 0, left, right, bottom, top, near, far); //Call the orthoM method to complete the operation }
//The method mainly includes three tasks void MyVulkanManager::initMatrix() { //The first is to set up the camera MatrixState3D::setCamera(0, 0, 2, 0, 0, 0, 0, 1, 0); //Set up camera //The second is to initialize the basic transformation matrix MatrixState3D::setInitStack(); //Initialize basic transformation matrix float ratio = (float)screenWidth / (float)screenHeight; //Calculate screen aspect ratio //The third is to set the orthogonal projection parameters MatrixState3D::setProjectOrtho(-ratio, ratio, -1, 1, 1.0f, 20); //Set orthographic projection parameters }
Remove rotation
void MyVulkanManager::flushUniformBuffer() { float* vertexUniformData = MatrixState3D::getFinalMatrix(); uint8_t *pData; VkResult result = vkMapMemory(device, sqsCL->memUniformBuf, 0, sqsCL->bufferByteCount, 0, (void **)&pData); assert(result == VK_SUCCESS); memcpy(pData, vertexUniformData, sqsCL->bufferByteCount); vkUnmapMemory(device, sqsCL->memUniformBuf); }
Add in void MyVulkanManager::drawObject()
//Rotates the coordinate system by a specified angle about the axis before painting the entire scene //Angle and angle are two variables defined in the MVulkanManager class header file //The value can be changed by changing the position of the touch point MatrixState3D::pushMatrix(); //Protect the site MatrixState3D::rotate(xAngle, 1, 0, 0); //Rotate xAngle about the X axis MatrixState3D::rotate(yAngle, 0, 1, 0); //Rotate the Y angle about the Y axis //Multiple hexagonal stars are drawn in different positions by combining translation transformation and protection / recovery field operation. for (int i = 0; i <= 5; i++) { //Cycle through all hexagonal stars MatrixState3D::pushMatrix(); //Protect the site MatrixState3D::translate(0, 0, i*0.5); //Translate along Z axis objForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout, //Draw object sqsCL->pipeline, &(sqsCL->descSet[0])); MatrixState3D::popMatrix(); } //Restore site MatrixState3D::popMatrix();
DrawableObjectCommonLight. Add members to class H
float* pushConstantData; //The first address pointer of the constant data array to be pushed
DrawableObjectCommonLight. Add in CPP
pushConstantData = new float[16]; //Initialization of push constant data array
DrawableObjectCommonLight::drawSelf
float* mvp = MatrixState3D::getFinalMatrix(); //Get total transformation matrix memcpy(pushConstantData, mvp, sizeof(float) * 16); //Copy the total transformation matrix into memory vkCmdPushConstants(cmd, pipelineLayout, //Feed constant data into pipeline VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 16, pushConstantData);
Modify and create pipeline code ShaderQueueSuit_Common.cpp
create_ pipeline_ Add in layout function
//add to const unsigned push_constant_range_count = 1; //Number of constant blocks pushed VkPushConstantRange push_constant_ranges[push_constant_range_count] = {}; push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; //Specifies the structure type push_constant_ranges[0].offset = 0; //Push constant data start offset push_constant_ranges[0].size = sizeof(float) * 16; //Total number of constant data bytes pushed //modify pPipelineLayoutCreateInfo.pushConstantRangeCount = push_constant_range_count; pPipelineLayoutCreateInfo.pPushConstantRanges = push_constant_ranges; //Push constant range list
Finally, modify the color code
vertshadertext.vert
#version 400 #extension GL_ARB_separate_shader_objects: enable / / start GL_ARB_separate_shader_objects #extension GL_ARB_shading_language_420pack: enable / / start GL_ARB_shading_language_420pack layout (push_constant) uniform bufferVals ///Push constant block {//Consistent block mat4 mvp;//Total transformation matrix } myConstantVals; layout (location = 0) in vec3 pos;//Vertex position of the incoming object coordinate system layout (location = 1) in vec3 color;//Incoming vertex color layout (location = 0) out vec3 vcolor;//The vertex color passed to the slice shader out gl_PerVertex {//Output interface block vec4 gl_Position;//Vertex final position }; void main() {//Main function gl_Position = myConstantVals.mvp * vec4(pos,1.0);//Calculate final vertex position vcolor=color;//Pass vertex colors to the slice shader }
I don't know why I can only draw a hexagonal star. It's strange