glfw庫鍵盤,鼠標點擊和滾輪事件處理

參考: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;
}

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