第十二节—关于矩阵变换和向量

本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

全文除大片代码外都是手打无复制,如果出现代码的细节错误,敬请谅解,如果可以提醒一下,感激不尽

在之前的学习中,经常出现MatrixStack,本节主要学习一下之前的变换都是怎么发生的。

一、理解变化和矩阵的关系

之前的章节中出现了物体的平移/旋转/投影等操作(包括缩放,只是没写),比如移动四边形,旋转圆环等等。在第一次绘制四边形的时候,我们的做法是选出一个顶点为相对移动点,根据矩形的四个顶点关系,算出相对点的坐标,也就可以算出其他三个点的坐标,使图形发生移动,但是如果顶点的数量非常多的话,那么这种计算的时间成本是很高昂的。而在圆环绘制的章节中,学习到了一种方法,可以解决顶点多的图形发生仿射变化需要耗费的时间长的问题,就是旋转坐标系,不旋转物体。

上述的一系列的变化,其实都是放射变化和矩阵的关系,根本上都是由于使用了某种变化矩阵,使得图形发生了改变。而关于矩阵,这里就要用到大学时候的数学知识,本节不过多赘述,只提一下本节要用的地方。

1.简述关于OpenGL中的数学

OpenGL中已经提供了math3d的库,就在GLTools中,我们主要是理解一下,在向量,矩阵和变化之间是怎样的一个逻辑。

在OpenGL中,我们经常要使用到(x,y,z)坐标来描述一些信息,这三个值其实可以表示图形的位置,而这个位置又带有两个信息,一个是方向,一个是数量。这就和数学中的向量有了交集。

规范化

在开发的时候,我们会经常的听到一个词叫规范化,比如经常听到的把一个矩阵向量做规范化或者标准化处理。规范化或者标准化就是把矩阵或者向量转换为单位矩阵或者单位矩阵。这个很简单理解,比如x轴上的单位向量就是(1,0,0),单位矩阵就是如(1.1)式所示:
\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \tag{1.1}
而这个矩阵,我们一般可以用一个一维数组来进行存储:float arr[] = {1,0,0,0,1,0,0,0,1};就可以。

这个单位向量和单位矩阵的用处就是比如我们让图形向某一个方向移动几个单位,就是以单位向量或者单位矩阵为基本单位。

OpenGL中的math3d库

之前在RenderScene中见到过的,M3DVector3fM3DVector4f,这两个前者表示的是定义一个三位向量(x,y,z),后者则是定义一个思维向量(x,y,z,w),在典型情况下呢,w一般设置为1.0,而x、y、z通过除以w来进行缩放。这两个类型其实都是typedef float的一维数组。比如声明一个三维向量和一个四维向量:


M3DVector3f aVector = {1,2,3};
M3DVector4f bVector = {1,2,3,4};

math3d中的向量/矩阵的点乘和叉乘

点乘:

参数都是两个向量/矩阵,一前一后,就不解释了。

方法1:

返回的是两个向量之间余弦值,是[-1,1]之间的值

float m3dDotProduct3(const M3DVector3f u , const M3DVector3f v)

方法2:

返回的是两个向量之间的弧度值

float m3dGetAngleBetweenVector3(const M3DVector3f u , const M3DVector3f v)

叉乘:

第一个参数是返回的结果要存储到哪个变量里面。后面的两个就是那两个向量/矩阵

void m3dCrossProduct3(M3DVector3f result , const M3DVector3f u , const M3DVector3f v)

矩阵类型:

也是typedef的。


typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];

2.理解变化

理解一下之前用到的一些变换,如下图1.2所示:

1.2.png

二、矩阵变换的实际操作

先来代码,然后通过一个简单的代码来学习。

就画一个移动的矩形,但是这次用的是变换矩阵,而不是算数。


//
//  main.cpp
//  06矩阵变换图形移动
//
//  Created by EasonLi on 2020/8/29.
//  Copyright © 2020 EasonLi. All rights reserved.
//

#include <stdio.h>

#pragma mark - 引用类

//包括了管理和创建着色器管理器的方法,还提供了一组存储着色器,可用于一些初级操作。
#include "GLShaderManager.h"
//GLTools工具类。里面包含了大多数GLTool类似于C语言的类、三角形批次类和设置工作区间
#include "GLTools.h"
//math3d数学库,有本节要使用到的,关于矩阵进行仿射变换需要用到的方法
#include "math3d.h"

//根据不同的系统引入不同的GLUT。
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#pragma mark - 公共变量

