# Vulkan development practice detailed learning notes - projection transformation

Posted by Amgine on Tue, 28 Dec 2021 04:43:37 +0100

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;
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;
{
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

```	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);
}
```

```		//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
```

```	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

```#version 400
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

Topics: Vulkan