OpenGL-利用摄像机实现三维空间漫游

目录

项目运行结果

摄像机相关知识

摄像机的位置

摄像机的方向

摄像机的右方

摄像机的上方

LookAt矩阵

欧拉角

鼠标控制

鼠标滑动(转动)

鼠标滚轮(缩放)

键盘控制

代码

Camera.h

main.cpp

资源下载


项目运行结果

最终结果

摄像机相关知识

摄像机的位置

摄像机也在3维空间中,我们可以用一个三维向量来表示摄像机所在的空间位置

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);

摄像机的方向

提到方向,必然要有两个坐标,两个一减就有了方向。也就是摄像机的位置减去要看的目标的坐标。

glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);

摄像机的右方

先定义一个上向量(Up Vector)。接下来把上向量和上面得到的方向向量进行叉乘。不论上向量与方向向量如何,叉乘必然垂直与它,这样就可以得到摄像机的右方,有了右方也就有了左方。

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); 
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));

摄像机的上方

现在我们已经有了x轴向量和z轴向量,获取一个指向摄像机的正y轴向量就相对简单了:我们把右向量和方向向量进行叉乘

到现在位置,一个确定了的摄像机就出现了,它的位置,方向,上下左右均确定了。

glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);

LookAt矩阵

来源于LearnOpenGL

我们得到了 RUDP 4个向量,按上述填好即可,就得到了我们的view矩阵。当然,glm库已经集成好了,你只需要填入位置,看的目标,还有up即可自动生成,返回给view矩阵即可。

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), 
           glm::vec3(0.0f, 0.0f, 0.0f), 
           glm::vec3(0.0f, 1.0f, 0.0f));

后面我们把上述代码封装到了Camera类里面,只需要在main函数里面这样写即可:

 viewMat = camera.GetViewMatrix();

欧拉角

来源于LearnOpenGL

欧拉通过飞机的俯仰角、偏航角和滚转角确定摄像机的旋转向量,由于滚转角会让用户头晕,一般是不会用到的,后面的摄像机类就不用滚转角作为参数了。

鼠标控制

进行鼠标控制之前,我们先把白色的鼠标显示关掉,以免影响效果。

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

鼠标滑动(转动)

我们写自己的mouse_callback回调函数,你可以输出鼠标的位置变化,在后面的代码中会有涉及,至于俯仰角和偏航角的改变,以及摄像机的front向量等我们封装到摄像机类里面。

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if(firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; 
    lastX = xpos;
    lastY = ypos;
    float sensitivity = 0.05;
    xoffset *= sensitivity;
    yoffset *= sensitivity;
    yaw   += xoffset;
    pitch += yoffset;
    if(pitch > 89.0f)
        pitch = 89.0f;
    if(pitch < -89.0f)
        pitch = -89.0f;
    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

用glfw的glfwSetCursorPosCallback来注册回调函数。

glfwSetCursorPosCallback(window, mouse_callback);

鼠标滚轮(缩放)

我们还会来实现一个缩放(Zoom)接口。视野(Field of View)或fov定义了我们可以看到场景中多大的范围。当视野变小时,场景投影出来的空间就会减小,产生放大(Zoom In)了的感觉。我们会使用鼠标的滚轮来放大。与鼠标移动、键盘输入一样,我们需要一个鼠标滚轮的回调函数:

//鼠标滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}

我们现在在每一帧都必须把透视投影矩阵上传到GPU

projMat = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f);//(透视投影,远小近大)

同样,我们把Zoom封装到了Camera类里面。

键盘控制

我们已经为GLFW的键盘输入定义过一个processInput函数了,当然,之前只有按Esc键会关闭窗口,现在,我们来新添加几个需要检查的按键命令:

//查询GLFW相关按键是否被按下/释放,根据情况作出反应
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
	camera.MovementSpeed = 2.5f; ; // 按键移动速度
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::RIGHT, deltaTime);
}

通过WASD键进行前后左右的移动,最后将摄像机的位置传给摄像机类的对象即可。

代码

