欢迎光临散文网 会员登陆 & 注册

OpenGL Look At矩阵

2023-06-23 10:29 作者:我梦见珍妮  | 我要投稿

OpenGL摄像机(Look At矩阵)

 转自:
blog.csdn.net/weixin_44478077/article/details/124140329

计算机图形学

OpenGL摄像机

1. 摄像机/观察空间

2. Look At

3. lookAt 矩阵案例

1. 摄像机/观察空间

观察矩阵将所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。当定义一个摄像机时需要它在世界空间中的位置、观察的方向、一个指向它右侧的向量以及一个指向它上方的向量。实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。如下图所示:


图1中的Position为摄像机位置


glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);


正z轴是从屏幕指向你的,如果我们希望摄像机向后移动,我们就沿着z轴的正方向移动


图1中的Direction为摄像机方向

摄像机方向是指摄像机指向场景原点:(0, 0, 0)的方向,用摄像机位置向量减去场景原点向量得到摄像机方向,也就是图中蓝色的指向


glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);

glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);


图1中的Right为右向量

它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧:先定义一个上向量(Up Vector),接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量,因此我们会得到指向x轴正方向的那个向量(如果我们交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量):


glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); 

glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));


图1中的Up为上轴

已经有了x轴向量和z轴向量,获取一个指向摄像机的正y轴向量就相对简单了:只需把右向量和方向向量进行叉乘:


glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);


2. Look At

可以用这3个轴外加一个平移向量来创建一个矩阵(LookAt矩阵)


其中R是右向量,U是上向量,D是方向向量P是摄像机位置向量。注意,位置向量是相反的,因为我们最终希望把世界平移到与我们自身移动的相反方向。把这个LookAt矩阵作为观察矩阵可以很高效地把所有世界坐标变换到刚刚定义的观察空间。LookAt矩阵就像它的名字表达的那样:它会创建一个看着(Look at)给定目标的观察矩阵。


在GLM库的支持下我们可以很方便创建一个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));


lookAt矩阵中传入的三个参数解释:


第一个为摄像机位置

第二个为目标位置

第三个为世界空间中的上向量(用来计算右向量使用的那个上向量)

3. lookAt 矩阵案例

摄像机在场景中旋转,构建lookAt矩阵


 glm::mat4 view = glm::mat4(1.0f);

float radius = 10.0f;

float camX = sin(glfwGetTime()) * radius;

float camZ = cos(glfwGetTime()) * radius;

glm::mat4 view;