//着色器管理器
GLShaderManager shaderManager;

//三角形批次类容器
GLBatch squareBatch;

//矩形边长的一半
GLfloat blockSize = 0.2f;

//每次移动的距离
GLfloat stepSize = 0.025f;

//矩形顶点
GLfloat vVerts[] = {
    
    -blockSize,blockSize,0,
    blockSize,blockSize,0,
    blockSize,-blockSize,0,
    -blockSize,-blockSize,0
    
};

//矩形的颜色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};

//在x轴移动的距离
GLfloat xPos = 0.f;
//在y轴移动的距离
GLfloat yPos = 0.f;

#pragma mark - 函数

//设置视口
void ChangeSize(int w,int h)
{
    
    glViewport(0, 0, w, h);
    
}

//设置渲染环境
void SetUpRC ()
{
    
    //设置清屏颜色
    glClearColor(0.3f, 0.3f, 0.3f, 1.f);
    
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //设置三角形批次类
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
    
}

//设置渲染
void RenderScene()
{
    
    //清空缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //定义矩阵
    //定义一个平移矩阵,一个旋转矩阵,一个综合结果矩阵
    M3DMatrix44f mTranslationMatrix,mRotateMatrix,mFinalMatrix;
    
    //用矩阵来做平移
    //把x,y轴移动的单位分别放入第2和3个参数,结果会放入第一个参数
    m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.f);
    
    //用矩阵来做旋转
    //先设置一个每次重绘或者说移动的时候,矩形旋转的角度,绕z轴转
    static float zRot = 5.f;
    zRot += 5.f;
    //用旋转矩阵做旋转,结果放到第一个参数的矩阵中
    m3dRotationMatrix44(mRotateMatrix, m3dDegToRad(zRot), 0.f, 0.f, 1.f);
    
    //结合旋转和平移
    m3dMatrixMultiply44(mFinalMatrix, mTranslationMatrix, mRotateMatrix);
    
    //选择并设置着色器/固定管线
    shaderManager.UseStockShader(GLT_SHADER_FLAT,mFinalMatrix,vRed);
    squareBatch.Draw();
    
    glutSwapBuffers();
    
}


//上下左右特殊键位移动
void SpecialKeys (int key , int x , int y)
{
    
    //设置键位移动
    switch (key) {
            
        case GLUT_KEY_UP:
            yPos += stepSize;
            break;
            
        case GLUT_KEY_DOWN:
            yPos -= stepSize;
            break;
        case GLUT_KEY_RIGHT:
            xPos += stepSize;
            break;;
        case GLUT_KEY_LEFT:
            xPos -= stepSize;
    }
    //边界检测
    if (xPos < blockSize - 1) xPos = blockSize - 1;
    if (xPos > 1 - blockSize) xPos = 1 - blockSize;
    if (yPos < blockSize - 1) yPos = blockSize - 1;
    if (yPos > 1 - blockSize) yPos = 1 - blockSize;
    
    glutPostRedisplay();
    
}


#pragma mark - main

int main (int argc,char *argv[])
{
    
    //设置工作目录和项目目录到同一目录/Resource下
    //GLUT的优先级设定已经做过这个操作,手动再做一次保证安全。
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT
    glutInit(&argc, argv);
    
    //初始化显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    //初始化窗口大小
    glutInitWindowSize(600, 600);
    
    //创建窗口并命名
    glutCreateWindow("矩阵变换——图形移动");
    
    //注册函数
    //注册重塑函数
    glutReshapeFunc(ChangeSize);
    
    //注册渲染函数
    glutDisplayFunc(RenderScene);
    
    //注册特殊键位函数
    glutSpecialFunc(SpecialKeys);
    
    //初始化一个Glew库,确保OpenGL的API完全可用
    //在做任何的渲染尝试之前,都要确保驱动程序的初始化不出现任何问题
    GLenum status = glewInit();
    if (GLEW_OK != status) {
        printf("glew init error : %s \n",glewGetErrorString(status));
        return 1;
    }
    
    //设置渲染环境
    SetUpRC();
    
    //开启循环。
    //本循环会接受并执行来自于系统或者窗口的指令,但是必须是在其之前键入的
    glutMainLoop();
    
    return 0;
    
}

其中,mTranslationMatrix,mRotateMatrix的位置如果发生了互换,结果就会不一样,这是因为矩阵的乘法,又不是特殊的伴随矩阵、单位矩阵和数量矩阵,所以不满足交换律的。

到此,关于矩阵变换和向量的一些知识就学习完成了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352