相对于上篇文章,我们增加了摄像机类,在main函数中加了摄像机,进行了 鼠标和键盘控制。这里只粘贴修改部分的代码,如果你跟着我做到了现在,你应该是可以解决的,最后我会把最终的代码上传,入门部分结束。

Camera.h

#pragma once
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>

// Default camera values
//默认的相机值
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;       //速度
const float SENSITIVITY = 0.1f; 
const float ZOOM = 45.0f;       //缩放级别

// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
//一个抽象的相机类,它处理输入并计算相应的欧拉角度、矢量和矩阵,以便在OpenGL中使用
class Camera
{
public:
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
//为摄像机移动定义了几种可能的选项。 一种抽象的用来远离窗口系统的特定输入方法
       enum Camera_Movement {
	   FORWARD,
	   BACKWARD,
	   LEFT,
	   RIGHT
        };
	// Camera Attributes
	//照相机属性
	glm::vec3 Position; //位置
	glm::vec3 Front;
	glm::vec3 Up;
	glm::vec3 Right;
	glm::vec3 WorldUp;
	// Euler Angles
	//欧拉角
	float Yaw;
	float Pitch;
	// Camera options
	//相机选项
	float MovementSpeed;    //移动速度
	float MouseSensitivity; //鼠标灵敏度
	float Zoom;             
	// Constructor with vectors
	//用矢量的构造函数
	Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = position;
		WorldUp = up;
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}
	// Constructor with scalar values
	//用标量的构造函数
	Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = glm::vec3(posX, posY, posZ);
		WorldUp = glm::vec3(upX, upY, upZ);
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}

	// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
	//返回使用欧拉角和LookAt矩阵计算的视图矩阵
	glm::mat4 GetViewMatrix()
	{
		return glm::lookAt(Position, Position + Front, Up);
	}

	// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
	//处理从任何类似键盘的输入系统接收的输入。 以摄像机定义的ENUM形式接受输入参数(从窗口系统中抽象出来)
	void ProcessKeyboard(Camera_Movement direction, float deltaTime)
	{
		float velocity = MovementSpeed * deltaTime;
		if (direction == FORWARD)
			Position += Front * velocity;
		if (direction == BACKWARD)
			Position -= Front * velocity;
		if (direction == LEFT)
			Position -= Right * velocity;
		if (direction == RIGHT)
			Position += Right * velocity;
	}

	// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
	// 处理从鼠标输入系统接收的输入。 期待获取x和y方向的偏移值。
	void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
	{
		xoffset *= MouseSensitivity;
		yoffset *= MouseSensitivity;
		Yaw += xoffset;
		Pitch += yoffset;
		// Make sure that when pitch is out of bounds, screen doesn't get flipped
		//确保当pitch超出范围时,屏幕不会翻转
		if (constrainPitch)
		{
			if (Pitch > 89.0f)
				Pitch = 89.0f;
			if (Pitch < -89.0f)
				Pitch = -89.0f;
		}
		// Update Front, Right and Up Vectors using the updated Euler angles
		// 使用更新的欧拉角进而更新Front、Right和Up矢量
		updateCameraVectors();
	}

	// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
	// 处理从鼠标滚轮事件接收的输入。 只需要在垂直轮轴上的输入
	void ProcessMouseScroll(float yoffset)
	{
		if (Zoom >= 1.0f && Zoom <= 45.0f)
			Zoom -= yoffset;
		if (Zoom <= 1.0f)
			Zoom = 1.0f;
		if (Zoom >= 45.0f)
			Zoom = 45.0f;
	}

private:
	// Calculates the front vector from the Camera's (updated) Euler Angles
	// 从Camera(更新的)欧拉角计算Front向量
	void updateCameraVectors()
	{
		// Calculate the new Front vector
		//计算新的Front向量
		glm::vec3 front;
		front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		front.y = sin(glm::radians(Pitch));
		front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		Front = glm::normalize(front);
		// Also re-calculate the Right and Up vector
		//同样,重新计算Right和Up向量
		// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
		//单位化矢量,因为它们的长度越接近0,你向上或向下看时时间越多,导致移动速度变慢。
		Right = glm::normalize(glm::cross(Front, WorldUp));  
		Up = glm::normalize(glm::cross(Right, Front));
	}
};