view = glm::lookAt(glm::vec3(camX, 2![请添加图片描述](https://img-blog.csdnimg.cn/a1212946586548c78f54b2dbfd13384a.gif)

.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0)); 


单一模型实现的情况


多个模型旋转

这边不放动图了,原本应该动起来


实现代码


#include <glad/glad.h>

#include <GLFW/glfw3.h>

#include <stb_image.h>


#include <glm/glm.hpp>

#include <glm/gtc/matrix_transform.hpp>

#include <glm/gtc/type_ptr.hpp>


#include <learnopengl/filesystem.h>

#include <learnopengl/shader_m.h>


#include <iostream>


void framebuffer_size_callback(GLFWwindow* window, int width, int height);

void processInput(GLFWwindow *window);


// settings

const unsigned int SCR_WIDTH = 800;

const unsigned int SCR_HEIGHT = 600;


int main()

{

    // glfw: initialize and configure

    // ------------------------------

    glfwInit();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);


#ifdef __APPLE__

    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

#endif


    // glfw window creation

    // --------------------

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_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);


    // glad: load all OpenGL function pointers

    // ---------------------------------------

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))

    {

        std::cout << "Failed to initialize GLAD" << std::endl;

        return -1;

    }


    // configure global opengl state

    // -----------------------------

    glEnable(GL_DEPTH_TEST);


    // build and compile our shader zprogram

    // ------------------------------------

    Shader ourShader("7.1.camera.vs", "7.1.camera.fs");


    // set up vertex data (and buffer(s)) and configure vertex attributes

    // ------------------------------------------------------------------

    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

    };

    // world space positions of our cubes

    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)

    };

    unsigned int VBO, VAO;

    glGenVertexArrays(1, &VAO);

    glGenBuffers(1, &VBO);


    glBindVertexArray(VAO);


    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    // position attribute

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

    glEnableVertexAttribArray(0);

    // texture coord attribute

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

    glEnableVertexAttribArray(1);



    // load and create a texture 

    // -------------------------

    unsigned int texture1, texture2;

    // texture 1

    // ---------

    glGenTextures(1, &texture1);

    glBindTexture(GL_TEXTURE_2D, texture1);

    // set the texture wrapping parameters

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // set texture filtering parameters

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // load image, create texture and generate mipmaps

    int width, height, nrChannels;

    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.

    unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/hutao.png").c_str(), &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

    {

        std::cout << "Failed to load texture" << std::endl;

    }

    stbi_image_free(data);

    // texture 2

    // ---------

    glGenTextures(1, &texture2);

    glBindTexture(GL_TEXTURE_2D, texture2);

    // set the texture wrapping parameters

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // set texture filtering parameters

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // load image, create texture and generate mipmaps

    data = stbi_load(FileSystem::getPath("resources/textures/awesomeface.png").c_str(), &width, &height, &nrChannels, 0);

    if (data)

    {

        // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

        glGenerateMipmap(GL_TEXTURE_2D);

    }

    else

    {

        std::cout << "Failed to load texture" << std::endl;

    }

    stbi_image_free(data);


    // tell opengl for each sampler to which texture unit it belongs to (only has to be done once)

    // -------------------------------------------------------------------------------------------

    ourShader.use();

    ourShader.setInt("texture1", 0);

    ourShader.setInt("texture2", 1);


    // pass projection matrix to shader (as projection matrix rarely changes there's no need to do this per frame)

    // -----------------------------------------------------------------------------------------------------------

    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

    ourShader.setMat4("projection", projection); 



    // render loop

    // -----------

    while (!glfwWindowShouldClose(window))

    {

        // input

        // -----

        processInput(window);


        // render

        // ------

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 


        // bind textures on corresponding texture units

        glActiveTexture(GL_TEXTURE0);

        glBindTexture(GL_TEXTURE_2D, texture1);

        glActiveTexture(GL_TEXTURE1);

        glBindTexture(GL_TEXTURE_2D, texture2);


        // activate shader

        ourShader.use();


        // camera/view transformation

        glm::mat4 view = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first

        float radius = 8.0f;

        float camX = static_cast<float>(sin(glfwGetTime()*0.5) * radius);

        float camZ = static_cast<float>(cos(glfwGetTime()*0.5) * radius);

        view = glm::lookAt(glm::vec3(0, 1.0f, 5.0), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

     

        ourShader.setMat4("view", view);


        // render boxes

        glBindVertexArray(VAO);

        for (unsigned int i = 0; i < 10; i++)

        {

            // calculate the model matrix for each object and pass it to shader before drawing

            glm::mat4 model = glm::mat4(1.0f);

            model = glm::translate(model, cubePositions[i]);

            float angle = 20.0f * i;

            model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));

            ourShader.setMat4("model", model);


            glDrawArrays(GL_TRIANGLES, 0, 36);

        }


        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)

        // -------------------------------------------------------------------------------

        glfwSwapBuffers(window);

        glfwPollEvents();

    }


    // optional: de-allocate all resources once they've outlived their purpose:

    // ------------------------------------------------------------------------

    glDeleteVertexArrays(1, &VAO);

    glDeleteBuffers(1, &VBO);


    // glfw: terminate, clearing all previously allocated GLFW resources.

    // ------------------------------------------------------------------

    glfwTerminate();

    return 0;

}


// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly

// ---------------------------------------------------------------------------------------------------------

void processInput(GLFWwindow *window)

{

    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)

        glfwSetWindowShouldClose(window, true);

}


// glfw: whenever the window size changed (by OS or user resize) this callback function executes

// ---------------------------------------------------------------------------------------------

void framebuffer_size_callback(GLFWwindow* window, int width, int height)

{

    // make sure the viewport matches the new window dimensions; note that width and 

    // height will be significantly larger than specified on retina displays.

    glViewport(0, 0, width, height);

}


OpenGL Look At矩阵的评论 (共 条)

分享到微博请遵守国家法律