计算机图形学(七)——画一个可以由鼠标键盘控制的立方体

画一个可以由鼠标键盘控制的立方体

本节将会再OpenGL中配置一个摄像机,让正方体能够再3D场景中自由移动,同时也会讨论鼠标和键盘输入。OpenGL本身没有相机的概念,我们可以通过把物体往相反的方向移动来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。
首先需要修改core.vs

#version 330 core
layout(location = 0) in vec3 position;
layout (location = 1) in vec3 color;
uniform mat4 transform;
uniform mat4 view;
uniform mat4 projection;

out vec3 outColor;

void main()
{
gl_Position = projection * view * transform * vec4(position,1.0f);
outColor=color;
}

片源着色器core.fs不用作修改

#version 330 core
in vec3 outColor;
out vec4 color;
void main()
{
color = vec4(outColor,1.0f);
}

接下来定义头文件Camera.h

#pragma once
#include<vector>
#define GLFW_STATIC
#include<GL/glew.h>

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

enum Camera_Movement
{
	FOWARD,
	BACKWARD,
	LEFT,
	RIGHT
};

//6个自由度

const GLfloat YAW = -90.0f;//
const GLfloat PITCH = 0.0f;
const GLfloat SPEED = 6.0f;
const GLfloat SENSITIVITY = 0.25f;//灵敏度
const GLfloat ZOOM = 45.0f;

class Camera
{
public:
	Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
		GLfloat yaw = YAW, GLfloat pitch = PITCH):front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED),
		mouseSensitivity(SENSITIVITY), zoom(ZOOM)
	{
		this->position = position;
		this->worldUp = up;
		this->yaw = yaw;
		this->pitch = pitch;
		this->updateCameraVectors();
	}
	Camera(GLfloat posX,GLfloat posY,GLfloat posZ,GLfloat upX,GLfloat upY,GLfloat upZ,
		GLfloat yaw = YAW, GLfloat pitch = PITCH) :front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED),
		mouseSensitivity(SENSITIVITY), zoom(ZOOM)
	{
		this->position = glm::vec3(posX,posY,posZ);
		this->worldUp = glm::vec3(upX,upY,upZ);
		this->yaw = yaw;
		this->pitch = pitch;
		this->updateCameraVectors();
	}
	void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)//时间控制速度
	{
		GLfloat velocity = this->movementSpeed * deltaTime;

		if (direction == FOWARD) {
			this->position += this->front * velocity;
		}
		if (direction == BACKWARD) {
			this->position -= this->front * velocity;
		}
		if (direction == LEFT) {
			this->position -= this->right * velocity;
		}
		if (direction == RIGHT) {
			this->position += this->right * velocity;
		}
	}
	void ProcessMouseMovement(GLfloat xOffset,GLfloat yOffset)
	{
		xOffset *= this->mouseSensitivity;
		yOffset *= this->mouseSensitivity;

		this->yaw += xOffset;
		this->pitch += yOffset;

		this->updateCameraVectors();
	}
	void ProcessScroll(GLfloat yOffset)
	{
		this->zoom += yOffset;
	}
	GLfloat GetZoom()
	{
		return this->zoom;
	}
	glm::mat4 GetViewMatrix()
	{//视角矩阵
		return glm::lookAt(this->position, this->position + this->front, this->up);
	}
private:
	GLfloat yaw;
	GLfloat pitch;
	GLfloat movementSpeed;
	GLfloat mouseSensitivity;
	GLfloat zoom;

	glm::vec3 position;
	glm::vec3 front;
	glm::vec3 up;
	glm::vec3 right;
	glm::vec3 worldUp;
	void updateCameraVectors()
	{
		//相机的位置和朝向(前、上、右)
		glm::vec3 front;
		front.x = cos(glm::radians(this->pitch)) * cos(glm::radians(this->yaw));
		front.y = sin(glm::radians(this->pitch));
		front.z = cos(glm::radians(this->pitch)) * sin(glm::radians(this->yaw));

		this->front = glm::normalize(front);
		//normalize取单位化
		this->right = glm::normalize(glm::cross(this->front, this->worldUp));
		this->up = glm::normalize(glm::cross(this->right, this->front));
	}
};

