OpenGL学习笔记(八)相机的控制

终于到了OpenGl入门教学的最后一章Camera,说真的,这个Camera是真的挺难的。
话不多说,上代码吧!
首先是先创建一个Camera类
Camera.h

#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
class Camera
{
public: 
	Camera(glm::vec3 position,glm::vec3 target,glm::vec3 worldUp);//通过目标点来控制视角
	Camera(glm::vec3 position, float pitch, float yaw, glm::vec3 worldUp);//通过欧拉角控制视角
	glm::vec3 Position;//相机位置
	glm::vec3 WorldUp;//世界坐标的竖直方向
	glm::vec3 ForWard;//模型前方
	glm::vec3 Right;//模型右方
	glm::vec3 Up;//模型上方
	float Pitch;//俯仰角
	float Yaw;//偏航角
	float Sensitivity=0.01f;
	void CameraViewMove(float offsetx,float offsety);//相机视野的移动
	void CameraPosMoveZ(float offsetz);//相机位置Z轴上的移动
	void CameraPosMoveX(float offsetx);//相机位置X轴上的移动

	glm::mat4 GetViewMatrix();//获取视角矩阵
private:
	void UpdateViewPosition();//刷新相机位置
};

Camera.cpp

#include "Camera.h"

Camera::Camera(glm::vec3 position, glm::vec3 target, glm::vec3 worldUp)//通过glm::lookAt使用相机功能
{
	Position = position;
	WorldUp = worldUp;
	ForWard = glm::normalize(target - position);//用目标点位置减去相机位置,再做一个归一化
	Right = glm::normalize(glm::cross(ForWard, WorldUp));//向量之间的叉乘得到的是垂直于该平面的向量,如x与z,得到的是y
	Up = glm::normalize(glm::cross(Right,ForWard));
	//GetViewMatrix();
}
Camera::Camera(glm::vec3 position, float pitch, float yaw, glm::vec3 worldUp) {
	Position = position;
	WorldUp = worldUp;
	Pitch = pitch;
	Yaw = yaw;
	//下面是通过俯仰角和偏航角来计算前方位置,记住在这个上,官方教程有误,按本教程写,想知道原理的看傅老师的camera(2)
	ForWard.x = sin(Yaw) * cos(Pitch);
	ForWard.z = cos(Yaw)*cos(Pitch);
	ForWard.y =  sin(Pitch);

	ForWard = glm::normalize(ForWard);
	Right = glm::normalize(glm::cross(ForWard, WorldUp));
	Up = glm::normalize(glm::cross(Right, ForWard));
}
void Camera::CameraViewMove(float offsetx, float offsety)//相机视角的移动,通过鼠标上一坐标点与下一坐标点差值做比较所得
{
	Pitch -= offsety*Sensitivity;
	Yaw -= offsetx* Sensitivity;
	UpdateViewPosition();
}
void Camera::CameraPosMoveZ( float offsetz)//在模型z轴上进行移动
{
	Position += ForWard * offsetz*0.1f;
}
void Camera::CameraPosMoveX(float offsetx)//在模型x轴上进行移动
{

	Position += Right * offsetx * 0.1f;
}
glm::mat4 Camera::GetViewMatrix()//获取视角矩阵
{
	return glm::lookAt(Position, Position+ForWard,Up);
}
void Camera::UpdateViewPosition()//刷新视角位置
{
	ForWard.x = sin(Yaw) * cos(Pitch);
	ForWard.z = cos(Yaw) * cos(Pitch);
	ForWard.y = sin(Pitch);
	ForWard = glm::normalize(ForWard);
	Right = glm::normalize(glm::cross(ForWard, WorldUp));
	Up = glm::normalize(glm::cross(Right, ForWard));

}

然后是main.cpp,这次我会将所有的代码都贴出来,相当于对入门阶段的学习,做一个完结吧!

#include<iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include<GLFW/glfw3.h>
#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"
using namespace std;
void ProcessInput(GLFWwindow* window);

//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    // 左上
//};
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
};
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)
};
GLuint indices[] = { 3,2,1,3,1,0 };

void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;
Camera* myCamera = new Camera(glm::vec3(0, 0, 5.0f), glm::radians(-15.0f), glm::radians(180.0f), glm::vec3(0, 1.0f, 0));


