0x00 一些废话

如果要深入学习 CG (Computer Graphics,计算机图形学),必然要学习相关的数学知识。CG 涉及到多个不同的领域,根据所研究领域的不同,也会涉及到不同的数学分支。但其中一定少不了线性代数的身影。凡涉及空间中的几何表示与操作,都离不开线性代数的主要研究对象:矩阵。



上图出自电影:The Matrix

以顶点着色器为例,顶点着色器如何完成坐标变换的工作?如果知道矩阵的知识,就会有这样的意识:坐标变换用矩阵来计算是最合适不过的。我们可以利用矩阵简洁地描述几何体的变换,例如缩放、旋转和平移。除此之外,还可以借助矩阵将点或向量的坐标在不同的标架之间进行转换。即,坐标变换。

本文不谈论顶点着色器中坐标变换的具体细节,也不谈论有关矩阵的过多细节,是以快速回顾并熟悉矩阵的仿射变换为目的。

0x01 矩阵乘法

一个规模为 m x n 的矩阵 (matrix) M,是由 m 行 n 列实数所构成的矩形阵列。对于参与矩阵乘法的两个矩阵 A 和 B,如果 A 的规模为 m x n,则 B 的规模须为 n x p。即,A 的行向量的维数与 B 的列向量的维数要一致。两者乘积 AB 的结果是一个规模为 m x p 的矩阵 C。C 中第 i 行、第 j 列的元素,由矩阵 A 的第 i 个行向量与矩阵 B 的第 j 个列向量的点积求得。

例如,

\({\begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix}}{\begin{bmatrix} a & b \\ c & d \end{bmatrix}}={\begin{bmatrix} A & B \\ C & D \\ E & F \end{bmatrix}}\)

上式等号右边的矩阵,其中的 B 位于该矩阵的第 1 行、第 2 列。所以,应由乘式左边向量的第 1 个行向量:\({\begin{pmatrix} 1 & 2 \end{pmatrix}}\),与乘式右边向量的第 2 个列向量:\({\begin{pmatrix} b & d \end{pmatrix}}\),两者的点积所得。即,\(B = 1 \times b + 2 \times d\)。

需要注意的是,矩阵乘法不满足交换律。因为,A 和 B 交换后,B 的行向量的维数 p 与 A 的列向量的维数 m 并不能保证一致。

0x02 仿射变换

仿射变换就是线性变换加上平移。

什么是线性变换?线性变换需要满足两点:一是,直线在变换后仍然保持为直线,不能有所弯曲;二是,原点必须保持固定。线性变换包括:缩放、翻转、错切、旋转。下面是这几个操作的变换矩阵。这里假设了所有操作都是在二维空间完成的上,变换前的点记为 \((x, y)\),变换后的点记为 \((x^{'}, y^{'})\)。

缩放:\({\begin{bmatrix} x^{'} \\ y^{'} \end{bmatrix}}={\begin{bmatrix} s & 0 \\ 0 & s \end{bmatrix}}{\begin{bmatrix} x \\ y \end{bmatrix}}\)

翻转:缩放的一种,当缩放值为负就可以达到翻转的效果,

错切:\({\begin{bmatrix} x^{'} \\ y^{'} \end{bmatrix}}={\begin{bmatrix} 1 & a \\ 0 & 1 \end{bmatrix}}{\begin{bmatrix} x \\ y \end{bmatrix}}\)

旋转:\({\begin{bmatrix} x^{'} \\ y^{'} \end{bmatrix}}={\begin{bmatrix} \cos{\Theta} & -\sin{\Theta} \\ \sin{\Theta} & \cos{\Theta} \end{bmatrix}}{\begin{bmatrix} x \\ y \end{bmatrix}}\)

为什么平移不属于线性变换呢?原因是平移操作后,原点的位置会发生了改变。这种情况下,平移也无法写成矩阵乘法的形式。

加法:\({\begin{bmatrix} x^{'} \\ y^{'} \end{bmatrix}}={\begin{bmatrix} x \\ y \end{bmatrix}}+{\begin{bmatrix} t_x \\ t_y \end{bmatrix}}\)

线性变换总是将原点映射到原点,因此无法用来呈现平移。不过,我们可以在原空间维度 n 的基础上增加 1 个维度。这样,原空间的平移操作,可以借由更高维度空间中的切变操作来完成。

