目录

1. 矩阵知识总结

缩放

旋转

位移

2. glm使用方法

安装

使用

位移(使用translate函数)

旋转(使用rotate及radians函数)

缩放(使用scale函数)

在你的cpp中将变换矩阵传递给着色器

3.举个栗子

代码

截图


参考:LearnOpenGL

1. 矩阵知识总结

看下面内容之前请先恶补一下线性代数的知识,不对矩阵乘法等内容进行讲解,仅做总结。

缩放

如果我们把缩放变量表示为(S1,S2,S3)我们可以为任意向量(x,y,z)定义一个缩放矩阵:

缩放矩阵

构造思路:想让一个向量缩放多少倍,就是在对应的方向乘一个数字。例如,需要在x方向进行缩放,也就是在矩阵的第一行写上(S1,0,0,0),这样,缩放因子S1就会乘上x,而与其他方向无关。所以最后就是把对应向量的缩放因子写在了对角线上。由于某些原因,OpenGL是用的4维的,以后再做讲解,w分量是有用的。

旋转

旋转矩阵

构造思路:沿某个轴旋转,则那个轴所在方向的向量分量是不变的,也就是对应对角线为1。其他的要根据沿哪个轴变化,看最后向量的结果,两行都是cos sin和sin cos。所以,记住负号的位置就好了。当然,如果你不考试的话,也没必要记住。一是,你可以将图片存在电脑上或看我博客;二是,glm库已经帮你弄好,你只填入旋转角度这个参数即可。

位移

位移矩阵

构造方法:要想进行某个方向的位移,需要最后的向量是该方向的原数据+位移数据。这时候,我们就用到了前面所说的w分量,

保持原数据不变,则对角线为1,利用w分量,则只需将对应位移数据写到对应行的第4列即可,这样w分量是1就可以了。

2. glm使用方法

安装

百度网盘:传送门
提取码:wbc7

直接把安装包放到你的include文件中即可,我是把所有用的库都放在一起了,这样只包含一个include路径即可。

我的OpenGL的include路径:G:\vs2015\VC\include\GL

使用

在你需要使用的时候包含以下头文件:

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

定义一个mat4类型的变量,默认是一个4×4单位矩阵。

// 下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
// 下面这行代码就需要改为:
// glm::mat4 trans = glm::mat4(1.0f)
glm::mat4 trans;

创建一个变换矩阵

位移(使用translate函数)

trans = glm::translate(trans, glm::vec3(1.0f, 0.0f, 0.0f));

你只需要填入后面的三个参数,表示对某个方向的位移距离(上面就是x方向移1.0),glm就会帮你生成位移矩阵,很智能吧:)。

x方向移1.0

旋转(使用rotate及radians函数)

trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));

你只需要填入radians参数即可(表示沿z轴逆时针旋转的角度),上面代码表示逆时针旋转45度。

逆时针旋转45度

缩放(使用scale函数)

trans = glm::scale(trans, glm::vec3(1.30, 1.30, 1.30));

你只需要填入后面的三个参数,代表你在每个分量的缩放比例,上面代码表示每个方向均缩放到1.30倍。

放大到1.30倍

如果你只写了以上代码是不够的,还需要把矩阵传递给着色器。

在你的cpp中将变换矩阵传递给着色器

     unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

在顶点着色器中定义mat4类型的变量,表示变换矩阵,并在main函数中与向量相乘后赋值给gl_Position,这样才会改变。

uniform mat4 transform;void main()
{gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);vertexColor = vec4(aColor.x, aColor.y, aColor.z, 1.0);TexCoord = aTexCoord;
}

注意:如果进行多个变换,需要按照位移、旋转、缩放的顺序进行排列,否则,最后的结果与你预想的不一样,这是因为矩阵乘法不满足交换律。

3.举个栗子

代码

项目是在上篇文章的基础上增加,仅给出与上一篇文章有改变文件的代码

main.cpp