// The MAIN function, from here we start the application and run the game loop
int main(int argc, char* argv[])
{

	
	// Init GLFW
	glfwInit();
	// Set all the required options for GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

	// Create a GLFWwindow object that we can use for GLFW's functions
	GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	glfwMakeContextCurrent(window);
	//隐藏鼠标
	glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_DISABLED);//新增
	//鼠标移动时呼叫mouse_callback函数
	glfwSetCursorPosCallback(window, mouse_callback);//新增

	// Set the required callback functions
	//glfwSetKeyCallback(window, key_callback);

	// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
	glewExperimental = GL_TRUE;
	 Initialize GLEW to setup the OpenGL Function pointers
	glewInit();

	// Define the viewport dimensions
	glViewport(0, 0, WIDTH, HEIGHT);
	
	GLuint VBO, VAO;//声明顶点缓冲,声明顶点数组用于管理顶点数据
	glGenVertexArrays(1, &VAO);//创建顶点数组,返回一个独一无二的整数,标识数组
	glGenBuffers(1, &VBO);//创建顶点缓冲,返回一个独一无二的整数,标识缓冲区

	glBindVertexArray(VAO);//绑定顶点数组
	glBindBuffer(GL_ARRAY_BUFFER, VBO);//绑定顶点缓冲
  //指定顶点数组的数据源为vertices,第四个参数代表显卡如何管理给定的数据,GL_STATIC_DRWA代表几乎不会改变
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//先存在vbo里面,然后在给vao

	// 指定顶点属性的解析方式。即,如何从顶点缓冲获取相应的顶点属性和相应的颜色属性。或者说,顶点着色器中如何知道去哪个顶点属性分量重着色呢
   //对每一个顶点而言,属性有2种,一是位置属性,而是颜色属性,因此每六个浮点数决定了一个顶点的位置和颜色

	//顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值(Location),因此第一个参数,代表属性分量的索引
	//参数二:顶点位置属性的维度,参数三:属性向量的数据类型,参数四:是否标准化;参数五,顶点位置属性的总字节长度,参数六:在缓冲数组中的偏移量,即起始位置
	glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);//从vao里面在0号索引位上拿取三个值
	//glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 8* sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));//从vao里面在0号索引位上拿取三个值
	glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));//从vao里面在0号索引位上拿取三个值

	glEnableVertexAttribArray(3);//启用属性0,因为默认是禁用的
	//glEnableVertexAttribArray(4);//启用属性0,因为默认是禁用的
	glEnableVertexAttribArray(5);//启用属性0,因为默认是禁用的

	GLuint EBO;
	glGenBuffers(1, &EBO);//创建一个缓冲区
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//绑定一个元素缓冲区
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//定义缓冲区中的数据,既对顶点数组的索引

  //顶点数组对象(Vertex Array Object, VAO)的好处就是,当配置顶点属性指针时,你只需要将上面的代码调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。如下文循环中的绑定再解绑
	glBindVertexArray(0); // 解绑 VAO

	unsigned int texbufferA;//贴图缓冲区ID
	glGenTextures(1, &texbufferA);//创建
	glActiveTexture(GL_TEXTURE0);//申请textbuffer中的缓冲号,这里申请的是一号
	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
	{
		printf("stbi load fail!");
	}

	stbi_image_free(data);//释放空间

	unsigned int texbufferB;//贴图缓冲区ID
	glGenTextures(1, &texbufferB);
	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, texbufferB);//绑定
	stbi_set_flip_vertically_on_load(true);
	unsigned char* data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);//载入图像,宽度、高度和颜色通道的个数

	if (data2)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);//生成贴图
		glGenerateMipmap(GL_TEXTURE_2D);//生成多级纹理
	}
	else
	{
		printf("stbi load fail!");
	}
	stbi_image_free(data2);//释放空间
	glm::mat4 viewMat;

	glm::mat4 projMat;

	projMat = glm::perspective(glm::radians(45.0f), (float)WIDTH / (float)HEIGHT, 0.01f, 100.0f);
	glEnable(GL_DEPTH_TEST);
	Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");

	// Game loop
	while (!glfwWindowShouldClose(window))
	{	// 检查事件,调用相应的回调函数,如下文的glfwInput函数
		ProcessInput(window);
		
		//trans = glm::translate(trans, glm::vec3(0.01f, 0, 0));

		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_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, texbufferB);//绑定

		glBindVertexArray(VAO);//每次循环都调用,绑定函数绑定VAO
		//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	
		viewMat = myCamera->GetViewMatrix();//刷新视角//修改
			for (size_t i = 1; i <= 10; i++)
		{
			glm::mat4 modelMat;//模型矩阵
			modelMat = glm::translate(modelMat, cubePositions[i]);
			if(i==1||i%3==0)
			modelMat = glm::rotate(modelMat, (float)glfwGetTime(), glm::vec3(0, 1.0f, 0));
			else
				modelMat = glm::rotate(modelMat, glm::radians(i*10.0f), glm::vec3(0, 1.0f, 0));
			myShader->Use();
			glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0);
			glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 2);
			//glUniformMatrix4fv(glGetUniformLocation(myShader->ID,"transform"),1,GL_FALSE,glm::value_ptr(trans));
			glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "modelMat"), 1, GL_FALSE, glm::value_ptr(modelMat));
			glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "viewMat"), 1, GL_FALSE, glm::value_ptr(viewMat));
			glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "projMat"), 1, GL_FALSE, glm::value_ptr(projMat));


			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
	
		//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制三角形,根据索引数组绘制6个顶点,索引数组类型为GL_UNSIGNED_INT,偏移值为0
		//glDrawArrays(GL_TRIANGLES, 0, 3);//开始绘制三角形从0起始,画三组数值
		//glBindVertexArray(0);//解绑

		// Swap the screen buffers
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	// Properly de-allocate all resources once they've outlived their purpose
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	// Terminate GLFW, clearing any resources allocated by GLFW.
	glfwTerminate();
	return 0;
}

// Is called whenever a key is pressed/released via GLFW
void ProcessInput(GLFWwindow* window)//通过监控鼠标来控制相机的移动//新增
{
	
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, GL_TRUE);
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		myCamera->CameraPosMoveZ(1);
	}
	else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		myCamera->CameraPosMoveZ( -1);
	}
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		myCamera->CameraPosMoveX(1);
	}
	else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		myCamera->CameraPosMoveX(-1);
	}
}
bool firstMouse = true;
float lastx = 300, lasty = 400;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)//检查鼠标输入//新增
{
	if (firstMouse==true)//第一次调用该函数的时候直接将当前坐标给过去坐标值,已此来防止一开始的时候出现视角的巨大晃动
	{
		lastx = xpos;
		lasty = ypos;
		firstMouse = false;
	}
	float offsetx = xpos - lastx;
	float offsety = ypos - lasty;
	//std::cout << offsetx << std::endl;
	lastx = xpos;
	lasty = ypos;
	myCamera->CameraViewMove(offsetx,offsety);//调用CameraViewMove函数
}


最后的效果是一个类似于FPS第一人称视角的相机控制。不会发视频,还有就是难得去找,截几张图吧!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值