欧拉角

欧拉角可以表示3D空间中任何旋转的3个值,由莱昂哈德·欧拉(Leonhard Euler)在18世纪提出。一共有3种欧拉角:俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll),下面的图片展示了它们的含义:
在这里插入图片描述
俯仰角是描述我们如何往上或往下看的角,可以在第一张图中看到。第二张图展示了偏航角,偏航角表示我们往左和往右看的程度。滚转角代表我们如何翻滚摄像机,通常在太空飞船的摄像机中使用。每个欧拉角都有一个值来表示,把三个角结合起来我们就能够计算3D空间中任何的旋转向量了。

对于我们的摄像机系统来说,我们只关心俯仰角和偏航角,所以我们不会讨论滚转角。给定一个俯仰角和偏航角,我们可以把它们转换为一个代表新的方向向量的3D向量。
Shader.h不做改动

#pragma once

#include <string.h>
#include <fstream>
#include <sstream>
#include <iostream>

#include <GL/glew.h>

class Shader
{
	GLuint vertex, fragment;
public:
	GLuint Program;
	Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
	{
		std::string vertexCode;
		std::string fragmentCode;
		std::ifstream vShaderFile;
		std::ifstream fShaderFile;

		vShaderFile.exceptions(std::ifstream::badbit);
		fShaderFile.exceptions(std::ifstream::badbit);

		try
		{
			vShaderFile.open(vertexPath);
			fShaderFile.open(fragmentPath);

			std::stringstream vShaderStream, fShaderStream;
			vShaderStream << vShaderFile.rdbuf();
			fShaderStream << fShaderFile.rdbuf();

			vShaderFile.close();
			fShaderFile.close();

			vertexCode = vShaderStream.str();
			fragmentCode = fShaderStream.str();

		}
		catch (std::ifstream::failure e)
		{
			std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << std::endl;
		}
		const GLchar* vShaderCode = vertexCode.c_str();
		const GLchar* fShaderCode = fragmentCode.c_str();

		//compile Shaders
		GLint success;
		GLchar infoLog[512];
		vertex = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex, 1, &vShaderCode, NULL);
		glCompileShader(vertex);

		glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(vertex, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
				<< infoLog << std::endl;
		}

		fragment = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment, 1, &fShaderCode, NULL);
		glCompileShader(fragment);

		glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(fragment, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
				<< infoLog << std::endl;
		}

		this ->Program = glCreateProgram();
		glAttachShader(this->Program, vertex);
		glAttachShader(this->Program, fragment);
		glLinkProgram(this->Program);

		glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
		if (!success) {
			glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n"
				<< infoLog << std::endl;
		}
	}
	~Shader() {
		glDetachShader(this->Program, vertex);
		glDetachShader(this->Program, fragment);
		glDeleteShader(vertex);
		glDeleteShader(fragment);
		glDeleteProgram(this->Program);
	}
	void Use()
	{
		glUseProgram(this->Program);
	}
};


main.cpp

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

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

const GLint WIDTH = 800, HEIGHT = 600;

void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode);

void MouseCallback(GLFWwindow* window, double xPos, double yPos);
void ScrollCallback(GLFWwindow* window, double xOffset, double yOffset);
void DoMovement();

GLfloat lastX = 0.0f;
GLfloat lastY = 0.0f;
bool firstMouse = true;

bool keys[1024];

