Book of Shaders 02 - 矩阵:二维仿射变换练习
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) //第二列
另外,在上面的变换练习中,是对整个坐标系进行变换的,而非绘制的图形本身。
参考资料:
- [1] The Book of Shaders
- [2] Games 101 第3节 基础变换(二维)
- [3] 百度百科:仿射变换
- [4] Matrix Layouts, DirectX and OpenGL
Book of Shaders 02 - 矩阵:二维仿射变换练习的更多相关文章
- Halcon二维仿射变换实例探究
二维仿射变换,顾名思义就是在二维平面内,对对象进行平移.旋转.缩放等变换的行为(当然还有其他的变换,这里仅论述这三种最常见的). Halcon中进行仿射变换的常见步骤如下: ① 通过hom_mat2d ...
- BZOJ 1567: [JSOI2008]Blue Mary的战役地图 矩阵二维hash
1567: [JSOI2008]Blue Mary的战役地图 Description Blue Mary最近迷上了玩Starcraft(星际争霸) 的RPG游戏.她正在设法寻找更多的战役地图以进一步提 ...
- 用Python实现根据角4点进行矩阵二维插值并画出伪彩色图
哈哈,题目取得这么绕,其实就是自己写了一个很渣的类似图像放大的算法.已知矩阵四周的4点,扩展成更大的矩阵,中间的元素值均匀插入,例如: 矩阵: 1 2 3 4 扩展成3x3的: 1 1.5 2 ...
- AcWing - 156 矩阵(二维哈希)
题目链接:矩阵 题意:给定一个$m$行$n$列的$01$矩阵$($只包含数字$0$或$1$的矩阵$)$,再执行$q$次询问,每次询问给出一个$a$行$b$列的$01$矩阵,求该矩阵是否在原矩阵中出现过 ...
- JS-DOM ~ 02. 隐藏二维码、锁定、获取输入框焦点、for循环为文本赋值、筛选条件、全选和反选、属性的方法操作、节点的层次结构、nodeType
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 剑指offer-顺时针打印矩阵-二维数组
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- 【opencv】 solvepnp 和 solvepnpRansac 求解 【空间三维坐标系 到 图像二维坐标系】的 三维旋转R 和 三维平移 T 【opencv2使用solvepnp求解rt不准的问题】
参考: pnp问题 与 solvepnp函数:https://www.jianshu.com/p/b97406d8833c 对图片进行二维仿射变换cv2.warpAffine() or 对图片进行二维 ...
- (转)QRCODE二维码介绍及常用控件推荐
什么是QR Code码? QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,它具有一维条码及其它二维条码所具有的信息容量大.可靠性高.可表示汉字及图象多种文字信息.保密防 ...
- Java 二维码生成工具类
/** * 二维码 工具 * * @author Rubekid * */ public class QRcodeUtils { /** * 默认version */ public static fi ...
随机推荐
- 校内测试:T1秋末的落叶(命题人gxl)官方题解
秋末的落叶 题解 传送门:https://www.luogu.com.cn/problem/U121886 Part 1:疏通题意 首先,我们从题意和样例解释中很容易提取到以下信息: \(1.\)本题 ...
- N叉树的前后序遍历和最大深度
package NTree; import java.util.ArrayList; import java.util.List; /** * N叉树的前后序遍历和最大深度 */ public cla ...
- PC,移动端H5实现实现小球加入购物车效果
HTML部分: <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" ...
- Docker入门教程-Linux环境安装Nginx及入门使用
介绍 Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服 ...
- keepalived的工作原理解析以及安装使用
一.keepalived keepalived是集群管理中保证集群高可用的一个服务软件,其功能类似于heartbeat,用来防止单点故障. keepalived官网http://www.keepali ...
- Cinder Volume 服务启动流程分析和周期性任务分析
1.cinder-volume服务的程序入口 #!/usr/bin/python2 # PBR Generated from u'console_scripts' import sys from ci ...
- springboot整合druid监控配置
方式一:直接引入druid 1.maven坐标 <dependency> <groupId>com.alibaba</groupId> <artifactId ...
- Reinforcement Learning Using a Continuous Time Actor-Critic Framework with Spiking Neurons
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Abstract 动物会重复奖励的行为,但基于奖励的学习的生理基础仅得到了部分阐明.一方面,实验证据表明神经调节剂多巴胺携带有关奖励的信息 ...
- SpringSecurity权限管理系统实战—九、数据权限的配置
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- vue混入mixins时注意的问题
mixin.js - 方式一:导出对象 const mixin = { mounted () { console.log('fffffffffffff') }, methods: { } } expo ...