OpenGL基础10:变换

必须掌握的前置技能https://blog.csdn.net/Jaihk662/article/details/103811465(向量与矩阵)

其实也不难,也就是一些线性代数的基本知识,暂时只需要理解到这种程度就好,后面可以再说

一、GLM库

和SOIL一样,GLM也是一个openGL的库,里面存着各种数学公式和算法

OpenGL环境配置(超全整合版)GLM库也可以从这篇文章中的链接中下载到,有个叫做glm-0.9.9.8的文件夹就是了,打开后将其中整个glm文件夹丢到VS的includes文件夹中(C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include)就OK了,不需要额外进行其它操作

一样,我们测试一下:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Shader.h"
#include<opengl/freeglut.h>
#include<SOIL.h>
int main()
{
	glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
	glm::mat4 trans = glm::translate(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 0.0f));
	vec = trans * vec;
	printf("向量平移后结果:(%.0f, %.0f, %.0f)\n", vec.x, vec.y, vec.z);

	trans = glm::scale(glm::mat4(1.0f), glm::vec3(3, 3, 2));
	vec = trans * vec;
	printf("在此之后,向量缩放后结果:(%.0f, %.0f, %.0f)\n", vec.x, vec.y, vec.z);

	trans = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
	vec = trans * vec;
	printf("在此之后,向量围绕z轴旋转90度后结果:(%.0f, %.0f, %.0f)\n", vec.x, vec.y, vec.z);


	vec = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
	trans = glm::mat4(1.0f);
	trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
	trans = glm::scale(trans, glm::vec3(3, 3, 2));
	trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
	vec = trans * vec;
	printf("(%.0f, %.0f, %.0f)\n", vec.x, vec.y, vec.z);
}

可以从上面看出,我们暂时只用到了以下3个库

#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>

结合文章开头的那篇文章(线代基础公式XX),我们看一下其中的一些函数/用法

基础变量/方法:

  • glm::vec4:定义一个长度为4的向量
  • glm::mat4:定义一个4x4的矩阵
  • glm::mat4(1.0f):获得一个单位矩阵E
  • glm::radians(90.0f):将角度转化为弧度

几个基础矩阵运算:

  • glm::translate(M, \textbf{a}):获得向量\textbf{a}对应的位移矩阵,用矩阵M乘以位移矩阵并得到最后的结果
  • glm::scale(M, \textbf{a}):获得向量\textbf{a}对应的缩放矩阵,用矩阵M乘以位移矩阵并得到最后的结果
  • glm::rotate(M, \textbf{a}):获得向量\textbf{a}对应的旋转矩阵,用矩阵M乘以位移矩阵并得到最后的结果

可以看出上面的代码中看出3点:这里也作为提醒

  1. 所有矩阵都是左乘,我们也可以先同时将位移矩阵、缩放矩阵和旋转矩阵先相乘得到一个新的矩阵,并拿这个新的矩阵乘与我们的向量以同时实现平移、旋转和缩放
  2. 矩阵乘法不遵循交换律
  3. 我们必须需要齐次坐标

二、会旋转的长方形

我们来实际运用一下,我们以这一章(SOIL库)的代码为基础扩展

效果如下:

非常简单,我们只需要将时间作为旋转和缩放的参数就好了,如下:

GLint transformLoc = glGetUniformLocation(shaderYellow.Program, "transform");
glm::mat4 trans = glm::translate(glm::mat4(1.0f), glm::vec3(0.5f, -0.5f, 0.0f));
trans = glm::rotate(trans, (GLfloat)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

GLfloat val = sin(glfwGetTime());
trans = glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, 0.5f, 0.0f));
trans = glm::scale(trans, glm::vec3(val, val, val));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

可以看出,我们绘制了两次,一次的位移向量为右下,并进行旋转,一次的位移向量为左上,并进行缩放(当然我们肯定是先进行的旋转,再进行的位移),其中trans即最终的变换矩阵,我们在计算完毕后用有Matrix4fv后缀的glUniform函数将其发送给着色器

  • glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans)):第1个参数是uniform的位置值,第2个参数表示我们需要传送的矩阵个数,这里肯定是1,第三个参数表示是否对矩阵进行置换(Transpose),也就是说交换矩阵的行和列,最后一个参数才是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据

其中对于第3个参数,OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局,GLM的默认布局就是列主序,所以我们并不需要置换矩阵,填GL_FALSE,所谓的列主序布局,即是将长度为n的向量表示成n*1的形式,并且与矩阵进行乘法运算时只能使用左乘(left or pre-multiplication)

其实顶点着色器我们当然也要修改,多一个uniform的变量,应该很容易理解:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec4 color;
layout (location = 2) in vec2 texture;
out vec4 colorIn;
out vec2 texIn;
uniform mat4 transform;
void main()
{
    gl_Position = transform * vec4(position, 1.0);
    colorIn = color;
    texIn = vec2(texture.x, 1.0f - texture.y);
}

好了到这就大功告成了,完整代码如下,其中片段着色器和Shader.h的代码可以在SOIL库这一章找到,没有修改

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Shader.h"
#include<opengl/freeglut.h>
#include<SOIL.h>

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLuint WIDTH = 800, HEIGHT = 600;

int main()
{
    glfwInit();
    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);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glewExperimental = GL_TRUE;
    glewInit();

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    Shader shaderYellow("VShader.txt", "FShaderY.txt");

    GLfloat vertices[] =
    {
        -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f
    };
    GLuint indices[] =
    {
        0, 1, 2,        //用前3个顶点绘制第一个三角形
        1, 2, 3         //用后3个顶点绘制第二个三角形
    };
    GLuint VBO, EBO, VAO, texture;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    glGenTextures(1, &texture);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBindTexture(GL_TEXTURE_2D, texture);


    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    int picWidth, picHeight;
    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);
    unsigned char* image = SOIL_load_image("timg.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    GLint transformLoc = glGetUniformLocation(shaderYellow.Program, "transform");
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glBindTexture(GL_TEXTURE_2D, texture);
        shaderYellow.Use();

        GLint transformLoc = glGetUniformLocation(shaderYellow.Program, "transform");
        glm::mat4 trans = glm::translate(glm::mat4(1.0f), glm::vec3(0.5f, -0.5f, 0.0f));
        trans = glm::rotate(trans, (GLfloat)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        GLfloat val = sin(glfwGetTime());
        trans = glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, 0.5f, 0.0f));
        trans = glm::scale(trans, glm::vec3(val, val, val));
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glfwTerminate();
    return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}
原创文章 1134 获赞 1439 访问量 61万+

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/106141488