OpenGL learning record (5)

References:
https://learnopengl.com/
https://learnopengl-cn.github.io/

What we want to implement this time is the Transform transformation function. OpenGL does not come with any knowledge of matrices and vectors, and you can use already-made math libraries, such as GLM. You can download the GLM library from here , and you need to import the following header files when using it:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

To perform operations such as displacement, rotation, and scaling on an image, a matrix is ​​inseparable. Therefore, in the vertex shader code, define a four-dimensional matrix variable and multiply it with gl_Position to realize the transformation function.

//顶点着色器编码
#version 330 core										
layout(location = 0) in vec3 aPos;						
layout(location = 1) in vec3 aColor;		
layout(location = 2) in vec2 aTexCoord;	

uniform mat4 transform;

out vec4 vertexColor;						  
out vec2 TexCoord;

void main() {
    
    											
	gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);    
	vertexColor = vec4(aColor, 1.0f);	
	TexCoord = aTexCoord;
}

In the loop drawing phase in the main function, you can use the glUniformMatrix4fv function to send the matrix data to the shader. The first parameter is the position value of the uniform. Use glGetUniformLocation to obtain the position value of the uniform variable required in the corresponding shader. We define a uniform mat4 transform, so the position value of the transform variable is obtained. The second parameter tells OpenGL how many matrices we are going to send, 1 here. The third parameter asks if we want to transpose our matrix, that is, swap the rows and columns of our matrix. OpenGL developers typically use an internal matrix layout called a Column-major Ordering layout. The default layout of GLM is column-major order, so there is no need to transpose the matrix, so fill in GL_FALSE. The last parameter is the real matrix data, but GLM does not store their matrices as OpenGL expects to accept, so we need to use GLM's own function value_ptr to transform these data first.

glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "transform"), 1, GL_FALSE, glm::value_ptr(trans)); //把矩阵数据发送给着色器

Next, you only need to define the trans matrix, which is the real transformation matrix, which can determine the transformation method of the image. Declare a four-dimensional matrix using glm::mat4. glm::translate can convert it into a displacement transformation matrix, which is the value of the corresponding elements in the first three rows in the fourth column. Its parameters require filling in the matrix to be transformed and the three-dimensional displacement transformation vector. The displacement matrix is ​​as follows:

insert image description here

glm::rotate can convert it into a rotation transformation matrix, fill in the matrix to be transformed, the angle to be transformed (actually required is the radian value, use glm::radians to convert the angle to radians), and decide which axis to rotate around The three-dimensional rotation variable of . The transformation matrix for rotation around the x, y, and z axes is as follows:

insert image description here

glm::scale converts to a scaling matrix, passing in the matrix to be transformed, and a three-dimensional scaling vector (that is, the element values ​​on the diagonal of the first three rows and the first three columns of the matrix). The scaling matrix is ​​as follows:

insert image description here

It is worth noting that matrix multiplications are not commutative, which means their order matters. For example, if the model is rotated and then displaced, the model will be displaced in a new direction because the frontal orientation of the model has changed after the rotation, which is not in line with the original expectation. In order to make the transformation more intuitive, the scaling operation should be performed first, then the rotation, and finally the displacement. The matrix is ​​operated with the vector in the form of left multiplication, so when several matrices are multiplied, the "rightmost" matrix will first affect the vector, so it should be transformed by displacement such as Mt * Mr * Ms * v Matrix, rotation transformation matrix, and scaling operation matrix are multiplied in turn as the final transformation matrix. So the code for the transformation matrix is ​​as follows:

//变换矩阵
glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(0.3f, 0.3f, 0.2f)); //位移
trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0, 0, 1.0f)); //旋转
trans = glm::scale(trans, glm::vec3(0.5f, 0.5f, 0.5f)); //缩放

In this way, the effect of the trans matrix on the image is: first scale the three dimensions of xyz to 0.5 times the original, then rotate 45 degrees around the Z axis, and then move the image by 0.3 in the x direction and 0.3 in the y direction , move 0.2 in the z direction. The code of the main program file and the result after running are as follows:

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//顶点数据
float vertices[] = {
    
    
	//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
		 0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
		 0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
		-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
		-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
};

//顶点索引
unsigned int indices[] = {
    
    
	0, 1, 2,   //第一个三角形使用的顶点
	2, 3, 0    //第二个三角形使用的顶点
};


//检查输入函数
void processInput(GLFWwindow* window)
{
    
    
	//按下ESC键时,将WindowShouldClose设为true,循环绘制将停止
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
	{
    
    
		glfwSetWindowShouldClose(window, true);
	}
}

//视口改变时的回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    
    
	glViewport(0, 0, width, height); //OpenGL渲染窗口的尺寸大小
}