//头文件
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>//函数声明
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);// 设置窗体宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;//主函数
int main()
{// glfw: 初始化和配置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 窗体生成GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "rectwithtex", NULL, NULL);//设置标题//判断窗体是否生成if (window == NULL){std::cout << "生成GLFW窗口失败" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// glad: 加载所有的OpenGL功能指针----------------------------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "初始化GLAD失败" << std::endl;return -1;}// 建立并编译着色器--------------------------------------------------------------------Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");// 设置点数据 (还有缓冲) 配置点的属性(包含点坐标等) 这里设置了4个,将以索引的方式选择点来画三角形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    // 左上};unsigned int indices[] = {  0, 1, 2,  // 第一个三角形选择索引为 0 1 3的三个点2, 3, 0,  // 第一个三角形选择索引为 1 2 3的三个点};unsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);  //注意,这里使用EBO作为缓冲对象// 绑定顶点数组, 然后绑定并设置缓冲, 最后配置顶点属性-------------------------------glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//修改属性-----------------------------------------------------------------------------glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(6);glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(7);glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(8);//纹理-------------------------------------------------------------------------------unsigned int TexBufferA, TexBufferB;stbi_set_flip_vertically_on_load(true);//y轴翻转//木箱部分glGenTextures(1, &TexBufferA);//激活glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, TexBufferA);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindTexture(GL_TEXTURE_2D, TexBufferA);//绑定int width, height, nrChannels;unsigned char *data = stbi_load("container.jpg", &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 << "加载纹理失败" << std::endl;}stbi_image_free(data);//释放//笑脸部分glGenTextures(1, &TexBufferB);glActiveTexture(GL_TEXTURE3);glBindTexture(GL_TEXTURE_2D, TexBufferB);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindTexture(GL_TEXTURE_2D, TexBufferB);//绑定unsigned char *data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);if (data){glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);//注意,有Alpha通道glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理}else{std::cout << "加载纹理失败" << std::endl;}stbi_image_free(data2);//注意这是允许的,对glVertexAttribPointer的调用将VBO注册为顶点属性的绑定顶点缓冲区对象,所以之后我们可以安全地解除绑定glBindBuffer(GL_ARRAY_BUFFER, 0);// 记住:当VAO处于活动状态时,不要取消绑定EBO,因为绑定元素缓冲对象IS存储在VAO中; 保持EBO的约束力。//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);// 您可以在之后取消绑定VAO,以便其他VAO调用不会意外地修改此VAO,但这种情况很少发生。无论如何, // 修改其他VAO需要调用glBindVertexArray,因此我们通常不会在不直接需要时解除VAO(VBO同样)的绑定。glBindVertexArray(0);// 取消注释此调用会绘制线框多边形。//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//glm相关------------------------------------------------------------------// 下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本// 下面这行代码就需要改为:// glm::mat4 trans = glm::mat4(1.0f)glm::mat4 trans;trans = glm::translate(trans, glm::vec3(1.0f, 0.0f, 0.0f));trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));trans = glm::scale(trans, glm::vec3(1.30, 1.30, 1.30));while (!glfwWindowShouldClose(window)){// 输入processInput(window);Sleep(200);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);//绑定纹理glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元glBindTexture(GL_TEXTURE_2D, TexBufferA);glActiveTexture(GL_TEXTURE3); // 在绑定纹理之前先激活纹理单元glBindTexture(GL_TEXTURE_2D, TexBufferB);glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用着色器myShader->use();glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0); // 手动设置//glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 3); // 手动设置myShader->setInt("ourFace", 3); // 或者使用着色器类设置unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));// 画矩形--------------------------------------------------------------------------//可以知道我们只有一个三角形VAO,没必要每次都绑定它,但是我们这么做会让代码有一点组织性glBindVertexArray(VAO);//glDrawArrays(GL_TRIANGLES, 0, 6);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);// glBindVertexArray(0); //没必要每次都解绑 // 交换buffers和poll的IO事件 (按键按下/释放,鼠标移动等.)glfwSwapBuffers(window);glfwPollEvents();}//一旦他们超出已有的资源,就取消所有资源的分配:glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);// glfw:终止,清空之前所有的GLFW的预分配资源glfwTerminate();return 0;
}
//查询GLFW相关按键是否被按下/释放,根据情况作出反应
void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}
// g无论窗口大小何时改变(由操作系统或用户自己)这个回调函数将会被执行
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//确定viewport与新的窗口尺寸匹配; 请注意,宽度和高度将明显大于显示器上指定的宽度和高度。glViewport(0, 0, width, height);
}

vertexSource.txt

#version 330 core
layout (location = 6) in vec3 aPos;
layout (location = 7) in vec3 aColor;
layout (location = 8) in vec2 aTexCoord;
out vec4 vertexColor;
out vec2 TexCoord;uniform mat4 transform;void main()
{gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);vertexColor = vec4(aColor.x, aColor.y, aColor.z, 1.0);TexCoord = aTexCoord;
}

截图

平移、旋转、缩放后的截图

全部代码下载

我的网盘
提取码:waxk

更多OpenGL知识:现代OpenGL入门教程

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