平移:\({\begin{bmatrix} x^{'} \\ y^{'} \\ w^{'} \end{bmatrix}}={\begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix}}{\begin{bmatrix} x + t_x \\ y + t_y \\ 1 \end{bmatrix}}\)

根据矩阵乘法不满足交换律的性质,我们还可以推断出一个信息:变换有时序。先平移再旋转和先旋转再平移,得到的结果是不同的。

0x03 变换练习

#ifdef GL_ES
precision mediump float;
#endif uniform vec2 u_resolution;
uniform float u_time; // 缩放
mat3 scale(vec2 scl) {
return mat3(scl.x, 0.0, 0.0,
0.0, scl.y, 0.0,
0.0, 0.0, 1.0);
} // 错切
mat3 shear(vec2 shr) {
return mat3(1.0, shr.y, 0.0,
shr.x, 1.0, 0.0,
0.0, 0.0, 1.0);
} // 旋转
mat3 rotate(float angle) {
return mat3(cos(angle), -sin(angle), 0.0,
sin(angle), cos(angle), 0.0,
0.0, 0.0, 1.0);
} // 平移
mat3 translate(vec2 pos) {
return mat3(1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
pos.x, pos.y, 1.0);
} void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy; vec3 color = vec3(0.0);
vec3 p3 = vec3(st, 1.0); mat3 t = scale(vec2(2.0));
t *= translate(vec2(0.25, 0.25));
t *= shear(vec2(-0.5, 0.0));
t *= rotate(u_time);
t *= translate(vec2(-0.5, -0.5));
st = (t * p3).xy; // 画个正方形
vec2 bl = step(vec2(0.0), st);
vec2 tr = step(vec2(0.0), 1.0 - st);
float p = bl.x * bl.y * tr.x * tr.y;
color = vec3(p * st, 0.4); gl_FragColor = vec4(color, 1.0);
}

效果:

0x04 一些说明

需要注意的是 OpenGL 的矩阵以列为主。即:

mat2(a, b, //第一列
c, d) //第二列

另外,在上面的变换练习中,是对整个坐标系进行变换的,而非绘制的图形本身。

参考资料:

Book of Shaders 02 - 矩阵:二维仿射变换练习的更多相关文章

  1. Halcon二维仿射变换实例探究

    二维仿射变换,顾名思义就是在二维平面内,对对象进行平移.旋转.缩放等变换的行为(当然还有其他的变换,这里仅论述这三种最常见的). Halcon中进行仿射变换的常见步骤如下: ① 通过hom_mat2d ...

  2. BZOJ 1567: [JSOI2008]Blue Mary的战役地图 矩阵二维hash

    1567: [JSOI2008]Blue Mary的战役地图 Description Blue Mary最近迷上了玩Starcraft(星际争霸) 的RPG游戏.她正在设法寻找更多的战役地图以进一步提 ...

  3. 用Python实现根据角4点进行矩阵二维插值并画出伪彩色图

    哈哈,题目取得这么绕,其实就是自己写了一个很渣的类似图像放大的算法.已知矩阵四周的4点,扩展成更大的矩阵,中间的元素值均匀插入,例如: 矩阵: 1  2 3  4 扩展成3x3的: 1  1.5  2 ...

  4. AcWing - 156 矩阵(二维哈希)

    题目链接:矩阵 题意:给定一个$m$行$n$列的$01$矩阵$($只包含数字$0$或$1$的矩阵$)$,再执行$q$次询问,每次询问给出一个$a$行$b$列的$01$矩阵,求该矩阵是否在原矩阵中出现过 ...

  5. JS-DOM ~ 02. 隐藏二维码、锁定、获取输入框焦点、for循环为文本赋值、筛选条件、全选和反选、属性的方法操作、节点的层次结构、nodeType

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 剑指offer-顺时针打印矩阵-二维数组

    输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1     2  3    4 5     6  7    8 9   10 11 12 13 14 15 16 ...

  7. 【opencv】 solvepnp 和 solvepnpRansac 求解 【空间三维坐标系 到 图像二维坐标系】的 三维旋转R 和 三维平移 T 【opencv2使用solvepnp求解rt不准的问题】

    参考: pnp问题 与 solvepnp函数:https://www.jianshu.com/p/b97406d8833c 对图片进行二维仿射变换cv2.warpAffine() or 对图片进行二维 ...

  8. (转)QRCODE二维码介绍及常用控件推荐

    什么是QR Code码? QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,它具有一维条码及其它二维条码所具有的信息容量大.可靠性高.可表示汉字及图象多种文字信息.保密防 ...

  9. Java 二维码生成工具类

    /** * 二维码 工具 * * @author Rubekid * */ public class QRcodeUtils { /** * 默认version */ public static fi ...

随机推荐

  1. Webpack开发指南

    前言 成为一个全栈工程师,前端是必不可少的,这位前端构建工具webpack是一门必修的技术. 在学习webpack之前,先熟悉一下npm工具:https://www.runoob.com/nodejs ...

  2. PythonCrashCourse 第七章习题

    编写一个程序,询问用户要租赁什么样的汽车,并打印一条消息,如"Let me see if I can find you a Subaru" car =input("Wha ...

  3. [PyTorch 学习笔记] 1.4 计算图与动态图机制

    本章代码:https://github.com/zhangxiann/PyTorch_Practice/blob/master/lesson1/computational_graph.py 计算图 深 ...

  4. 构造 IPv6 报文

    #!/usr/bin/python from scapy.all import * a=IPv6(nh=58, src='fe80::214:f2ff:fe07:af0', dst='ff02::1' ...

  5. 斗地主小游戏随机发牌PHP代码

    <?php header("Content-Type:text/html;charset=UTF-8"); $num=['A','2','3','4','5','6','7' ...

  6. A Distributional Perspective on Reinforcement Learning

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! arXiv:1707.06887v1 [cs.LG] 21 Jul 2017 In International Conference on ...

  7. .sync 修饰符

    vue 修饰符sync的功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定 //写一个(子)组件Child.vue <template> <div c ...

  8. Java多线程_Master-Worker设计模式

    Master-Worker模式是常用的并行模式之一,它的核心思想是:系统由Master进程和Worker进程两类进程协同工作,Master负责接收和分配任务,Wroker负责处理子任务.当各个Work ...

  9. linux计算命令

    1.在lammps中通过car文件导出data文件 (进入工作目录xxx cd xxx/ msi2lmp.exe xxx -class I -frc clayff -i -ignore > ou ...

  10. Java枚举简述

    Java 枚举(enum) Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等. Java 枚举类使用 enum 关键 ...