0%

概念讨论

内存对齐是一个很基础的概念。虽然内存地址的单位是字节,但是CPU访存指令大部分都是以机器字长(64位的CPU机器字长即8字节)为单位,并且由于内存提供的访存电路接口限制,CPU只能从机器字长的倍数的地址开始访问。即CPU能够在一条指令中访存0000-0007,但是不能够从0003-0010,而必须拆分成两条访存指令,分别从0000和0008开始。

CPU按机器字长访存容易理解,但是只能从倍数地址开始访问难以让人理解。据参考,倍数地址的限制更多来源于内存访问器的硬件连线限制。

在没有内存对齐的情况下,假设有两个连续数据INT8,INT64,分别占据了地址0000和0001-0008。此时64位CPU需要几次访存才能拿到INT64数据呢?

  1. 首先访存0000-0007,拿到0001-0007的部分。
  2. 再次访存0008-0015,拿到0008的部分。

即需要访存两次才能取到数据。因此,通常程序会对数据的布局作出内存对齐,以优化访存效率。例如还是INT8和INT64,对齐后地址分别为0000和0008-0015。这样取INT64就只需要访存一次。

据此,业界给出了一个内存对齐的基本原则:将X字节的数据放在X倍数的起始地址上。这样即可在尽可能节约内存空间的情况下,减少访存指令。

阅读全文 »

为了写一个半透明物体的shader,有以下几点基础要求:

  1. 更近的不透明物体要挡住透明物体。因此透明物体需要深度测试来决定是否渲染。
  2. 透明物体不能挡住透明物体。因此透明物体之间不能进行深度测试来剔除渲染,即透明物体不能写入深度开启深度写入,透明物体错误地挡住透明物体
  3. 既然不能写入深度,不透明物体与透明物体之间的渲染顺序就十分关键。如果更远的不透明物体晚一点渲染,其会不合理地挡住更近的透明物体(因为透明物体没有写入深度值)。因此需要分离渲染顺序队列
  4. 透明物体之间的顺序问题。当然Unity也知道顺序很关键,由于没有fragment-level的顺序信息,因此unity会对透明物体按object-level从远到近排序,再进行渲染。这通常没有问题,但是当两个透明物体的深度是交叉的时候就会产生错误。因此理想解决方案是顺序无关透明(order independent transparency, OIT),即逐像素排序。 透明物体之间的渲染顺序错误。交叉地带应该由A覆盖B,实际上由于B的中心点更近,因此其所有fragment都在A之后渲染,导致B错误的覆盖A。
阅读全文 »

在三维空间中描述旋转是一件挺麻烦的事。常用的欧拉角虽然直观简单,但是存在顺序依赖万向节死锁的问题,在通用的旋转运算上并不可行。进而引入的四维数虽然在运算上很有效,但是对于第一次见到的人来说一点也不算直观、易懂。

Krasjet的文章从二维复数出发,进而理解三维四元数的表示意义,循序渐进,通俗易懂,因此记录一下学习笔记。

复数与2D旋转

复数的定义和运算性质不再赘述。对于$ z = a + bi $,相当于对复数基底 $ (1,i) $ 的线性组合,因此可以表示为一个列向量 \(z=[a,b]^T\),但更进一步,其实我们能把复数表示为矩阵。

对于两个复数\(z_1 = a + bi , z_2=c + di\),可以得到观察一下其乘法结果:

\[ \begin{aligned} z_1z_2&=a \boldsymbol{c} -b \boldsymbol{d} \\ &+(b \boldsymbol{c}+a \boldsymbol{d})i \\ &=\left[\begin{array}{cc} a & -b \\ b & a \end{array}\right]\left[\begin{array}{l} c \\ d \end{array}\right] \end{aligned} \]

我们知道右边的\([c,d]^T\)\(z_2\)的列向量表示形式,而左边的矩阵,也只和\(z_1\)的系数有关,因此也可以看做是\(z_1=a+bi\)的矩阵表示。我们不妨尝试将两边都写成矩阵形式:

\[ z_1z_2=\left[\begin{array}{cc} a & -b \\ b & a \end{array}\right] \left[\begin{array}{l} c & -d\\ d & c \end{array}\right] =\left[\begin{array}{cc} ac-bd & -(bc+ad) \\ bc+ad & ac-bd \end{array}\right] =ac-bd + (bc+ad)i \]

上式可以看出\(z_1,z_2,z_1z_2\)在矩阵表示下也同样满足复数基本运算。这时我们可以发现一个神奇的现象:复数可以表示为矩阵,复数乘法可以表示为矩阵乘法,而矩阵乘法进而可以让我们联想到矩阵表示的仿射变换:

\[ z=\left[\begin{array}{cc} a & -b \\ b & a \end{array}\right] =\sqrt{a^2+b^2} \left[\begin{array}{cc} \frac{a}{\sqrt{a^2+b^2}} & \frac{-b}{\sqrt{a^2+b^2}} \\ \frac{b}{\sqrt{a^2+b^2}} & \frac{a}{\sqrt{a^2+b^2}} \end{array}\right] \]

这种式子是不是很眼熟——即以a,b为边的三角变换式:

\[ z=\sqrt{a^2+b^2}\left[\begin{array}{cc} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{array}\right] =\left[\begin{array}{cc} ||z|| & 0 \\ 0 & ||z|| \end{array}\right] \left[\begin{array}{cc} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{array}\right] \]

此时复数\(z=a+bi=||z||(cos\theta+sin\theta i)\)表示的二维变换水落石出,左边的矩阵是一个 等比放缩矩阵,右边的矩阵是一个 旋转矩阵复数乘法,即是两个变换矩阵的作用。

并且再将复数的三角形式代回\(z_1z_2\)可知,\(z_1z_2=cos(\theta+\alpha)+sin(\theta+\alpha) i\) ,即复数的累乘,在变换上体现为旋转角度的累加\(\theta+\alpha\),以及缩放上的累乘。

阅读全文 »

看到几篇很棒的博客,如何阅读——刘未鹏怎样花两年时间去面试一个人,想要从中汲取一些知识,并且记录一些自己的思绪。

学习的方向

其实现在的在校生根本不缺学习的动力(特别是计算机,特别是研究生),毕竟现在的大环境已经卷的不成样子。看着各大公司的招聘简历,以及相关面经,会发现大学的课程完全不足以培养你进入大厂工作,如果真只是按部就班上完了全部课程,那恭喜你达到了新成就毕业即失业。在这个能力为硬指标的行业,但凡对工作有所期望的人,都会想方设法去学习更多的东西,起码面试三轮可以有能力通过一轮吧。

阅读全文 »