OpenGL-利用矩阵变换(缩放、旋转、位移),使用glm库相关推荐

  1. CSS3变形和动画:旋转、扭曲、缩放、位移、矩阵 、原点 transform-origin、过渡属性 transition-property、过渡所需时间 transition-duration...

    无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家.教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家.点这里可以跳转到教程. 旋转.扭曲.缩放.位移.矩阵 ...

  2. 【OpenGL】平移、旋转和缩放矩阵推导

    [OpenGL]平移.旋转和缩放矩阵推导 平移 将点p(x,y,z)平移到p'(x',y',z'),在X轴.Y轴.Z轴三个方向上平移的距离分别为Tx,Ty,Tz,其中Tz为0(二维平面的平移),如图3 ...

  3. opengl glm库 改变mvp矩阵 实现3D漫游

    上一篇glm来实现的mvp矩阵变换实现opengl渲染模型,讲到了如何利用glm库实现mvp矩阵,这篇主要讲解下,我们如何利用glm库的平移.旋转矩阵实现一个简单的3d漫游的效果. 原理 移动 cam ...

  4. OpenGL矩阵运算——GLM库的使用

    GLM库简介 OpenGL没有内建矩阵运算方法,常用的第三方库为GLM.GLM是OpenGL Mathematics的缩写.作为一个header only库,GLM只要包括了相应的头文件就可以使用它提 ...

  5. Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 一.转换位图的像素色彩 给图片添加装饰物,只是在局部变换,如果想让图片一边保持轮廓一边改变色彩,就要深入图像的每个像素点,将这些像素点统统采取某种算法 ...

  6. Android OpenGL射线拾取手势旋转(二)

    上回分解-_-!,Android OpenGL射线拾取&手势旋转(一). 3)Renderer:RayPickRenderer.java OpenGL渲染器,比较多的东西都在这里面了. pub ...

  7. 4.花瓣特效----js+旋转+位移+随机颜色+随机位置

    在3.花瓣特效----js+旋转+位移的基础上再增加位置和颜色的变化. 1.思路: 利用Math.random来产生(0,16)的随机数,然后创建从1到F的数组New_color,New_color[ ...

  8. [Python从零到壹] 三十八.图像处理基础篇之图像几何变换(平移缩放旋转)

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  9. hammer.js移动端拖拽缩放旋转元素

    第一步 下载hammer.js并引入 下载地址可以是:http://hammerjs.github.io/ 第二步 复制下面这些代码,放在你的js里面 function drag(drag){var ...

最新文章

  1. 工作9年开发面试华为要薪1W,华为员工:我司没有这么低工资的岗.....
  2. JQuery选择器 属性值 等于 以开头 以结尾 元素选择
  3. (转)开源 Apache 服务器安全防护技术精要及实战
  4. 动画演示 Delphi 2007 IDE 功能[3] - 修改属性
  5. jquery-显示隐藏-链式调用
  6. OpenCV2 图像处理与计算机视觉(一)—— 去除一幅二值化图像中的椒盐噪声
  7. FastReport studio 动态加载数据集 (zhuan)
  8. dw1820网卡支持linux吗,黑苹果驱动DW1820A无线网卡教程
  9. 计算机软件用户体验报告,软件项目用户体验性测试报告.doc
  10. linux下virtualbox安装win7虚拟机无法调整分辨率
  11. 2.3两个列表或元组首尾相连
  12. [MIT]微积分重点 第三课 极值和二阶导数 学习笔记
  13. [分享] 《步步为营封 Win7》--skyfree
  14. android的word默认字体大小设置,更改Microsoft Word文档的默认字体大小和样式 | MOS86...
  15. DNS区域以及批量创建
  16. 液压管路渗漏图像识别检测方法研究
  17. rsync+inotify实现文件增量实时同步
  18. 围绕“场景+人”,戴尔商用终端做了一篇大文章
  19. xaut 循环结构习题 公式求兀值
  20. mysql udp提权_SQL Server数据库1433端口解封提权

热门文章

  1. 详解:与运算()、或运算(|)、异或运算(^)
  2. 名帖211 赵孟頫 行书《洛神赋》
  3. 综合项目实战(电影购票系统)
  4. 黑猴子的家:Python 嵌套for循环
  5. [ROS] 什么是ROS、ROS的优缺点
  6. 恭王府内部景点最短路径
  7. 计算机网络系统集成工程师考试时间,2021年全国系统集成项目管理工程师报名时间和报名入口...
  8. SQL注入之天书学习
  9. Hook textout可能遇到的问题
  10. 无尽对决怎么修改服务器地区,无尽对决换区攻略及游戏全介绍