main.cpp

//头文件
#include <glad/glad.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>
#include "Camera.h"
//函数声明
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
// 设置窗体宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//鼠标
float lastX;
float lastY;
bool firstMouse = true;
float fov = 1.0f;
//摄像机
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间
//使用Camera的矢量构造函数 传参:位置 up yaw pitch
Camera camera(cameraPos, cameraUp, glm::radians(180.0f), glm::radians(0.0f));
//主函数
int main()
{
	// glfw: 初始化和配置
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
#endif
	// glfw 窗体生成
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Camera", NULL, NULL);//设置标题
	//判断窗体是否生成
	if (window == NULL)
	{
		std::cout << "生成GLFW窗口失败" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//关闭鼠标显示
	// glad: 加载所有的OpenGL功能指针----------------------------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "初始化GLAD失败" << std::endl;
		return -1;
	}
	//开启深度测试
	glEnable(GL_DEPTH_TEST);
	// 建立并编译着色器--------------------------------------------------------------------
	Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");

	// 设置点数据 (还有缓冲) 配置点的属性(包含点坐标等) 这里设置了4个,将以索引的方式选择点来画三角形
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
	};
	unsigned int indices[] = {  
		0, 1, 2,  // 第一个三角形选择索引为 0 1 3的三个点
		2, 3, 0,  // 第一个三角形选择索引为 1 2 3的三个点
	};
	//10个立方体的坐标 
	glm::vec3 cubePositions[] = {
		glm::vec3(0.0f,  0.0f,  0.0f),
		glm::vec3(2.0f,  5.0f, -15.0f),
		glm::vec3(-1.5f, -2.2f, -2.5f),
		glm::vec3(-3.8f, -2.0f, -12.3f),
		glm::vec3(2.4f, -0.4f, -3.5f),
		glm::vec3(-1.7f,  3.0f, -7.5f),
		glm::vec3(1.3f, -2.0f, -2.5f),
		glm::vec3(1.5f,  2.0f, -2.5f),
		glm::vec3(1.5f,  0.2f, -1.5f),
		glm::vec3(-1.3f,  1.0f, -1.5f)
	};
	unsigned int VBO, VAO, EBO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);  //注意,这里使用EBO作为缓冲对象
	// 绑定顶点数组, 然后绑定并设置缓冲, 最后配置顶点属性-------------------------------
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
	//修改属性-----------------------------------------------------------------------------
	glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(6);
	glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(8);
	//纹理-------------------------------------------------------------------------------
	unsigned int TexBufferA, TexBufferB;
	stbi_set_flip_vertically_on_load(true);//y轴翻转
	//木箱部分
	glGenTextures(1, &TexBufferA);
	//激活
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, TexBufferA);
	glDrawArrays(GL_TRIANGLES, 0, 36);
	glBindTexture(GL_TEXTURE_2D, TexBufferA);//绑定
	int width, height, nrChannels;
	unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 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);//释放
	//笑脸部分
	glGenTextures(1, &TexBufferB);
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, TexBufferB);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
	glBindTexture(GL_TEXTURE_2D, TexBufferB);//绑定
	unsigned char *data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);//注意,有Alpha通道
		glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理
	}
	else
	{
		std::cout << "加载纹理失败" << std::endl;
	}
	stbi_image_free(data2);
	//注意这是允许的,对glVertexAttribPointer的调用将VBO注册为顶点属性的绑定顶点缓冲区对象,所以之后我们可以安全地解除绑定
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	// 您可以在之后取消绑定VAO,以便其他VAO调用不会意外地修改此VAO,但这种情况很少发生。无论如何, 
	// 修改其他VAO需要调用glBindVertexArray,因此我们通常不会在不直接需要时解除VAO(VBO同样)的绑定。
	glBindVertexArray(0);
	//glm相关------------------------------------------------------------------
	// 下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
	// 下面这行代码就需要改为:
	// glm::mat4 trans = glm::mat4(1.0f)
	glm::mat4 trans;
	glm::mat4 modelMat;
	modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	glm::mat4 viewMat;
	glm::mat4 projMat;
	while (!glfwWindowShouldClose(window))
	{
		//在每一帧中我们计算出新的deltaTime以备后用。
		float currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;
		glfwSetCursorPosCallback(window, mouse_callback);//鼠标回调函数
		glfwSetScrollCallback(window, scroll_callback);  //鼠标滚轮回调函数
		processInput(window);// 键盘输入
		Sleep(200);
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		//清除颜色及深度测试
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//绑定纹理
		glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
		glBindTexture(GL_TEXTURE_2D, TexBufferA);
		glActiveTexture(GL_TEXTURE3); // 在绑定纹理之前先激活纹理单元
		glBindTexture(GL_TEXTURE_2D, TexBufferB);
		glBindVertexArray(VAO);
		/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
		viewMat = camera.GetViewMatrix();
		projMat = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f);//(透视投影,远小近大)
		for (int i = 0;i < 10;++i)
		{
			glm::mat4 modelMat2;
			modelMat2 = glm::translate(modelMat2, cubePositions[i]);
			float angle = 20.0f * i;//每个旋转角度不同
			modelMat2 = glm::rotate(modelMat2, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
			glDrawArrays(GL_TRIANGLES, 0, 36);
			//使用着色器
			myShader->use();
			glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0); // 手动设置
			myShader->setInt("ourFace", 3); // 或者使用着色器类设置
			unsigned int modelMatLoc = glGetUniformLocation(myShader->ID, "modelMat");
			unsigned int viewMatLoc = glGetUniformLocation(myShader->ID, "viewMat");
			unsigned int projMatLoc = glGetUniformLocation(myShader->ID, "projMat");
			glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, glm::value_ptr(modelMat2));//使用modelMat的话就是一个,拿出for循环
			glUniformMatrix4fv(viewMatLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
			glUniformMatrix4fv(projMatLoc, 1, GL_FALSE, glm::value_ptr(projMat));
		}
		// 画矩形--------------------------------------------------------------------------
		//可以知道我们只有一个三角形VAO,没必要每次都绑定它,但是我们这么做会让代码有一点组织性
		glBindVertexArray(VAO);
		/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
		glDrawArrays(GL_TRIANGLES, 0, 36);
		// glBindVertexArray(0); //没必要每次都解绑 
		// 交换buffers和poll的IO事件 (按键按下/释放,鼠标移动等.)
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	//一旦他们超出已有的资源,就取消所有资源的分配:
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);
	// glfw:终止,清空之前所有的GLFW的预分配资源
	glfwTerminate();
	return 0;
}
//查询GLFW相关按键是否被按下/释放,根据情况作出反应
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
	camera.MovementSpeed = 2.5f; ; // 按键移动速度
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(Camera::RIGHT, deltaTime);
}
// g无论窗口大小何时改变(由操作系统或用户自己)这个回调函数将会被执行
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	//确定viewport与新的窗口尺寸匹配; 请注意,宽度和高度将明显大于显示器上指定的宽度和高度。
	glViewport(0, 0, width, height);
}
//鼠标晃动回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}
	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos;
	//输出
	std::cout << "xoffset" << xoffset << std::endl;
	std::cout << "yoffset" << yoffset << std::endl;
	lastX = xpos;
	lastY = ypos;
	camera.MouseSensitivity = 0.15f;
	camera.ProcessMouseMovement(xoffset,yoffset);
}
//鼠标滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}

资源下载

https://download.csdn.net/download/lady_killer9/11126577

下载后进行如下设置:

使用vs2015., 打开项目

修改项目属性:

VC++ 目录->包含目录 修改为当前项目下的GL文件

VC++目录->库目录    修改为当前项目下的lib文件

运行即可

更多OpenGL知识:现代OpenGL入门教程

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

发布了163 篇原创文章 · 获赞 471 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/lady_killer9/article/details/89389036