int main()
{
    
    
	glfwInit(); //初始化GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //告诉GLFW要使用OpenGL的版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //主版本号、次版本号都为3,即3.3版本
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //告诉GLFW使用核心模式(Core-profile)

	//打开 GLFW Window
	GLFWwindow* window = glfwCreateWindow(1600, 1200, "My OpenGL Game", nullptr, nullptr);
	if (window == nullptr) //若窗口创建失败,打印错误信息,终止GLFW并return -1
	{
    
    
		printf("Open window failed.");
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window); //创建OpenGL上下文
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //用户改变窗口大小的时候,视口调用回调函数

	//初始化GLEW
	glewExperimental = true;
	if (glewInit() != GLEW_OK) //若GLEW初始化失败,打印错误信息并终止GLFW窗口
	{
    
    
		printf("Init GLEW failed.");
		glfwTerminate();
		return -1;
	}


	Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");

	//创建VAO(顶点数组对象)
	unsigned int VAO;
	glGenVertexArrays(1, &VAO); //生成一个顶点数组对象
	glBindVertexArray(VAO); //绑定VAO

	//创建VBO(顶点缓冲对象)
	unsigned int VBO;
	glGenBuffers(1, &VBO); //生成缓冲区对象。第一个参数是要生成的缓冲对象的数量,第二个是要输入用来存储缓冲对象名称的数组,由于只需创建一个VBO,因此不需要用数组形式
	glBindBuffer(GL_ARRAY_BUFFER, VBO); //把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上,GL_ARRAY_BUFFER是一种顶点缓冲对象的缓冲类型。OpenGL允许同时绑定多个缓冲,只要它们是不同的缓冲类型。
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //把定义的数据复制到当前绑定缓冲的函数。参数1:目标缓冲类型,参数2:指定传输数据的大小(以字节为单位),参数3:我们希望发送的实际数据,参数4:指定显卡如何管理给定的数据,GL_STATIC_DRAW表示数据不会或几乎不会改变。

	//创建EBO(元素缓冲对象/索引缓冲对象)
	unsigned int EBO;
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


	//位置属性
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); //告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)。参数1:要配置的顶点属性(layout(location = 0),故0),参数2:指定顶点属性的大小(vec3,故3),参数3:指定数据的类型,参数4:是否希望数据被标准化,参数5:在连续的顶点属性组之间的间隔,参数6:表示位置数据在缓冲中起始位置的偏移量(Offset)。
	glEnableVertexAttribArray(0); //以顶点属性位置值作为参数,启用顶点属性,由于前面声明了layout(location = 0),故为0

	//颜色属性
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	//UV属性
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2);

	//使用纹理单元0绑定TexBufferA
	unsigned int TexBufferA;
	glGenTextures(1, &TexBufferA);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, TexBufferA);

	int width, height, nrChannel;
	stbi_set_flip_vertically_on_load(true); //翻转图像y轴

	//加载并生成第一张纹理
	unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannel, 0);
	if (data)
	{
    
    
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
    
    
		std::cout << "图片加载失败" << std::endl;
	}
	stbi_image_free(data); //释放

	//使用纹理单元3绑定TexBufferB
	unsigned int TexBufferB;
	glGenTextures(1, &TexBufferB);
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, TexBufferB);

	//加载并生成第二张纹理
	unsigned char* data2 = stbi_load("awesomeface1.png", &width, &height, &nrChannel, 0);
	if (data2)
	{
    
    
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
    
    
		std::cout << "图片加载失败" << std::endl;
	}
	stbi_image_free(data2);

	//变换矩阵
	glm::mat4 trans;
	trans = glm::translate(trans, glm::vec3(0.3f, 0.3f, 0.2f)); //位移
	trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0, 0, 1.0f)); //旋转
	trans = glm::scale(trans, glm::vec3(0.5f, 0.5f, 0.5f)); //缩放

	//让程序在手动关闭之前不断绘制图像
	while (!glfwWindowShouldClose(window))
	{
    
    
		//检测输入
		processInput(window);

		//渲染指令
		glClearColor(0.f, 0.5f, 0.5f, 1.0f); //设置清空屏幕所用的颜色
		glClear(GL_COLOR_BUFFER_BIT); //清空屏幕的颜色缓冲区

		//绑定纹理到对应的纹理单元
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, TexBufferA);
		glActiveTexture(GL_TEXTURE3);
		glBindTexture(GL_TEXTURE_2D, TexBufferB);

		glBindVertexArray(VAO); //绘制物体的时候就拿出相应的VAO,绑定它
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); //绑定EBO
		

		myShader->use();

		//告诉OpenGL每个着色器采样器属于哪个纹理单元
		glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0); 
		glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 3);

		glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "transform"), 1, GL_FALSE, glm::value_ptr(trans)); //把矩阵数据发送给着色器

		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //绘制,参数1:绘制模式(三角形),参数2:绘制顶点数(两个三角形6个顶点),参数3:索引的类型,参数4:指定EBO中的偏移量。


		//检查并调用事件,交换缓冲区
		glfwSwapBuffers(window); //交换颜色缓冲区,前缓冲区保存最终输出的图像,后缓冲区负责绘制渲染指令,当渲染指令执行完毕后,交换前后缓冲区,使完整图像呈现出来,避免逐像素绘制图案时的割裂感
		glfwPollEvents(); //检查触发事件,如键盘输入、鼠标移动等
	}


	glfwTerminate(); //关闭GLFW并退出
	return 0;
}

insert image description here

Guess you like

Origin blog.csdn.net/weixin_47260762/article/details/128203559