zoukankan      html  css  js  c++  java
  • 游戏中的2d数学-判断一个点是否在三角形内部

    资料参考

    https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage

    题外话
    scratchapixel的文章都很不错,缺点是稍啰嗦,啃起来比较慢。
    老外的教程都是怕别人看不懂,一个概念用2种方法说2遍的那种。

    下面是总结:

    首先我们先来判断一个点在直线的左边还是右边

    设直线起点和终点分别是p0和p1,点为p

    我们用p1-p0 得到直线的向量 (vec l)
    用p-p0得到关于p点的向量 (vec p)

    然后对2个向量使用叉乘:

    (vec l imes vec p)

    利用2d向量的叉乘性质,在右手坐标系中
    如果点p在直线的右边,叉乘结果>0
    如果点p在直线的左边,叉乘结果<0
    如果点p在直线上,叉乘结果=0

    对三角形来说,利用这个原理,对三角形的三条边分别叉乘
    如果三个结果都>0或者都<0,则点在三角形内部

    以下是对scratchapixel关于点在三角形内部判断的节选翻译:

    边界函数

    之前已经提到过,得到点是否和三角形重合有多重方法。列出更古老的方法确实是个不错的主意,不过这个课程中,我们只说明当今最普遍的方法。这个方法被Juan Pineda 在1988年的论文《"A Parallel Algorithm for Polygon Rasterization》中被提出。

    在我们开始细究Pineda的技术之前,我们先描述一下算法的原理。我们可以说,一个三角形的边可以看做是一条把一个2d平面分成2个部分的直线。Pineda的算法的原理是找到一个函数,他称之为边界函数,这样我们可以测试给定一个点在直线的哪一边(图二的点p)。点在直线的左边,则这个函数返回负数,右边返回正数,恰好在直线上则返回0。

    原文的图2

    在图2中,我们用这个函数对三角形的第一条边(用顶点v0和v1定义。注意2者的先后顺序非常重要)进行检测。如果我们对另外2条边也用相同的方法,我们可以清晰的看到一个区域(白色部分),这个区域内的所有点都是正值(图3)

    原文的图3

    如果我们取这个区域内的任意一点,我们就能发现,这个点位均为于三条边的右侧。如果P是在一个像素中心的一个点,我们就可以使用这个方法判断像素是否和三角形重叠。假设对于这个点,我们发现三角形的所有三条边均返回正值,那么该像素被三角形包含(或者与某条边重合)。Pinada 的这个方法正巧是线性的,这就意味着这个方法可以被增量计算,这一点我们待会再讲。

    既然我们已经理解了原理,让我们揭示下这个函数是什么。边界函数被定义为(边界用顶点v0和v1来定义):

    [E01(P)=(P.x−v0.x)∗(V1.y−V0.y)−(P.y−V0.y)∗(V1.x−V0.x) ]

    就像论文中说的,这个函数有用的属性是这个值反映了点(x,y)相对于V0和V1定义的边的位置:

    E(P) > 0,P在直线的右侧
    E(P) = 0,P恰好在直线上
    E(P) < 0,P在直线的左侧

    实际上这个函数等价于数学上向量(v1-v0)和向量(P-V0)之间叉乘的结果。我们也可以把这2个向量用矩阵的形式写出(用矩阵描述不涉及其他方面,仅仅是为了书写起来更简洁)。

    [left[ egin{matrix} (P.x−V0.x)&(V1.x−V0.x)\ (P.y−V0.y)&(V1.y−V0.y) end{matrix} ight] ]

    如果我们令(A=(P−V0)),$ B=(V1−V0)$,那么我们可以把上面的形式写成关于A和B的2X2矩阵

    [left[ egin{matrix} A.x&A.y\ B.x&B.y end{matrix} ight] ]

    计算这个矩阵的行列式:

    (A.x∗B.y−A.y∗B.x)

    如果你把A和B用向量 (P-V0)和 (V1-V0) 替换,得到:

    ((P.x−V0.x)∗(V1.y−V0.y)−(P.y−V0.y)∗(V1.x−V0.x))

    你发现,这个和上面定义的边界函数是一样的。换句话说,这个边界函数也可以被看做是有2D向量(P-v0)和 (v1-v0) 构成的2X2矩阵的行列式,或者是 (P-V0) 和 (V1-V0)这2个向量之间的叉积。事实上2个向量的行列式和叉乘的大小都有相同的集合解释。

    当我们观察3d向量之间(图4)的叉乘的时候,我们可以更容易理解到底发生了什么。在3D空间中,叉乘结果是返回另一个向量,该向量和另外2个源向量都垂直(或者说正交)。但就像你在图4上看到的那样,这个正交向量的大小随着2个向量之间的朝向而发生改变。当向量A(红色)和向量B(蓝色)同向或者反向时,叉乘结果向量C(绿色)的值是0。向量A在坐标(1,0,0)并且固定不动。B的坐标是(0,0,-1),那么绿色向量C的坐标是(0,-1,0)。假设我们想要找出C的“带正负的”大小,我们会发现他等于-1。另一个例子,当B的坐标等于(0,0,1),那么C的坐标是(0,1,0)并且“带正负的”大小等于1。第一个例子中,符号大小是负数,第二个例子是正数。

    未完待续...

  • 相关阅读:
    结对-结对编项目作业名称-开发环境搭建过程
    课后作业-阅读任务-阅读提问-3
    团队-象棋游戏-项目进度
    结对-五子棋游戏-测试过程
    课后作业-阅读任务-阅读提问2
    201501006-构建之法:现代软件工程-阅读笔记
    结对编程-五子棋游戏-开发过程
    象棋游戏,代码规范
    结编程队-五子棋游戏-项目进度
    《个人-GIT使用方法》
  • 原文地址:https://www.cnblogs.com/terrynoya/p/13226339.html
Copyright © 2011-2022 走看看