計算機圖形學作業( 四):實現正方體的正交、透視投影並實現一個camera類控制攝像頭

計算機圖形學作業( 四):實現正方體的正交、透視投影並實現一個camera類控制攝像頭

正交投影

正交投影的示例圖如下所示:

上面的平截頭體定義了可見的座標,它由由寬、高、近(Near)平面和遠(Far)平面所指定。任何出現在近平面之前或遠平面之後的座標都會被裁剪掉。這種投影會產生不真實的結果,因爲這個投影沒有將透視(Perspective)考慮進去。

要在程序中使用正射投影,就用以下代碼:

projection = glm::ortho(ortho_left, ortho_right, ortho_bottom, ortho_top, ortho_near, ortho_far);

前兩個參數指定了平截頭體的左右座標,第三和第四參數指定了平截頭體的底部和頂部。通過這四個參數我們定義了近平面和遠平面的大小,然後第五和第六個參數則定義了近平面和遠平面的距離。

最終效果如下圖:

透視投影

透視投影的示例圖如下:

由於透視,你會注意到離你越遠的東西看起來更小,正更符合現實生活體驗。

要使用透視投影,就是用以下代碼:

projection = glm::perspective(glm::radians(perspective_fov), perspective_division, perspective_near, perspective_far);

它的第一個參數定義了fov的值,它表示的是視野(Field of View),並且設置了觀察空間的大小,它的值通常設置爲45.0f。第二個參數設置了寬高比,由視口的寬除以高所得。第三和第四個參數設置了平截頭體的近和遠平面。

最終效果圖如下:

視角變換

把cube放置在(0, 0, 0)處,做透視投影,使攝像機圍繞cube旋轉,並且時刻看着cube中心 。

爲了達成上述目標,首先我們要創建一個攝像機,也就是view觀察矩陣,創建攝像機的觀察矩陣,我們會用到lookAt函數,如下所示:

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));

該函數的第一個參數是攝像機位置,第二個參數是要觀察的目標的位置,第三個參數是攝像機的上向量。

那麼,爲了使攝像機繞cube旋轉並觀察,我們需要修改lookAt函數中的第一個參數,也就是攝像機的位置座標要呈現出一個圓形的變化。我們用以下方法實現:

float radius = 10.0f;
float camX = sin(glfwGetTime()) * radius;
float camZ = cos(glfwGetTime()) * radius;
glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));

這樣的話,攝像機在x0y平面上,就會隨時間變化呈現出一個圓形移動。

最終結果如下:

對攝像機空間與物體空間的理解

在現實生活中,我們一般將攝像機擺放的空間View matrix和被拍攝的物體擺設的空間Model matrix分開,但 是在OpenGL中卻將兩個合二爲一設爲ModelView matrix,通過上面的作業啓發,你認爲是爲什麼呢?

我認爲這是OpenGL中固定管線與可編程管線的區別。
在固定管線中,攝像機擺放的空間View matrix和被拍攝的物體擺設的空間Model matrix是合二爲一的,固定渲染管線的OpenGLES不需要也不允許你自己去定義頂點渲染和像素渲染的具體邏輯,它內部已經固化了一套完整的渲染流程,只需要開發者在CPU代碼端輸入渲染所需要的參數並指定特定的開關,就能完成不同的渲染,有一定的侷限性。
而在可編程管線中,兩種矩陣是分開的,可編程渲染管線的OpenGLES版本必須由開發者自行實現渲染流程,否則無法繪製出最終的畫面。開發者可以根據自己的具體需要來編寫頂點渲染和像素渲染中的具體邏輯,可最大程度的簡化渲染管線的邏輯以提高渲染效率,也可自己實現特定的算法和邏輯來渲染出固定管線無法渲染的效果。具有很高的可定製性,但同時也對開發者提出了更高的要求。

camera類(加分項)

camera類的源碼可以在OpenGL的官方教程中找到:https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h,只要在項目中引用此頭文件即可,重點在於理解和使用該camera類。

移動

爲了使用該camera類,首先我們要初始化一個camera類,並初始化一些參數,如下所示:

Camera camera(glm::vec3(0.0f, 0.0f, 10.0f));
//初始鼠標位置
float mouseX = 0.0f;
float mouseY = 0.0f;
bool firstMouse = true;
//當前幀與上一幀的時間差
float deltaTime = 0.0f;
//上一幀的時刻
float lastFrame = 0.0f;

其中,deltaTime,lastFrame這兩個變量是用來衡量當前計算的渲染速度, 如果我們的deltaTime很大,就意味着上一幀的渲染花費了更多時間。當實現camera的移動速度時,我們要結合機器渲染速度,即deltaTime值,使得camera的移動速度較爲平滑。

然後我們控制鍵盤輸入的在processInput函數中,增添攝像機移動命令,就可實現攝像機的移動,如下:

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(RIGHT, deltaTime);
}

