三角形重心坐标空间
作者:追风剑情 发布于:2023-11-29 10:48 分类:Algorithms
虽然我们经常在 3D 中使用三角形,但三角形的表面是一个平面,它天生是一个 2D 物体。在 3D 中任意朝向的三角形表面上移动是一件令人烦恼的事。最好是有一个坐标空间与三角形表面相关联且独立于三角形所在的 3D 坐标空间。重心坐标空间正是这样的坐标空间。
三角形所在平面的任意点都能表示为顶点的加权平均值。这个权就称作重心坐标,从重心坐标(b1, b2, b3)到标准3D坐标的转换为: \begin{flalign} &(b_1,b_2,b_3)\Leftrightarrow b_1 \mathbf{v}_1 + b_2 \mathbf{v}_2 + b_3 \mathbf{v}_3 \tag{公式 12.21 从重心坐标中计算 3D 点坐标}&\\ \end{flalign}
重心坐标的总合是 1: \begin{flalign} &b_1 + b_2 + b_3 = 1 &\\ \end{flalign}
b1,b2和b3的值是每个顶点对该点的“贡献”或“权”。下图展示了一些点和它们的重心坐标。
这里应注意以下几点:
- 第一。三角形三个顶点的重心坐标都是单位向量: \begin{flalign} &(1,0,0) \Leftrightarrow \mathbf{v}_1 &\\ &(0,1,0) \Leftrightarrow \mathbf{v}_2 &\\ &(0,0,1) \Leftrightarrow \mathbf{v}_3 &\\ \end{flalign}
- 第二。在某顶点的相对边上的所有点的对应重心坐标分量为0。例如,对于所有与v1相对边上的点,b1=0。
-
第三。不只是三角形内的点,该平面上的所有点都能用重心坐标描述。三角形内的点的重心坐标在范围 0 到 1 之间变化。三角形外的点至少有一个坐标为负。重心坐标用和原三角形大小相同的块“嵌满”整个平面。如图 12.20 所示。
重心坐标空间的本质不同于笛卡尔坐标空间。这是因为重心坐空间系是 2D 的,但却使用了三个坐标。又因为坐标的和等于 1,所以重心坐标空间仅有两个自由度,有一个分量是冗余的。从另一方面说,重心坐标空间中仅用两个数就能完全的描述一个点,用这两个数就可以计算出第三个。
要将一个点从重心坐标空间转换到普通的 3D 坐标空间,只需要应用公式 12.21 来计算顶点加权平值就可以了。而计算 2D 或 3D 中任意一点的重心坐标就稍微困难一些。让我们看看怎样在 2D 中做到这一点。见图 12.21,它标出了三个顶点v1,v2,v3和点p。我们还标出了三个“子三角形”T1,T2,T3,它们和同样下标的顶点相对。稍后会用到它们。
现在,我们知道的是三个顶点和点 p 的笛卡尔坐标,而任务就是要计算重心坐标b1,b2和b3。根据这些已知条件可以列出三个等式和三个未知数(x,y为顶点) \begin{flalign} &\mathbf{p}_x=b_1 x_1 + b_2 x_2 + b_3 x_3 &\\ &\mathbf{p}_y=b_1 y_1 + b_2 y_2 + b_3 y_3 &\\ &b_1 + b_2 + b_3 = 1 &\\ \end{flalign} 解这个方程组得公式: \begin{flalign} &b_1=\frac{(\mathbf{p}_y - y_3)(x_2 - x_3)+(y_2 - y_3)(x_3-\mathbf{p}_x)}{(y_1 - y_3)(x_2 - x_3)+(y_2 - y_3)(x_3 - x_1)} &\\ &b_2=\frac{(\mathbf{p}_y-y_1)(x_3-x_1)+(y_3-y_1)(x_1-\mathbf{p}_x)}{(y_1 - y_3)(x_2 - x_3)+(y_2 - y_3)(x_3 - x_1)} \tag{公式 12.22 求 2D 点的重心坐标}&\\ &b_3=\frac{(\mathbf{p}_y-y_2)(x_1-x_2)+(y_1-y_2)(x_2-\mathbf{p}_x)}{(y_1 - y_3)(x_2 - x_3)+(y_2 - y_3)(x_3 - x_1)} &\\ \end{flalign}
仔细观察公式 12.22,发现每个表达式中的分母相同,并且都等于三角形面积的两倍 (参见 已知三角形各顶点的笛卡尔坐标求面积公式) 还有,对每个重心坐标b,其分子等于“子三角形”T面积的两倍。换句话说: \begin{flalign} &b_1=A(T_1)/A(T) &\\ &b_2=A(T_2)/A(T) \tag{公式 12.23 把重心坐标解释成面积比}&\\ &b_3=A(T_3)/A(T) &\\ \end{flalign}
注意,即使 p 在三角形外,这个解释也是成立的,这是因为如果顶点以逆时针方向列出,计算面积的公式将得到一个负值。如果三角形的三个顶点共线,分母上的“子三角形”的面积为零,重心坐标也就没有定义。
计算 3D 中任意点的重心坐标比在 2D 中复杂。不能再像以前那样解一个方程组了,因为有三个未知数和四个方程。另一个导致复杂性的地方是p可能不在三角形所在的平面中,这时重心坐标没有意义。但现在我们假设 p 在三角形所在的平面上。
一种技巧是通过抛弃x,y,z之中的一个分量,将 3D 问题转化到 2D 中,这和将三角形投影到三个基本平面中的某一个上面的原理相同。理论上,这是能解决问题的,因为投影面积和原面积成比例。
那么应该抛弃哪个坐标呢?不能总是抛弃某一个,因为如果三角形垂直于某个平面,投影点将共线如果三角形接近垂直于投影平面,会遇到浮点数精度问题。一种解决方法是挑选投影平面,使得投影面积最大。这可以通过检查平面的法向量做到,我们要抛弃的就是绝对值最大的坐标。例如,法向量为[-1,0,0]。我们将抛弃顶点和 p 的 x 分量,把三角形投影到 yz 平面。下面的代码展示了怎样计算 3D 中任意点的重心坐标:
using System;
public class Program
{
static void Main(string[] args)
{
Vector3 v1 = new Vector3(1, 0, 0);
Vector3 v2 = new Vector3(0, 0, 1);
Vector3 v3 = new Vector3(1, 1, 1);
Vector3[] v = { v1, v2, v3 };
Vector3 p = new Vector3(0.5f, 0.5f, 0.5f);
Vector3 b;
ComputeBarycentricCoords3d(v, p, out b);
Console.WriteLine("b=({0},{1},{2})", b.x, b.y, b.z);
Console.ReadKey();
}
/// <summary>
/// 计算p点的重心空间坐标
/// </summary>
/// <param name="v">三角形各顶点坐标</param>
/// <param name="p">要计算的坐标</param>
/// <param name="b">p点对应的重心坐标</param>
/// <returns></returns>
public static bool ComputeBarycentricCoords3d(Vector3[] v, Vector3 p, out Vector3 b)
{
b.x = b.y = b.z = 0;
//首先,计算两个边向量,呈顺时针方向
Vector3 d1 = v[1] - v[0];
Vector3 d2 = v[2] - v[1];
//用叉乘计算法向量,许多情况下,这一步都可以省路,因为法向量是预先计算的
//不需要正则化,不管预先计算的法向量是否正则化过。
Vector3 n = Vector3.CrossProduct(d1, d2);
//判断法向量中占优势的轴,选择投影平面
float u1, u2, u3, u4;
float v1, v2, v3, v4;
if (Math.Abs(n.x) >= Math.Abs(n.y) && Math.Abs(n.x) >= Math.Abs(n.z))
{
Console.WriteLine("yz平面投影");
//抛弃x,向yz平面投影
u1 = v[0].y - v[2].y;
u2 = v[1].y - v[2].y;
u3 = p.y - v[0].y;
u4 = p.y - v[2].y;
v1 = v[0].z - v[2].z;
v2 = v[1].z - v[2].z;
v3 = p.z - v[0].z;
v4 = p.z - v[2].z;
}
else if (Math.Abs(n.y) >= Math.Abs(n.z))
{
Console.WriteLine("xz平面投影");
//抛弃y,向xz平面投影
u1 = v[0].z - v[2].z;
u2 = v[1].z - v[2].z;
u3 = p.z - v[0].z;
u4 = p.z - v[2].z;
v1 = v[0].x - v[2].x;
v2 = v[1].x - v[2].x;
v3 = p.x - v[0].x;
v4 = p.x - v[2].x;
}
else
{
Console.WriteLine("xy平面投影");
//抛弃z,向xy平面投影
u1 = v[0].x - v[2].x;
u2 = v[1].x - v[2].x;
u3 = p.x - v[0].x;
u4 = p.x - v[2].x;
v1 = v[0].y - v[2].y;
v2 = v[1].y - v[2].y;
v3 = p.y - v[0].y;
v4 = p.y - v[2].y;
}
//计算分母,并判断是否合法
float denom = v1 * u2 - v2 * u1;
if (denom == 0.0f)
{
//退化三角形--面积为零的三角形
return false;
}
//计算重心坐标
float oneOverDenom = 1.0f / denom;
b.x = (v4 * u2 - v2 * u4) * oneOverDenom;
b.y = (v1 * u3 - v3 * u1) * oneOverDenom;
b.z = 1.0f - b.x - b.y;
return true;
}
public struct Vector3
{
public float x;
public float y;
public float z;
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector3 operator -(Vector3 l, Vector3 r)
{
Vector3 v;
v.x = l.x - r.x;
v.y = l.y - r.y;
v.z = l.z - r.z;
return v;
}
public static Vector3 operator *(Vector3 l, Vector3 r)
{
Vector3 v;
v.x = l.x * r.x;
v.y = l.y * r.y;
v.z = l.z * r.z;
return v;
}
public static Vector3 CrossProduct(Vector3 l, Vector3 r)
{
Vector3 v;
v.x = l.y * r.z - r.y * l.z;
v.y = l.z * r.x - r.z * l.x;
v.z = l.x * r.y - r.x * l.y;
return v;
}
}
}
标签: Algorithms
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号