Cocos Creator 向量基础及其使用

前言

  1. 在某一次Cocos的线下沙龙中,有大佬推荐了 Games 101 的课程,去观摩了,发现十分收益,因此就有了这次的文章,或者更多是个人笔记
  2. 以下内容主要来自 Games 101 第二节课 https://www.bilibili.com/video/BV1X7411F744?p=2
  3. 个人在这个基础上,在结合 Cocos Creator 进行的一些个人理解及理论实例使用

一、向量归一化

向量 \vec a 的归一化表示得到一个方向和向量 \vec a 相同的向量,但是向量的模(向量的长度)为 1

归一化后的向量,也被叫作单位向量。

二、向量点乘

向量点乘公式:

\vec a \cdot \vec b = \lvert a \rvert \lvert b \rvert cos \theta

向量点乘满足一般运算规则:

  • 交换律:\vec a \cdot \vec b = \vec b \cdot \vec a
  • 结合律:\vec a \cdot (\vec b + \vec c) = \vec a \cdot \vec b + \vec a \cdot \vec c
  • 分配律:(k \vec a) \cdot \vec b = \vec a \cdot (k \vec b)

直角坐标系下,在二维空间下,计算点乘:

\vec a \cdot \vec b = {x_a \choose y_a} \cdot {x_b \choose y_b} = x_a x_b + y_a y_b

直角坐标系下,在三维空间下,计算点乘:

\vec a \cdot \vec b = \begin{pmatrix}x_a \\\ y_a \\\ z_a\end{pmatrix} \cdot \begin{pmatrix}x_b \\\ y_b \\\ z_b\end{pmatrix} = x_a x_b + y_a y_b + z_a + z_b

根据点乘公式,我们知道,向量点乘是一个,那么这个数在图形学上的几何意义是什么呢?

2.1 计算两个向量之间的夹角

根据向量点乘公式,我们可以推导出:

cos \theta = \frac {\vec a \cdot \vec b}{\lvert a \rvert \lvert b \rvert}

如果将向量 \vec a 和向量 \vec b 进行归一化,那么 \lvert a \rvert \lvert b \rvert = 1,可以在推导出

cos \theta = \vec a \cdot \vec b

根据 cos \theta ,我们就可以知道两个向量之间的夹角(角度)[0^\circ,180^\circ]

整理一下,在实际的计算中,过程如下:

  1. 将两个向量归一化
  2. 计算归一化后的向量的点乘结果

GLSL 中可以表示为:

vec3 a;
vec3 b;
float c = dot(normalize(a), normalize(b));

2.2 判断两个向量前后(方向)

利用点乘我们可以知道两个方向之间的夹角:

cos \theta = \frac {\vec a \cdot \vec b}{\lvert a \rvert \lvert b \rvert}

根据余弦函数的曲线图,我们可以知道

  • \theta = 0^\circ 时,cos\theta = 0
  • 0 < \theta < 90^\circ 时,0 < cos\theta < 1
  • \theta = 90^\circ 时,cos\theta = 1
  • 90 < \theta < 180^\circ 时,-1 < cos\theta < 0
  • \theta = 180^\circ 时,cos\theta = -1

也就是说

  • \vec a \cdot \vec b = 1,向量 \vec a 和向量 \vec b 方向 完全一致
  • 0 < \vec a \cdot \vec b < 1,向量 \vec a 和向量 \vec b 方向 基本一致
  • \vec a \cdot \vec b = 0,向量 \vec a 和向量 \vec b 方向 垂直
  • -1 < \vec a \cdot \vec b < 0,向量 \vec a 和向量 \vec b 方向 基本相反
  • \vec a \cdot \vec b = -1,向量 \vec a 和向量 \vec b 方向 完全相反

根据这个数值,我们可以得出,向量 \vec a 和向量 \vec b前后关系

image.png

利用这个几何意义,可以实现:

2.3 计算向量投影

计算向量 \vec b 在向量 \vec a 上的投影向量:

image.png

得到投影后,还可以在进一步分解向量 \vec b

image.png

投影的一个典型应用在对OBB包围盒进行碰撞检测的时候,经常会使用 分离轴定理SAT(Separating Axis Theorem) 进行检测

分离轴定理:通过判断任意两个矩形在任意角度下的投影是否均存在重叠,来判断是否发生碰撞。若在某一角度光源下,两物体的投影存在间隙,则为不碰撞,否则为发生碰撞。

计算投影就可以用到向量点乘了

详细可以参考 碰撞检测的向量实现

三、向量叉乘

向量叉乘公式:

\vec a \times \vec b = \begin{pmatrix}y_a z_b - y_b z_a \\\ z_a x_b - x_a z_b \\\ x_a y_b - y_a x_b\end{pmatrix}

GLSL 中可以表示为:

vec3 a;
vec3 b;
vec3 c = cross(a, b);

ps:叉乘的结果是一个向量,点乘是得到一个数

image.png

3.1 计算法线向量

向量 \vec a 和向量 \vec b 的叉乘得到的是一个同时垂直于向量 \vec a 和向量 \vec b 的向量 \vec c

只要向量 \vec a 和向量 \vec b 的夹角不为 0^\circ180^\circ ,那么向量 \vec a 和向量 \vec b 可以组成一个平面,而向量 \vec a 和向量 \vec b 的叉乘就得到一个垂直于这个平面的向量,这个向量也叫法向量。