Camera camera(glm::vec3(0.0f, 0.0f, 2.0f));
GLfloat deltaTime = 0.0f;
GLfloat lastTime = 0.0f;

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

	GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	int screenWidth, screenHeight;
	glfwGetFramebufferSize(window, &screenWidth, &screenHeight);

	if (window == nullptr) {
		std::cout << "Failed to create GLFW window" <<
			std::endl;
		glfwTerminate();
		return  -1;
	}

	glfwMakeContextCurrent(window);

	glfwSetKeyCallback(window, KeyCallback);
	glfwSetCursorPosCallback(window, MouseCallback);
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	glfwSetScrollCallback(window,ScrollCallback);

	glewExperimental = GL_TRUE;

	if (glewInit() != GLEW_OK) {
		std::cout << "Failed to initialize GLEW" <<
			std::endl;
		glfwTerminate();
		return -1;
	}

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//允许深度测试
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);//深度信息小的覆盖信息大的

	Shader shader = Shader("res/shaders/core.vs","res/shaders/core.fs");
	Shader lightShader = Shader("res/shaders/light.vs", "res/shaders/light.fs");
	//Cube
	GLfloat vertices[] =
	{
		//position			//color
		//back
		-0.5,-0.5f,-0.5f,   1.0f,0.0f,0.0f,
		0.5f,-0.5f,-0.5f,   1.0f,0.0f,0.0f,
		0.5f,0.5f,-0.5f,    1.0f,0.0f,0.0f,
		0.5f,0.5f,-0.5f,    1.0f,0.0f,0.0f,
		-0.5f,0.5f,-0.5f,   1.0f,0.0f,0.0f,
		-0.5f,-0.5f,-0.5f,  1.0f,0.0f,0.0f,
		//front
		-0.5,-0.5f,0.5f,   0.0f,1.0f,0.0f,
		0.5f,-0.5f,0.5f,   0.0f,1.0f,0.0f,
		0.5f,0.5f,0.5f,    0.0f,1.0f,0.0f,
		0.5f,0.5f,0.5f,    0.0f,1.0f,0.0f,
		-0.5f,0.5f,0.5f,   0.0f,1.0f,0.0f,
		-0.5f,-0.5f,0.5f,  0.0f,1.0f,0.0f,
		//left
		-0.5f,0.5f,0.5f,	0.0f,0.0f,1.0f,
		-0.5f,0.5f,-0.5f,	0.0f,0.0f,1.0f,
		-0.5f,-0.5f,-0.5f,	0.0f,0.0f,1.0f,
		-0.5f,-0.5f,-0.5f,	0.0f,0.0f,1.0f,
		-0.5f,-0.5f,0.5f,	0.0f,0.0f,1.0f,
		-0.5f,0.5f,0.5f,	0.0f,0.0f,1.0f,
		//right
		0.5f,0.5f,0.5f,		0.0f,1.0f,1.0f,
		0.5f,0.5f,-0.5f,	0.0f,1.0f,1.0f,
		0.5f,-0.5f,-0.5f,	0.0f,1.0f,1.0f,
		0.5f,-0.5f,-0.5f,	0.0f,1.0f,1.0f,
		0.5f,-0.5f,0.5f,	0.0f,1.0f,1.0f,
		0.5f,0.5f,0.5f,		0.0f,1.0f,1.0f,
		//bottom
		-0.5f,-0.5f,-0.5f,	1.0f,0.0f,1.0f,
		0.5f,-0.5f,-0.5f,	1.0f,0.0f,1.0f,
		0.5f,-0.5f,0.5f,	1.0f,0.0f,1.0f,
		0.5f,-0.5f,0.5f,	1.0f,0.0f,1.0f,
		-0.5f,-0.5f,0.5f,	1.0f,0.0f,1.0f,
		-0.5f,-0.5f,-0.5f,	1.0f,0.0f,1.0f,
		//up
		-0.5f,0.5f,-0.5f,	1.0f,1.0f,0.0f,
		0.5f,0.5f,-0.5f,	1.0f,1.0f,0.0f,
		0.5f,0.5f,0.5f,		1.0f,1.0f,0.0f,
		0.5f,0.5f,0.5f,		1.0f,1.0f,0.0f,
		-0.5f,0.5f,0.5f,	1.0f,1.0f,0.0f,
		-0.5f,0.5f,-0.5f,	1.0f,1.0f,0.0f,
	};

	GLuint VAO, VBO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	GLuint VAO1, VBO1;
	glGenVertexArrays(1, &VAO1);
	glGenBuffers(1, &VBO1);
	glBindVertexArray(VAO1);
	glBindBuffer(GL_ARRAY_BUFFER, VBO1);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	while (!glfwWindowShouldClose(window)) {
		GLfloat currentTime = glfwGetTime();
		deltaTime = currentTime - lastTime;
		lastTime = currentTime;

		glViewport(0, 0, screenWidth, screenHeight);
		glfwPollEvents();
		DoMovement();//在PollEvents后响应
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//用到都要初始化
		shader.Use();
		glm::mat4 view = camera.GetViewMatrix();

		glm::mat4 projection = glm::perspective(glm::radians(camera.GetZoom()),
			static_cast<GLfloat>(screenWidth) / static_cast<GLfloat>(screenHeight), 0.1f, 100.0f);

		GLuint viewLoc = glGetUniformLocation(shader.Program, "view");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));

		GLuint projectionLoc = glGetUniformLocation(shader.Program, "projection");
		glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

		glm::mat4 transform = glm::mat4(1.0f);

		//平移
		transform = glm::translate(transform, glm::vec3(0.0f, 0.4f, 0.0f));
		//旋转
		transform = glm::rotate(transform, glm::radians(20.0f)*static_cast<GLfloat>(glfwGetTime()), glm::vec3(1.0f, 1.0f, 1.0f));
		//缩放
		transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.5f));
		
		

		GLuint transLoc = glGetUniformLocation(shader.Program, "transform");
		glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));

		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 36);
		glBindVertexArray(0);

		lightShader.Use();
		viewLoc = glGetUniformLocation(lightShader.Program, "view");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));

		projectionLoc = glGetUniformLocation(lightShader.Program, "projection");
		glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

		transform = glm::mat4(1.0f);

		transform = glm::rotate(transform, glm::radians(20.0f) * static_cast<GLfloat>(glfwGetTime()), glm::vec3(1.0f, 1.0f, 1.0f));
		transform = glm::translate(transform, glm::vec3(0.0f, 2.0f, 0.0f));
		transform = glm::scale(transform, glm::vec3(0.1f, 0.1f, 0.1f));
		transLoc = glGetUniformLocation(lightShader.Program, "transform");
		glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));

		glBindVertexArray(VAO1);
		glDrawArrays(GL_TRIANGLES, 0, 36);
		glBindVertexArray(0);

		glfwSwapBuffers(window);
	}
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glfwTerminate();
	return 0;
}

