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

Topics: Vulkan