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第一人稱視角的相機控制。不會發視頻,還有就是難得去找,截幾張圖吧!
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章