void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	if (key >= 0 && key < 1024) {
		if (action == GLFW_PRESS) {
			keys[key] = true;
		}
		else if (action == GLFW_RELEASE)
		{
			keys[key] = false;
		}
	}
}
void MouseCallback(GLFWwindow* window, double xPos, double yPos)
{
	if (firstMouse) {
		lastX = xPos;
		lastY = yPos;
		firstMouse = false;
	}
	GLfloat xOffset = xPos - lastX;
	GLfloat yOffset = lastY - yPos;

	lastX = xPos;
	lastY = yPos;

	camera.ProcessMouseMovement(xOffset, yOffset);
}
void DoMovement()
{
	if (keys[GLFW_KEY_W] || keys[GLFW_KEY_UP]) {
		//up
		camera.ProcessKeyboard(FOWARD,deltaTime);
	}
	if (keys[GLFW_KEY_S] || keys[GLFW_KEY_DOWN]) {
		//down
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	}
	if (keys[GLFW_KEY_A] || keys[GLFW_KEY_LEFT]) {
		//left
		camera.ProcessKeyboard(LEFT, deltaTime);
	}
	if (keys[GLFW_KEY_D] || keys[GLFW_KEY_RIGHT]) {
		//right
		camera.ProcessKeyboard(RIGHT, deltaTime);
	}
}
void ScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
{
	camera.ProcessScroll(yOffset);
}

运行结果

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值