OpenGl之变换

news/2024/11/17 5:50:06/

目录

缩放

位移

齐次坐标(Homogeneous Coordinates)

 旋转


缩放

  对一个向量进行缩放(Scaling)就是对向量的长度进行缩放,而保持它的方向不变。由于我们进行的是2维或3维操作,我们可以分别定义一个有2或3个缩放变量的向量,每个变量缩放一个轴(x、y或z)。

  我们先来尝试缩放向量v¯=(3,2)。我们可以把向量沿着x轴缩放0.5,使它的宽度缩小为原来的二分之一;我们将沿着y轴把向量的高度缩放为原来的两倍。我们看看把向量缩放(0.5, 2)倍所获得的s¯是什么样的:

  记住,OpenGL通常是在3D空间进行操作的,对于2D的情况我们可以把z轴缩放1倍,这样z轴的值就不变了。我们刚刚的缩放操作是不均匀(Non-uniform)缩放,因为每个轴的缩放因子(Scaling Factor)都不一样。如果每个轴的缩放因子都一样那么就叫均匀缩放(Uniform Scale)。

  我们下面会构造一个变换矩阵来为我们提供缩放功能。我们从单位矩阵了解到,每个对角线元素会分别与向量的对应元素相乘。如果我们把1变为3会怎样?这样子的话,我们就把向量的每个元素乘以3了,这事实上就把向量缩放3倍。如果我们把缩放变量表示为(S1,S2,S3)我们可以为任意向量(x,y,z)定义一个缩放矩阵:

 

注意,第四个缩放向量仍然是1,因为在3D空间中缩放w分量是无意义的。w分量另有其他用途。

位移

  位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程,从而在位移向量基础上移动了原始向量。我们已经讨论了向量加法,所以这应该不会太陌生。

和缩放矩阵一样,在4×4矩阵上有几个特别的位置用来执行特定的操作,对于位移来说它们是第四列最上面的3个值。如果我们把位移向量表示为(Tx,Ty,Tz),我们就能把位移矩阵定义为:

这样是能工作的,因为所有的位移值都要乘以向量的w行,所以位移值会加到向量的原始值上(想想矩阵乘法法则)。而如果你用3x3矩阵我们的位移值就没地方放也没地方乘了,所以是不行的。

齐次坐标(Homogeneous Coordinates)

  向量的w分量也叫齐次坐标。想要从齐次向量得到3D向量,我们可以把x、y和z坐标分别除以w坐标。我们通常不会注意这个问题,因为w分量通常是1.0。使用齐次坐标有几点好处:它允许我们在3D向量上进行位移(如果没有w分量我们是不能位移向量的),而且下一章我们会用w值创建3D视觉效果。

  如果一个向量的齐次坐标是0,这个坐标就是方向向量(Direction Vector),因为w坐标是0,这个向量就不能位移(译注:这也就是我们说的不能位移一个方向)。有了位移矩阵我们就可以在3个方向(x、y、z)上移动物体,它是我们的变换工具箱中非常有用的一个变换矩阵。

 旋转

上面几个的变换内容相对容易理解,在2D或3D空间中也容易表示出来,但旋转(Rotation)稍复杂些。如果你想知道旋转矩阵是如何构造出来的。

首先我们来定义一个向量的旋转到底是什么。2D或3D空间中的旋转用角(Angle)来表示。角可以是角度制或弧度制的,周角是360角度或2 PI弧度。

大多数旋转函数需要用弧度制的角,但幸运的是角度制的角也可以很容易地转化为弧度制的:

  • 弧度转角度:角度 = 弧度 * (180.0f / PI)
  • 角度转弧度:弧度 = 角度 * (PI / 180.0f)

PI约等于3.14159265359。

转半圈会旋转360/2 = 180度,向右旋转1/5圈表示向右旋转360/5 = 72度。下图中展示的2D向量v¯是由k¯向右旋转72度所得的:

在3D空间中旋转需要定义一个角和一个旋转轴(Rotation Axis)。物体会沿着给定的旋转轴旋转特定角度。如果你想要更形象化的感受,可以试试向下看着一个特定的旋转轴,同时将你的头部旋转一定角度。当2D向量在3D空间中旋转时,我们把旋转轴设为z轴(尝试想象这种情况)。