視角

然後便是鼠標對攝像機俯視角和偏航角的控制,我們在main.cpp裏先聲明定義鼠標事件回調函數。如下:

//鼠標移動
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
	if (firstMouse)
	{
		mouseX = xpos;
		mouseY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - mouseX;
	float yoffset = mouseY - ypos;

	mouseX = xpos;
	mouseY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);
}

然後再通知glfw使用這回調函數,如下

glfwSetCursorPosCallback(window, mouse_callback);

還有,最重要的一點是,增加這條配置語句,告訴glfw捕獲我們的鼠標移動,否則視角的左右移動將無法大於90度。

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

放縮

最後是實現滑輪的放縮功能,首先是生命定義滑輪的回調函數,如下:

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
	camera.ProcessMouseScroll(yoffset);
}

然後是通知glfw使用滑輪迴調函數

glfwSetScrollCallback(window, scroll_callback);

最後是在cube的投影矩陣,使用camera類的zoom放縮參數

projection = glm::perspective(glm::radians(camera.Zoom), perspective_division, perspective_near, perspective_far);

效果

最終結果如下所示:

代碼

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>
#include "camera.h"

using namespace std;

const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 800;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow* window);

//頂點着色器可以向片段着色器傳數據
const char* vertexShaderSource = "#version 330 core\n"
	"layout (location = 0) in vec3 aPos;\n"
	"layout (location = 1) in vec3 aColor;\n"
	"out vec3 ourColor;\n"
	"uniform mat4 model;\n"
	"uniform mat4 view;\n"
	"uniform mat4 projection;\n"
	"void main()\n"
	"{\n"
	"gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
	"ourColor = aColor;\n"
	"}\0";

//片段着色器
const char* fragmentShaderSource = "#version 330 core\n"
	"out vec4 FragColor;\n"
	"in vec3 ourColor;\n"
	"void main()\n"
	"{\n"
	"FragColor = vec4(ourColor, 1.0);\n"
	"}\0";

Camera camera(glm::vec3(0.0f, 0.0f, 10.0f));
//鼠標位置
float mouseX = 0.0f;
float mouseY = 0.0f;
bool firstMouse = true;
//每一幀間隔時間
float deltaTime = 0.0f;
//上一幀的時刻
float lastFrame = 0.0f;