垂直于一个平面的向量,方向有两个,并且这两个方向完全相反。为了准确得到方向,我们可以采用右手螺旋定则

  • 当为 \vec a \times \vec b 时:
    • 伸出左手,摆出点赞姿势,左手握住向量\vec b,左手拇指指向向量\vec b的方向,此时其余四个手指握拳姿势,按着这4个手指的指向姿势,绕着拇指旋转90^\circ,得到的新向量即为 \vec a \times \vec b 的结果向量
  • 当为 \vec b \times \vec a 时:
    • 此时则为左手握住向量 \vec a 旋转

操作下来可以发现,两次叉乘得到的新向量,方向完全相反,但是大小(长度)是一致的,于是有:

\vec a \times \vec b = -\vec b \times \vec a

3.2 判断向量的左右

image.png

假设向量 \vec a 向量 \vec b 都在 xy 的二维平面上,并假设 \vec a \times \vec b = \vec c 。那么

\vec c = \vec a \times \vec b = \begin{pmatrix}y_a z_b - y_b z_a \\\ z_a x_b - x_a z_b \\\ x_a y_b - y_a x_b\end{pmatrix}

因为二维平面上,向量 \vec a 和向量 \vec bz 肯定为 0,所以

\vec c = \begin{pmatrix}0 \\\ 0 \\\ x_a y_b - y_a x_b\end{pmatrix}

根据右手螺旋定则,\vec a \times \vec b 表示,法向量 \vec c 是绕向量 \vec a\vec b 所在平面旋转得到的,这里可以定义

  • \vec cz 值为正,则表示向量 \vec a 在向量 \vec b右侧
  • \vec cz 值为负,则表示向量 \vec a 在向量 \vec b左侧

3.2.1 判断点在多边形内部还是外部

image.png

以上图为例,在刚才左右的基础上,如果

  • 向量 \vec {AP} 在向量 \vec {AB} 的左边
  • 向量 \vec {BP} 在向量 \vec {BC} 的左边
  • 向量 \vec {CP} 在向量 \vec {CA} 的左边

那么,点P 在三角线 ABC 内。

这样子通过叉乘就可以知道点是否在三角形内/外,这也是光栅化的基础,判断点是否在三角形内

更进一步,我们还可以通过向量叉乘来判断点是否在多边形内

比如:

  • Cocos Creator 提供的 cc.Intersection.pointInPolygon 方法,其内部原理是通过向量叉乘来判断点是否在多边形内
image.png
  • SVG 的填充属性 fill-rule: evenodd(奇偶填充)nonzero(非零填充) ,其内部实现 我猜 应该也是可以通过向量叉乘来解决

3.2.2 画多边形

既然知道了向量叉乘可以判断点是否在多边形内外,那么我们也可以根据这个几何意义去画任意多边形。以六边形为例:

image.png

标注及代码如下:

image.png
/**
  * 画六边形
  * @param center 中心点
  * @param side   六边形边长
  * @param color  六边形颜色
  */
vec4 drawHex(vec2 center, float side, vec4 color) {
  // 将uv往六边形中心点偏移,实现偏移后的坐标系原点在纹理中心,x 向右 y 向下
  // 并转换为我们需要判断的点
  vec2 uv = v_uv0.xy - center;
  vec3 p = vec3(uv, 0.0);

  // 计算六边形的六个顶点
  float c = cos(radians(60.0));
  float s = sin(radians(60.0));
  vec3 p0 = vec3(side, 0.0, 0.0);
  vec3 p1 = vec3(side * c, -side * s, 0.0);
  vec3 p2 = vec3(-side * c, -side * s, 0.0);
  vec3 p3 = vec3(-side, 0.0, 0.0);
  vec3 p4 = vec3(-side * c, side * s, 0.0);
  vec3 p5 = vec3(side * c, side * s, 0.0);

  // 计算当前点是否在六边形内(通过向量叉乘)
  float r0 = step(0.0, cross(p-p0, p1-p0).z);
  float r1 = step(0.0, cross(p-p1, p2-p1).z);
  float r2 = step(0.0, cross(p-p2, p3-p2).z);
  float r3 = step(0.0, cross(p-p3, p4-p3).z);
  float r4 = step(0.0, cross(p-p4, p5-p4).z);
  float r5 = step(0.0, cross(p-p5, p0-p5).z);

  // 如果在内部,inside = 1.0,否则 inside = 0.0
  float inside = r0 * r1 * r2 * r3 * r4 * r5;
  return vec4(color.rgb, color.a * inside);
}

void main() {
 // ... 其他代码
 gl_FragColor = drawHex(vec2(0.5, 0.5), 0.5, o);
}

参考资料

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

推荐阅读更多精彩内容

  • godot的坐标系 二维平面上的任何位置都可以用一对数字来表示。然而,我们也可以将位置 (4, 3) 看作是从 (...
    Zszen阅读 1,734评论 0 51
  • [toc] OpenGL 矩阵向量 向量 一个顶点同时也是一个向量. 方向 数量 长度或模为1的 当前这个顶点为 ...
    清风烈酒2157阅读 350评论 0 1
  • 1、委托是什么,事件是委托吗? 它们有什么区别? C#中委托通常是指委托类型创建的对象,它用于保存和调用同类型的方...
    SeriousWilson阅读 2,234评论 0 1
  • 一:什么是协同程序?答:在主线程运行时同时开启另一段逻辑处理,来协助当前程序的执行。换句话说,开启协程就是开启一个...
    CrixalisAs阅读 2,066评论 1 7
  • 这个是我刚刚整理出的Unity面试题,为了帮助大家面试,同时帮助大家更好地复习Unity知识点,如果大家发现有什么...
    编程小火鸡阅读 3,892评论 2 35