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. eric4 中 pyqt .py文件结尾的 代码

    if __name__ == "__main__": import sys app = QtGui.QApplication(sys.argv) ui = MainWindow() ...

  2. eric4 中pyqt 字符串 输入 获取

    在eric4中使用pyqt需要注意: 输入 中文 时,前面加 u ,例如: from PyQt4.QtGui import * from PyQt4.QtCore import * QMessageB ...

  3. 数据恢复----重组raid5解析

    重组Raid5 第一步:判断RAID5条带大小 利用winhex同时打开6个物理镜像每块成员盘中的$MFT文件记录(16进制搜索“46494c45”),并且保证找到的文件记录在每块物理盘的同一扇区(在 ...

  4. Spark中遇到的问题

    spark启动slave时提示 JAVA_HOME is not set 解决方法: 在sbin目录spark-config.sh 中添加自己的jdk 路径export JAVA_HOME=/home ...

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

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

  6. redis 过期策略你知道多少,看完文章你会不自觉说喔哦

    Redis 所有的数据结构都可以设置过期时间,时间一到,就会自动删除.你可以想象 Redis 内部有一个死神,时刻盯着所有设置了过期时间的 key,寿命一到就会立即收割. 你还可以进一步站在死神的角度 ...

  7. 3D渲染集群,你了解多少?

    摘要:渲染是批处理运行模式,那能不能使用Docker容器技术,做大规模的渲染集群呢? 我们先说说3D渲染涉及哪些领域: 1.什么是3D渲染 简单粗暴的讲:就是将虚拟世界里的3D模型,变为(人眼看到)平 ...

  8. 【转】Echarts自适应

    var myChart1 = echarts.init(document.getElementById('chart1')); var option = myChart1.getOption(); w ...

  9. e3mall商城总结11之sso系统的分析、应用以及解决ajax跨域问题

    说在前面的话 一.sso系统分析 什么是sso系统 SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次 ...

  10. Asp.Net WebAPI的简介及创建

    一.WebAPI简介 ASP.NET Web API 是一种框架,用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务.ASP.NET Web API 是一种用于在 .NET F ...