另一种(Yet Another)三角形线性插值方法

栏目: 后端 · 发布时间: 5年前

内容简介:本文简述了一种三角形线性插值的方法(本文仅讨论二维情况)给定一个三角形的顶点坐标(问题说的有些抽象,给张图会清晰明了一些,图中的

本文简述了一种三角形线性插值的方法(本文仅讨论二维情况)

相关问题

给定一个三角形的顶点坐标( P0, P1 和 P2 )以及对应的数值( V0, V1 和 V2 ),插值求解三角形内一点(坐标给定为 P )的数值( V ).

问题说的有些抽象,给张图会清晰明了一些,图中的 V 即是我们想要插值求解的数值.

另一种(Yet Another)三角形线性插值方法

插值方法

如何求解呢?一般我们是采用比例面积的方法,见下图(图中 a0, a1 和 a2 分别代表三个分割三角形的面积):

另一种(Yet Another)三角形线性插值方法

图中 a0 占三角形整体面积的比例代表 V2 ( P2 对应的数值)占 V 的比例, a1 占三角形整体面积的比例代表 V1 ( P1 对应的数值)占 V 的比例, 而 a2 占三角形整体面积的比例则代表 V0 ( P0 对应的数值)占 V 的比例,假设三角形的整体面积为 a , 则我们有:

V = a 0 a V 2 + a 1 a V 1 + a 2 a V 0 = ( a 0 V 2 + a 1 V 1 + a 2 V 0 ) / a \begin{aligned} V & = \dfrac{a_0}{a} * V_2 + \dfrac{a_1}{a} * V_1 + \dfrac{a_2}{a} * V_0 \\ & = (a_0 * V_2 + a_1 * V_1 + a_2 * V_0) / a \end{aligned}

接下来的问题就是如何根据三角形的顶点求解三角形的面积了,这里我们直接给出结论,有兴趣的朋友可以从 这里 开始了解,方法就是使用 叉积 :

假设三角形的三个顶点分别为 P0, P1 和 P2 , 则三角形面积 a 的计算公式为:

a = 1 2 ( P 1 P 0 ) × ( P 2 P 0 ) a = \dfrac{1}{2} * | (P_1 - P_0) \times (P_2 - P_0) |

如果 P0 的坐标为 <x0, y0> , P1 的坐标为 <x1, y1> , P2 的坐标为 <x2, y2> ,则上面的公式可表达为:

a = 1 2 ( P 1 P 0 ) × ( P 2 P 0 ) = 1 2 ( &lt; x 1 , y 1 &gt; &lt; x 0 , y 0 &gt; ) × ( &lt; x 2 , y 2 &gt; &lt; x 0 , y 0 &gt; ) = 1 2 ( &lt; x 1 x 0 , y 1 y 0 &gt; ) × ( &lt; x 2 x 0 , y 2 y 0 &gt; ) = 1 2 ( x 1 x 0 ) ( y 2 y 0 ) ( x 2 x 0 ) ( y 1 y 0 ) \begin{aligned} a &amp; = \dfrac{1}{2} * | (P_1 - P_0) \times (P_2 - P_0) | \\ &amp; = \dfrac{1}{2} * |(&lt;x_1, y_1&gt; - &lt;x_0, y_0&gt;) \times (&lt;x_2, y_2&gt; - &lt;x_0, y_0&gt;) | \\ &amp; = \dfrac{1}{2} * |(&lt;x_1 - x_0, y_1 - y_0&gt;) \times (&lt;x_2 - x_0, y_2 - y_0&gt;) | \\ &amp; = \dfrac{1}{2} * |(x_1 - x_0) * (y_2 - y_0) - (x_2 - x_0) * (y_1 - y_0) | \end{aligned}

这里需要说明的一点是,二维向量实际上是没有叉积定义的,但是我们可以将二维坐标点看做是三维坐标点(第三维取 0 即可)来进行求解,更多细节还是请参看 这里 .

讲到这里,我们便可以进行实现了,参考代码如下:

public struct Value2<T>
{
    public float x;
    public float y;
    public T v;

    public Vector2 Vector
    {
        get
        {
            return new Vector2(x, y);
        }
    }

    public Value2(float x, float y, T v)
    {
        this.x = x;
        this.y = y;
        this.v = v;
    }
}
public static float Cross(Vector2 v0, Vector2 v1)
{
    return v0.x * v1.y - v1.x * v0.y;
}

public static float TriangleArea(Vector2 v0, Vector2 v1)
{
    return 0.5f * Math.Abs(Cross(v0, v1));
}

public static float TriangleLerp(Value2f val0, Value2f val1, Value2f val2, Vector2 p)
{
    var v01 = val1.Vector - val0.Vector;
    var v02 = val2.Vector - val0.Vector;
    var v0p = p - val0.Vector;

    var a = TriangleArea(v01, v02);
    Debug.Assert(a > 0, "[MathUtil]Error to do triangle Lerp, seems vertexes collinear ...");
    
    var a0 = TriangleArea(v01, v0p);
    var a1 = TriangleArea(v0p, v02);
    var a2 = a - a0 - a1;

    return (val2.v * a0 + val1.v * a1 + val0.v * a2) / a;
}

另一种(Yet Another)插值方法

实际上我还尝试了一种类似于 双线性插值 的求解方法,发现也是可行的,参考下图:

另一种(Yet Another)三角形线性插值方法

其中的虚线线段平行于向量 P2 - P1 (虚线取用其他线段也是可行的,只是计算上不方便),只要我们求解出 P1’ 的对应数值 V1’ , P2’ 的对应数值 v2’ ,以及子线段( P1’ 至 P )占总线段( P1’ 至 P2’ )的比例 t ,则 P 点对应的数值 V 便可以用简单的线性插值来求解了:

V = ( 1 t ) V 1 + t V 2 V = (1 - t) * V_1&#x27; + t * V_2&#x27;

我们可以采用解析法来求解上面所需的 V1’ , V2’t , 参考下图:

另一种(Yet Another)三角形线性插值方法

我们设红色向量部分( P1’ - P )等于 t1 * (P2 - P1) ,黄色向量部分( P1’ - P0 )等于 t2 * (P1 - P0) ,由于相似三角形对应边成比例的关系,蓝色向量部分( P2’ - P0 )的比例系数也为 t2 ,类似的,向量 P2’ - P1’ 相对与向量 P2 - P1 的比例系数同样也为 t2 .

我们知道:

P 1 P 0 = ( P P 0 ) + ( P 1 P ) &ThickSpace; &ThickSpace; t 2 ( P 1 P 0 ) = ( P P 0 ) + t 1 ( P 2 P 1 ) \begin{aligned} &amp; P_1&#x27; - P_0 = (P - P_0) + (P_1&#x27; - P) \implies \\ &amp; t_2 * (P_1 - P_0) = (P - P_0) + t_1 * (P_2 - P_1) \end{aligned}

并且 P0 的坐标为 <x0, y0> , P1 的坐标为 <x1, y1> , P2 的坐标为 <x2, y2> , P 的坐标为 <x, y>

则有:

{ t 2 ( x 1 x 0 ) = x x 0 + t 1 ( x 2 x 1 ) t 2 ( y 1 y 0 ) = y y 0 + t 1 ( y 2 y 1 ) \left\{ \begin{aligned} t_2 * (x_1 - x_0) &amp; = x - x_0 + t_1 * (x_2 - x_1) \\ t_2 * (y_1 - y_0) &amp; = y - y_0 + t_1 * (y_2 - y_1) \end{aligned} \right.

求解可得:

{ t 1 = ( x 1 x 0 ) ( y y 0 ) ( x x 0 ) ( y 1 y 0 ) ( x 2 x 1 ) ( y 1 y 0 ) ( x 1 x 0 ) ( y 2 y 1 ) = ( P 1 P 0 ) × ( P P 0 ) ( P 2 P 1 ) × ( P 1 P 0 ) t 2 = ( x x 0 ) + t 1 ( x 2 x 1 ) x 1 x 0 o r ( y y 0 ) + t 1 ( y 2 y 1 ) y 1 y 0 \left\{ \begin{aligned} t_1 &amp; = \dfrac{(x_1 - x_0) * (y - y_0) - (x - x_0) * (y_1 - y_0)}{(x_2 - x_1) * (y_1 - y_0) - (x_1 - x_0) * (y_2 - y_1)} = \dfrac{(P_1 - P_0) \times (P - P_0)}{(P_2 - P_1) \times (P_1 - P_0)} \\ t_2 &amp; = \dfrac{(x - x_0) + t_1 * (x_2 - x_1)}{x_1 - x_0} or \dfrac{(y - y_0) + t_1 * (y_2 - y_1)}{y_1 - y_0} \end{aligned} \right.

可以看到 t1 的计算公式是一个叉积比例的形式,其实这个形式除了使用先前的解析方法,也可以运用几何方法来进行求解,只是对思维的要求比较高,有兴趣的朋友可以自己尝试一下(提示:叉积->面积).

有了 t1t2 ,我们就可以计算之前的 V1’ , V2’t 了:

V 1 = ( 1 t 2 ) V 0 + t 2 V 1 V 2 = ( 1 t 2 ) V 0 + t 2 V 2 t = t 1 ( P 2 P 1 ) t 2 ( P 2 P 1 ) = t 1 t 2 \begin{aligned} V_1&#x27; &amp; = (1 - t_2) * V_0 + t_2 * V_1 \\ V_2&#x27; &amp; = (1 - t_2) * V_0 + t_2 * V_2 \\ t &amp;= | \dfrac{t_1 * (P_2 - P_1)}{t_2 * (P_2 - P_1)} | = |\dfrac{t_1}{t_2}| \end{aligned}

相关实现代码如下:

public static float TriangleLerpV2(Value2f val0, Value2f val1, Value2f val2, Vector2 p)
{
    var v01 = val1.Vector - val0.Vector;
    var v12 = val2.Vector - val1.Vector;
    var v0p = p - val0.Vector;
    
    var c1 = Cross(v01, v0p);
    var c2 = Cross(v12, v01);
    Debug.Assert(c2 != 0, "[MathUtil]Error to do triangle Lerp, seems vertexes collinear ...");
    var t1 = c1 / c2;
    var t2 = v01.x != 0 ? (v0p.x + t1 * v12.x) / v01.x : (v0p.y + t1 * v12.y) / v01.y;
    if (t2 == 0)
    {
        return val0.v;
    }
    else
    {
        var t3 = Math.Abs(t1 / t2);
        var lerp0 = (1 - t2) * val0.v + t2 * val1.v;
        var lerp1 = (1 - t2) * val0.v + t2 * val2.v;
        return (1 - t3) * lerp0 + t3 * lerp1;
    }
}

方法对比

简单的测试对比发现,第二种插值方法较第一种 快 10% 左右 ~

更多资料


以上所述就是小编给大家介绍的《另一种(Yet Another)三角形线性插值方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Unity 3D脚本编程

Unity 3D脚本编程

陈嘉栋 / 电子工业出版社 / 2016-9-1 / 79

《Unity 3D脚本编程——使用C#语言开发跨平台游戏》以Unity 3D 的跨平台基础Mono,以及其游戏脚本语言C#为基础进行讲解。全面系统地剖析了Unity 3D 的跨平台原理以及游戏脚本开发的特点。 第1 章主要介绍了Unity 3D 引擎的历史以及编辑器的基本知识;第2 章主要介绍了Mono,以及Unity3D 利用Mono 实现跨平台的原理,并且分析了C#语言为什么更适合Uni......一起来看看 《Unity 3D脚本编程》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具