使用三角学,给定一个角度,可以把一个向量变换为一个经过旋转的新向量。这通常是使用一系列正弦和余弦函数(一般简称sin和cos)各种巧妙的组合得到的。当然,讨论如何生成变换矩阵超出了这个教程的范围。

旋转矩阵在3D空间中每个单位轴都有不同定义,旋转角度用θ表示:

沿x轴旋转:

 

沿y轴旋转:

 

 沿z轴旋转:

利用旋转矩阵我们可以把任意位置向量沿一个单位旋转轴进行旋转。也可以将多个矩阵复合,比如先沿着x轴旋转再沿着y轴旋转。但是这会很快导致一个问题——万向节死锁(Gimbal Lock)。在这里我们不会讨论它的细节,但是对于3D空间中的旋转,一个更好的模型是沿着任意的一个轴,比如单位向量$(0.662, 0.2, 0.7222)$旋转,而不是对一系列旋转矩阵进行复合。这样的一个(超级麻烦的)矩阵是存在的,见下面这个公式,其中(Rx,Ry,Rz)代表任意旋转轴: 

下面是一个随时间改变大小的箱子的例子:

main.cpp

#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath> 
#include "../shader.h"
#include "../stb_image.h"
#include <glm/glm.hpp> 
#include <glm/gtc/matrix_transform.hpp> 
#include <glm/gtc/type_ptr.hpp>float vertices[] = {// 位置              // 颜色           // 纹理坐标0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // 右上0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 2.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, 2.0f  // 左上 
};unsigned int indices[] = { // 注意,我们从零开始算! 0, 1, 3, // 第一个三角形 1, 2, 3 // 第二个三角形 
};float ratio = 0.5;
void processInput(GLFWwindow* window);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
int main() {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);
#endifGLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}//GLFW将窗口的上下文设置为当前线程的上下文glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//GLAD// glad: 加载所有OpenGL函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}Shader ourShader("shaders/shader.vs","shaders/shader.fs");//创建VBO和VAO对象,并赋予IDunsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);//绑定VBO和VAO对象glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);//为当前绑定到target的缓冲区对象创建一个新的数据存储。//如果data不是NULL,则使用来自此指针的数据初始化数据存储glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//告知Shader如何解析缓冲里的属性值glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);//开启VAO管理的第一个属性值glEnableVertexAttribArray(0);//告知Shader如何解析缓冲里的属性值glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));//开启VAO管理的第一个属性值glEnableVertexAttribArray(1);//告知Shader如何解析缓冲里的属性值glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));//开启VAO管理的第一个属性值glEnableVertexAttribArray(2);glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);stbi_set_flip_vertically_on_load(true);unsigned int texture, texture1;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D, texture);// 加载并生成纹理 int width, height, nrChannels;unsigned char* data = stbi_load("../pics/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);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };//glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);}else {std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);glGenTextures(1, &texture1);glBindTexture(GL_TEXTURE_2D, texture1);data = stbi_load("../pics/awesomeface.png", &width, &height, &nrChannels, 0);if (data){glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };//glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);}else{std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texture);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, texture1);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);ourShader.use();ourShader.setInt("texture1", 0);ourShader.setInt("texture2", 1);ourShader.setFloat("ratio", ratio);//glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);//glm::mat4 trans = glm::mat4(1.0f);//trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));//vec = trans * vec;//std::cout << vec.x << vec.y << vec.z << std::endl;// 渲染循环while (!glfwWindowShouldClose(window)) {processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //状态设置glClear(GL_COLOR_BUFFER_BIT); //状态使用//glm::mat4 trans = glm::mat4(1.0f);//trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));//trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));//unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");//glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));glm::mat4 trans = glm::mat4(1.0f);float scaleValue = (float)sin(glfwGetTime());trans = glm::translate(trans, glm::vec3(-0.5f, 0.5f, 0.0f));trans = glm::scale(trans, glm::vec3(scaleValue, scaleValue, scaleValue));unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));ourShader.use();ourShader.setFloat("ratio", ratio);glBindVertexArray(VAO);// glfw: 交换缓冲区和轮询IO事件(按键按下/释放、鼠标移动等)glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);trans = glm::mat4(1.0f);//float scaleValue = (float)sin(glfwGetTime());trans = glm::scale(trans, glm::vec3(scaleValue, scaleValue, scaleValue));trans = glm::translate(trans, glm::vec3(-0.5f, 0.5f, 0.0f));glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window);glfwPollEvents();}// glfw: 回收前面分配的GLFW先关资源. glfwTerminate();glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(ourShader.ID);return 0;
}void processInput(GLFWwindow* window) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS){ratio += 0.001;printf("%lf\n", ratio);if (ratio > 1.0)ratio = 1.0;}if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS){ratio -= 0.001;if (ratio < 0.0)ratio = 0.0;}
}void framebuffer_size_callback(GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height);
}

 shader.fs

