參考:https://blog.csdn.net/ynsenyu/article/details/39423493
#include <iostream>
#include <cmath>
#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "utils/Shader.h"
//圖片工具庫
#include "utils/stb_image.h"
//矩陣工具庫
#include "utils/glm/glm.hpp"
#include "utils/glm/gtc/matrix_transform.hpp"
#include "utils/glm/gtc/type_ptr.hpp"
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);
void loadTexture(unsigned int *texture, const char *path);
void mouse_callback(GLFWwindow *window, double xpos, double ypos);
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset);
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.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 lastFrameTime = 0.0f;//上一幀的時間
float lastX = 300;
float lastY = 300;
float yaw;
float pitch;
float fov = 45.0f;//視野比例
/**
* 頂點數組對象:Vertex Array Object,VAO
* 頂點緩衝對象:Vertex Buffer Object,VBO
* 索引緩衝對象:Element Buffer Object,EBO或Index Buffer Object,IBO
* @return
*/
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_CORE_PROFILE);
//第三個參數是標題
GLFWwindow *window = glfwCreateWindow(600, 600, "LearnOpengl", NULL, NULL);
if (window == NULL) {
std::cout << "Fail to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
//奇特的寫法,本應是個函數,寫出來卻像個變量,連參數都不需要傳了
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cout << "File to initialize GLAD" << std::endl;
return -1;
}
//使窗口隱藏光標並且捕捉它
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//使用回調
glfwSetCursorPosCallback(window, mouse_callback);
unsigned int texture1, texture2;
const char *path1 = "D:\\cl_workspace\\TestOpengl\\resource\\girl.jpg";
const char *path2 = "D:\\cl_workspace\\TestOpengl\\resource\\awesomeface.jpg";
//兩個都要傳遞引用,這樣函數更改的值纔是上面的值
loadTexture(&texture1, path1);
loadTexture(&texture2, path2);
glViewport(0, 0, 600, 600);//指定視口大小,跟java一樣
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetScrollCallback(window, scroll_callback);
// Shader shader("D:\\cl_workspace\\TestOpengl\\shader\\vertex_shader_two.glsl",
// "D:\\cl_workspace\\TestOpengl\\shader\\fragment_shader_two.glsl");
Shader shader("D:\\cl_workspace\\TestOpengl\\shader\\vertex_shader_three.glsl",
"D:\\cl_workspace\\TestOpengl\\shader\\fragment_shader_three.glsl");
/*//矩形
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
};
unsigned int indices[] = { // 注意索引從0開始!
0, 1, 3, // 第一個三角形
1, 2, 3 // 第二個三角形
};
unsigned int VBO;//頂點緩衝對象
glGenBuffers(1, &VBO);//&符號應該是傳引用對象的意思,不然會傳值進去
//設置索引數據
unsigned int EBO;//索引緩衝對象
glGenBuffers(1, &EBO);
//創建一個VAO,看起來像是對上面的固定過程的封裝,畢竟鏈接着色器程序,指定數據,都是一些固定操作
//且VAO是必須的
unsigned int VAO;
glGenVertexArrays(1, &VAO);
//初始化代碼,一般只運行一次
//1.綁定VAP
glBindVertexArray(VAO);
//2.把頂點數組複製到緩衝中供OpenGl使用,GL_ARRAY_BUFFER這個是個特殊的東西
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//2.5 把索引數組複製到一個索引緩衝中,供opengl使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//設置頂點屬性指針
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) 0);
glEnableVertexAttribArray(0);
//設置顏色屬性指針
// glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (3 * sizeof(float)));
// glEnableVertexAttribArray(1);
//設置紋理屬性指針,第一個參數2,其實是Location,第三個參數是offset
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) (3 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//平移的例子
// glm::vec4 vec(1.0f,0.0f,0.0f,1.0f);
// glm::mat4 trans;
// trans = glm::translate(trans,glm::vec3(1.0f,1.0f,0.0f));
// vec = trans * vec;
//生成一個旋轉並且縮放的矩陣
// glm::mat4 trans;
// //radians是把90度轉化爲弧度
// trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));//沿某個向量轉90度
// trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
//設置矩陣
/*glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 view;
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
glm::mat4 projection;//創建投影矩陣,第二個值應該爲寬高比
projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);
shader.use();//必須先激活
//再次確認一下,設置值,必須在鏈接編譯程序以後才能進行
// unsigned int transformLoc = glGetUniformLocation(shader.ID, "transform");
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
//分別設置model,view,projection四個矩陣的值
int modelLoc = glGetUniformLocation(shader.ID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
int viewLoc = glGetUniformLocation(shader.ID, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
int projectionLoc = glGetUniformLocation(shader.ID, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));*/
//弄多個立方體
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)
};
glUniform1i(glGetUniformLocation(shader.ID, "texture1"), 0);
shader.setInt("texture1", 0);//我記得這兩個是等價的嘛,0對應GL_TEXTURE0
shader.setInt("texture2", 1);
//循環繪製
while (!glfwWindowShouldClose(window)) {//判斷界面是否關閉,沒關閉就循環繪製
processInput(window);
//記錄deltaTime
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrameTime;
lastFrameTime = currentFrame;
//同java
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//如果開啓了深度測試,這裏就要把GL_DEPTH_BUFFER_BIT也清空
//讓紋理轉起來
//生成一個不斷旋轉並且平移的矩陣
// glm::mat4 trans;
// float val = float(glfwGetTime());
// trans = glm::translate(trans, glm::vec3(val / (20.0f), val / (20.0f), 0.0));
// trans = glm::rotate(trans, val, glm::vec3(0.0, 0.0, 1.0));//沿某個向量轉90度
// //將值設置下去
// unsigned int transformLoc = glGetUniformLocation(shader.ID, "transform");
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
//開啓深度測試
glEnable(GL_DEPTH_TEST);
float radius = 5.0f;
float camX = sin(glfwGetTime()) * radius;
float camZ = cos(glfwGetTime()) * radius;
glm::mat4 view;
// view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
//所以說,這個lookat也就是創建一個視角矩陣,只是它更加形象
//第一個參數爲攝像機位置,第二個爲看的點的位置,目前設置的原點,第三個爲頭頂方向
//這樣我們就是在旋轉攝像機的位置,以自身旋轉的方式來
// 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));
//相機始終看着自己面前一個單位距離的位置
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glm::mat4 projection;//創建投影矩陣,第二個值應該爲寬高比
projection = glm::perspective(glm::radians(fov), 1.0f, 0.1f, 100.0f);
shader.use();
int modelLoc = glGetUniformLocation(shader.ID, "model");
// glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
int viewLoc = glGetUniformLocation(shader.ID, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
int projectionLoc = glGetUniformLocation(shader.ID, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glBindVertexArray(VAO);
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
for (int i = 0; i < 10; i++) {//平移到不同的位置 ,繪製多個立方體
//讓立方體不斷旋轉
//這裏我讓十個立方體都先自轉再平移,就需要用兩個矩陣,一個自轉,一個平移
//然後用平移的後乘旋轉的矩陣,印證了之前書裏的一個說法,不能用同一個矩陣先自轉,再平移達到這個效果
//因爲前後影響,生成的結果必然是先平移,再沿某個向量旋轉
glm::mat4 model1;
model1 = glm::rotate(model1, (float) glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
glm::mat4 model;
model = glm::translate(model, cubePositions[i]);
model = model * model1;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(0);
//雙緩衝機制,前緩衝保存着最終輸出的圖像,後緩衝則進行繪製,繪製完成以後與前緩衝交換,就會立即顯示
//單緩衝會存在閃爍問題
glfwSwapBuffers(window);//交換顏色緩衝
glfwPollEvents();//檢查有沒有什麼觸發事件,鼠標鍵盤等,並調用相關的回調
}
//回收數據
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) {
//幀間隔長,就讓它移動的多一些,這樣能間接保證速度
float cameraSpeed = 2.5f * deltaTime;
//按下esc鍵的意思
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);//關閉窗戶
} else if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
cameraPos += cameraSpeed * cameraFront;
} else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
cameraPos -= cameraSpeed * cameraFront;
} else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
//glm::normalize(glm::cross(cameraFront, cameraUp))這個求的是標準化的右向量
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
} else if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}
}
//*代表引用傳遞,傳參數時要用&符號,而取*裏的值則需要*p
void loadTexture(unsigned int *texture, const char *path) {
glGenTextures(1, texture);
// 對*p賦值,從而改變p所指的地址上說保存的值
//*textrue就能表示這個內存地址上表示的值
glBindTexture(GL_TEXTURE_2D, *texture);//GL_TEXTURE_2D同樣,它也是一個目標
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//設置環繞和過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//加載紋理,第三個參數是顏色通道的個數
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true);
//圖片
unsigned char *data = stbi_load(path, &width,
&height, &nrChannels, 0);
//第二個參數爲多級漸遠紋理的級別,0爲基本級別,第三個參數爲指定紋理存儲爲何種格式
//第六個總是設置爲0,第七第八定義源圖的格式和數據類型
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
//釋放圖像內存
stbi_image_free(data);
}
bool firstMouse = true;
void mouse_callback(GLFWwindow *window, double xpos, double ypos) {
//
std::cout << "xpos:" << xpos << " ypos:" << ypos << std::endl;
if (firstMouse) // 這個bool變量初始時是設定爲true的
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
// return;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.05f;//靈敏度
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
pitch = pitch > 89.0f ? 89.0f : pitch;
pitch = pitch < -89.0f ? -89.0f : pitch;
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))-1;
cameraFront = glm::normalize(front);
}
//滾輪的回調
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) {
if (fov >= 1.0f && fov <= 45.0f) {
fov -= yoffset;
}
fov = fov <= 1.0f ? 1.0f : fov;
fov = fov >= 45.0f ? 45.0f : fov;
}