int main() {
	//初始化opengl窗口和配置
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	
	GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "LearnOpenGL", NULL, NULL);
	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	//深度測試
	glEnable(GL_DEPTH_TEST);

	//創建並綁定ImGui
	const char* glsl_version = "#version 130";
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	ImGui::StyleColorsDark();
	ImGui_ImplGlfw_InitForOpenGL(window, true);
	ImGui_ImplOpenGL3_Init(glsl_version);

	//頂點着色器
	unsigned int vertexShader;
	vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	glCompileShader(vertexShader);

	//片段着色器
	unsigned int fragmentShader;
	fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);

	//着色器程序
	unsigned int shaderProgram;
	shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);

	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	bool show_window = true;

	unsigned int VAO;
	unsigned int VBO;
	unsigned int EBO;

	//正方體邊長爲2.0f*2s
	float half_edge = 2.0f;

	//顏色數據
	ImVec4 color1 = ImVec4(0.5f, 0.0f, 0.0f, 1.0f);
	ImVec4 color2 = ImVec4(0.0f, 0.5f, 0.0f, 1.0f);
	ImVec4 color3 = ImVec4(0.0f, 0.0f, 0.5f, 1.0f);
	ImVec4 color4 = ImVec4(0.0f, 0.5f, 0.5f, 1.0f);
	ImVec4 color5 = ImVec4(0.5f, 0.0f, 0.5f, 1.0f);
	ImVec4 color6 = ImVec4(0.5f, 0.5f, 0.0f, 1.0f);

	float vertices[] = {
	-half_edge, -half_edge, -half_edge, color1.x, color1.y, color1.z,
	 half_edge, -half_edge, -half_edge, color1.x, color1.y, color1.z,
	 half_edge,  half_edge, -half_edge, color1.x, color1.y, color1.z,
	 half_edge,  half_edge, -half_edge, color1.x, color1.y, color1.z,
	-half_edge,  half_edge, -half_edge, color1.x, color1.y, color1.z,
	-half_edge, -half_edge, -half_edge, color1.x, color1.y, color1.z,

	-half_edge, -half_edge,  half_edge, color2.x, color2.y, color2.z,
	 half_edge, -half_edge,  half_edge, color2.x, color2.y, color2.z,
	 half_edge,  half_edge,  half_edge, color2.x, color2.y, color2.z,
	 half_edge,  half_edge,  half_edge, color2.x, color2.y, color2.z,
	-half_edge,  half_edge,  half_edge, color2.x, color2.y, color2.z,
	-half_edge, -half_edge,  half_edge, color2.x, color2.y, color2.z,

	-half_edge,  half_edge,  half_edge, color3.x, color3.y, color3.z,
	-half_edge,  half_edge, -half_edge, color3.x, color3.y, color3.z,
	-half_edge, -half_edge, -half_edge, color3.x, color3.y, color3.z,
	-half_edge, -half_edge, -half_edge, color3.x, color3.y, color3.z,
	-half_edge, -half_edge,  half_edge, color3.x, color3.y, color3.z,
	-half_edge,  half_edge,  half_edge, color3.x, color3.y, color3.z,

	 half_edge,  half_edge,  half_edge, color4.x, color4.y, color4.z,
	 half_edge,  half_edge, -half_edge, color4.x, color4.y, color4.z,
	 half_edge, -half_edge, -half_edge, color4.x, color4.y, color4.z,
	 half_edge, -half_edge, -half_edge, color4.x, color4.y, color4.z,
	 half_edge, -half_edge,  half_edge, color4.x, color4.y, color4.z,
	 half_edge,  half_edge,  half_edge, color4.x, color4.y, color4.z,

	-half_edge, -half_edge, -half_edge, color5.x, color5.y, color5.z,
	 half_edge, -half_edge, -half_edge, color5.x, color5.y, color5.z,
	 half_edge, -half_edge,  half_edge, color5.x, color5.y, color5.z,
	 half_edge, -half_edge,  half_edge, color5.x, color5.y, color5.z,
	-half_edge, -half_edge,  half_edge, color5.x, color5.y, color5.z,
	-half_edge, -half_edge, -half_edge, color5.x, color5.y, color5.z,

	-half_edge,  half_edge, -half_edge, color6.x, color6.y, color6.z,
	 half_edge,  half_edge, -half_edge, color6.x, color6.y, color6.z,
	 half_edge,  half_edge,  half_edge, color6.x, color6.y, color6.z,
	 half_edge,  half_edge,  half_edge, color6.x, color6.y, color6.z,
	-half_edge,  half_edge,  half_edge, color6.x, color6.y, color6.z,
	-half_edge,  half_edge, -half_edge, color6.x, color6.y, color6.z,
	};

	unsigned int indices[] = { // 注意索引從0開始! 
		0, 1, 2, // 第一個三角形
		3, 4, 5,

		6, 7, 8,
		9, 10, 11,
		
		12, 13, 14,
		15, 16, 17,

		18, 19, 20,
		21, 22, 23,

		24, 25, 26,
		27, 28, 29,

		30, 31, 32,
		33, 34, 35
	};

	//必須先綁定VA0
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	//再綁定VBO
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//使用EBO畫多個三角形,組合成其它圖形
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	//再設置屬性
	//位置屬性
	//屬性位置值爲0的頂點屬性,有3個值,頂點着色器總參數大小爲6個float,位置屬性偏移爲0
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	//顏色屬性
	//屬性位置值爲0的頂點屬性,有3個值,頂點着色器總參數大小爲6個float,位置屬性偏移爲3個float
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	float scaleDelta = 1.0f;
	float rotateAngle = 0.5f;
	float ortho_left = -10.f; 
	float ortho_right = 10.0f;
	float ortho_bottom = -10.0f;
	float ortho_top = 10.0f;
	float ortho_near = 0.1f;
	float ortho_far = 100.0f;
	float perspective_fov = 45.0f;
	float perspective_division = (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT;
	float perspective_near = 0.1f;
	float perspective_far = 100.0f;
	//0代表正交投影,1代表透視投影,2代表使攝像機圍繞cube旋轉
	bool is_ortho = true;
	bool is_perspective = false;
	bool is_view_change = false;
	bool enable_camera = false;
	

	while (!glfwWindowShouldClose(window)) {
		if (enable_camera) {
			//告訴glfw獲取我們的鼠標
			glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
		}
		else {
			glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
		}
	

		//計算當前時間和幀間隔時間
		float currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		processInput(window);

		//創建座標變換
		glm::mat4 model = glm::mat4(1.0f);
		glm::mat4 view = glm::mat4(1.0f);
		glm::mat4 projection = glm::mat4(1.0f);

		if (is_view_change) {
			float radius = 20.0f;
			float camX = sin(glfwGetTime()) * radius;
			float camZ = cos(glfwGetTime()) * radius;
			view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
		}
		else {
			if (enable_camera) {
				view = camera.GetViewMatrix();
			}
			else {
				view = glm::translate(view, glm::vec3(0.0f, 0.0f, -10.0f));
			}
		}

		if (is_ortho) {
			//兩個參數指定了平截頭體的左右座標,第三和第四參數指定了平截頭體的底部和頂部,然後第五和第六個參數則定義了近平面和遠平面的距離
			projection = glm::ortho(ortho_left, ortho_right, ortho_bottom, ortho_top, ortho_near, ortho_far);
		}
		else if (is_perspective) {
			//它的第一個參數定義了fov的值,它表示的是視野,並且設置了觀察空間的大小。第二個參數設置了寬高比。第三和第四個參數設置了平截頭體的近和遠平面
			//glm::radians()是把角度轉爲弧度
			if (enable_camera) {
				//利用鼠標放縮
				projection = glm::perspective(glm::radians(camera.Zoom), perspective_division, perspective_near, perspective_far);
			}
			else {
				projection = glm::perspective(glm::radians(perspective_fov), perspective_division, perspective_near, perspective_far);
			}
			
		}
		else if (is_view_change) {
			projection = glm::perspective(glm::radians(perspective_fov), perspective_division, perspective_near, perspective_far);
		}

		//使用着色器程序
		glUseProgram(shaderProgram);

		//獲取着色器程序uniform
		unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model");
		unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view");
		unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &view[0][0]);
		glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, &projection[0][0]);
		
		glfwPollEvents();

		//清除屏幕
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glBindVertexArray(VAO);
		//model包含了位移、縮放與旋轉操作
		if (is_view_change) {
			model = glm::mat4(1.0f);
			model = glm::scale(model, glm::vec3(scaleDelta, scaleDelta, scaleDelta));
			model = glm::rotate(model, rotateAngle, glm::vec3(1.0f, 0.0f, 0.0f));
			//畫兩個三角形,方便判斷是攝像機旋轉,而不是cube旋轉,第二個參數代表三角形的個數 * 3
			for (int i = 0; i < 2; i++) {
				if (i == 0) {
					model = glm::translate(model, glm::vec3(0, 0, 0));
				}
				else {
					model = glm::translate(model, glm::vec3(-5.5, -3.0, 0.0));
				}
				glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
				glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
			}
		}
		else {
			model = glm::mat4(1.0f);
			model = glm::scale(model, glm::vec3(scaleDelta, scaleDelta, scaleDelta));
			model = glm::translate(model, glm::vec3(-1.5, 0.5, -1.5));
			model = glm::rotate(model, rotateAngle, glm::vec3(1.0f, 0.0f, 0.0f));
			glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
			glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
		}

		//創建imgui
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplGlfw_NewFrame();
		ImGui::NewFrame();

		ImGui::Begin("Edit cube", &show_window, ImGuiWindowFlags_MenuBar);
		if (is_perspective) {
			if (ImGui::Button("enable camera")) {
				if (enable_camera) {
					enable_camera = false;
				}
				else {
					enable_camera = true;
				}
			}
		}

		if (ImGui::RadioButton("ortho", is_ortho)) {
			is_ortho = true;
			is_perspective = false;
			is_view_change = false;
		}
		if (ImGui::RadioButton("perspective", is_perspective)) {
			is_ortho = false;
			is_perspective = true;
			is_view_change = false;
		}
		if (ImGui::RadioButton("View Changing", is_view_change)) {
			is_ortho = false;
			is_perspective = false;
			is_view_change = true;
		}

		if (is_ortho) {
			ImGui::SliderFloat("left", &ortho_left, -50.0f, 50.0f);
			ImGui::SliderFloat("right", &ortho_right, -50.0f, 50.0f);
			ImGui::SliderFloat("bottom", &ortho_bottom, -50.0f, 50.0f);
			ImGui::SliderFloat("top", &ortho_top, -50.0f, 50.0f);
			ImGui::SliderFloat("near", &ortho_near, 0.1f, 50.0f);
			ImGui::SliderFloat("far", &ortho_far, 0.1f, 50.0f);
		}
		else if (is_perspective) {
			ImGui::SliderFloat("fov", &perspective_fov, 0.0f, 100.0f);
			ImGui::SliderFloat("width/height", &perspective_division, 0.0f, 5.0f);
			ImGui::SliderFloat("near", &perspective_near, 0.1f, 50.0f);
			ImGui::SliderFloat("far", &perspective_far, 0.1f, 50.0f);
		}
		
		ImGui::End();

		ImGui::Render();
		int display_w, display_h;
		glfwMakeContextCurrent(window);
		glfwGetFramebufferSize(window, &display_w, &display_h);
		glViewport(0, 0, display_w, display_h);
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		glfwSwapBuffers(window);
	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);

	glfwTerminate();

	return 0;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(RIGHT, deltaTime);
}

//鼠標移動
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
	if (firstMouse)
	{
		mouseX = xpos;
		mouseY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - mouseX;
	float yoffset = mouseY - ypos;

	mouseX = xpos;
	mouseY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);
}

//鼠標放縮
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
	camera.ProcessMouseScroll(yoffset);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章