#version 330 core
out vec4 FragColor;
in vec2 TexCoord;uniform sampler2D texture1; 
uniform sampler2D texture2; 
uniform float ratio;void main()
{FragColor = mix(texture(texture1, TexCoord/2.0), texture(texture2, vec2(1.0-TexCoord.x,TexCoord.y)), ratio); 
}

shader.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;out vec2 TexCoord;
uniform float offsetX;
uniform mat4 transform;void main()
{gl_Position = transform*vec4(aPos.x,aPos.y,aPos.z, 1.0);TexCoord = aTexCoord;
}

 


http://www.ppmy.cn/news/125929.html

相关文章

第八篇、基于Arduino uno,获取MAX30102心率传感器的心率信息——结果导向

0、结果 说明&#xff1a;先来看看串口调试助手显示的结果&#xff0c;第一个值是原始的IR值&#xff0c;第二个值是实时的心跳&#xff0c;第三个值是平均心跳&#xff0c;如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;MAX30102心率传感器的外观如下…

安科瑞预付费管理系统对于学生公寓的设计

安科瑞 徐浩竣 江苏安科瑞电器制造有限公司 zx acrelxhj 摘要&#xff1a;论文设计了适用于学生公寓的自助式预付费控电控水管理系统&#xff0c;采用多种智能功能&#xff0c;可以监测和显示漏电现象&#xff0c;通过短路、跳线、零线接地等方式防范和记录用户的偷电行为&a…

git(版本控制)详细解说【工作必备技能】

Git 1 什么是Git Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用…

机械键盘无冲测试软件,全键无冲 键盘测试 优缺点总结

全键无冲 键盘测试 优缺点总结 最后&#xff0c;让我们进入键盘测试环节&#xff0c;来实际感受一下该款键盘的性能。 第一&#xff0c;我们来测试它的按键无冲&#xff0c;笔者在这里使用的是PassMark KeyboardTest软件。 全键无冲 通过测试&#xff0c;我们可以看到雷柏V700 …

荣耀猎人游戏本“智能”使用方法

荣耀猎人游戏本V700是荣耀第一代游戏笔记本&#xff0c;这款游戏本不仅在综合素质上要优于同级别游戏本&#xff0c;而且它的身上还具备各种各样的创新&#xff0c;将自身的市场竞争力大大提升&#xff0c;其中智能体验就是其中之一&#xff0c;今天为大家带来的&#xff0c;就…

荣耀猎人是鸿蒙,荣耀猎人游戏本V700有哪些产品优势?

荣耀猎人游戏本V700有哪些产品优势&#xff1f; 2020年10月23日 15:42作者&#xff1a;网络编辑&#xff1a;王动 分享 今年9月&#xff0c;荣耀猎人游戏本V700的出现轰动了游戏本圈&#xff0c;同时意味着荣耀品牌正式加入到游戏本品牌的行列当中&#xff0c;这款游戏本体内蕴…

三星v700刷android,三星SM-V700一键救砖教程,轻松刷回官方系统

三星SM-V700手机变砖了怎么办?对于经常刷机的安卓玩家来说&#xff0c;碰到刷机失败导致三星SM-V700手机无法启动甚至无法进入recovery都是在所难免的事&#xff0c;这个时候我们就需要用到奇兔线刷大师线刷救砖功能了&#xff0c;它能轻松实现三星SM-V700一键救砖。奇兔线刷大…

游戏本电脑性价比排行2020榜单,这款新发布的成黑马!

今天荣耀猎人游戏本V700终于发布了&#xff0c;亮点还是很多的&#xff0c;来逐一和大家说一说。之前比较看好这款游戏本&#xff0c;因为散热太给力了&#xff0c;现在散热好的游戏本其实不多见&#xff0c;好多都翻车了&#xff0c;这次荣耀猎人游戏本V700的散热让我很期待。…