给定一个由三个3D点定义的三角形顶点,如何计算它与给定点之间的角度。
class Point3D
{
double x, y, z;
}
class Vertex
{
Point3D P1, P2, P3;
}
Point3D LightPoint;
http://www.ianquigley.com/iq/RandomImages/vextor.png
红点亮点。蓝点 - 显示表面法线的三角形。
我需要计算表面法线,以及它与LightPoint之间的角度。我发现了一些碎片,但没有任何东西将它组合在一起。
答案 0 :(得分:5)
好的,这里......
你有点A,B和C,每个点都有坐标x,y和z。正如Matias所说,你想要法线的长度,这样你就可以计算出你的点与法线原点之间的矢量与法线本身的夹角。它可能会帮助您意识到您的图像在我们的计算目的中具有误导性;法线(蓝线)应该从三角形的一个顶点发出。要将您的点转换为矢量,它必须去某处,并且您只知道顶点的点(虽然您可以插入三角形内的任何点,但顶点着色的整个点都不必)。
无论如何,第一步是将你的Point3D变成Vector3Ds。这可以通过获取每个原点和目标点坐标之间的差异来实现。使用一个点作为两个向量的原点,将另外两个点作为每个向量的目标。因此,如果A是你的原点,则从B中减去A,然后从C中减去A.现在你有一个向量来描述X,Y和Z轴上从A点到B点的运动幅度,同样来自A值得注意的是,理论向量没有自己的起点;要到达B点,你必须从A开始并应用向量。
System.Windows.Media.Media3D命名空间有一个可以使用的Vector3D结构,并且很容易,同一命名空间中的Point3D有一个返回Vector3D的Subtract()函数:
Vector3D vectorAB = pointB.Subtract(pointA);
Vector3D vectorAC = pointC.Subtract(pointA);
现在,法线是两个向量的叉积。使用以下公式:
v1 x v2 = [y1 * z2 - y2 * z1,z1 * x2 - z2 * x1,x1 * y2 - x2 * y1]
这是基于矩阵数学,您不必严格了解它来实现它。矩阵中的三个项是法向量的X,Y和Z.幸运的是,如果你使用Media3D命名空间,Vector3D结构有一个CrossProduct()方法,可以为你做这个:
Vector3D vectorNormal = Vector3D.CrossProduct(vectorAB, vectorAC);
现在,您需要LightPoint和A之间的第三个向量:
Vector3D vectorLight = PointA.Subtract(LightPoint);
这是光线从你的光源到达PointA的方向。
现在,要找到它们之间的角度,您可以计算这两者的点积和这两者的长度:
| V | = sqrt(x ^ 2 + y ^ 2 + z ^ 2)
v1 * v2 = x1 * x2 + y1 * y2 + z1 * z2
或者,如果你使用Media3D,Vector3D有一个Length属性和一个DotProduct静态方法:
double lengthLight = vectorLight.Length;
double lengthNormal = vectorNormal.Length;
double dotProduct = Vector3D.DotProduct(vectorNormal, vectorLight);
最后,Matias提到了公式:
v1 * v2 = | v1 || v2 | cos(theta)
重新排列并替换变量名称:
double theta = arccos(dotProduct/(lengthNormal*lengthLight))
或者,如果您足够聪明地使用Media3D对象,请忘记所有长度和点数产品:
double theta = Vector3D.AngleBetween(vectorNormal, vectorLight);
Theta现在是以度为单位的角度。如果你需要那么,乘以数量2(pi)/ 360得到弧度。
故事的寓意是,使用框架给你的东西,除非你有充分的理由不这样做;使用Media3D命名空间,所有矢量代数都消失了,您可以通过5个易于阅读的行找到答案[我编辑了这个,添加了我使用的代码 - Ian]:
Vector3D vectorAB = Point3D.Subtract(pointB, pointA);
Vector3D vectorAC = Point3D.Subtract(pointC, pointA);
Vector3D vectorNormal = Vector3D.CrossProduct(vectorAB, vectorAC);
Vector3D vectorLight = Point3D.Subtract(pointA, LightPoint);
double lengthLight = light.Length;
double lengthNormal = norm.Length;
double dotProduct = Vector3D.DotProduct(norm, light);
double theta = Math.Acos(dotProduct / (lengthNormal * lengthLight));
// Convert to intensity between 0..255 range for 'Color.FromArgb(...
//int intensity = (120 + (Math.Cos(theta) * 90));
答案 1 :(得分:1)
要获得法线向量,请计算P2 - P1和P3 - P1之间的叉积。
要获得角度,请使用法线和lightPoint之间的点积。请记住,dot(a,b)= | a || b | * cos(theta),因为你可以计算两者的长度,你可以得到theta(它们之间的角度)。
答案 2 :(得分:0)
仅与切线相关,但大多数图形系统也允许曲面法线插值,其中每个顶点都有相关的法线。然后,从顶点法线内插给定U,V处的有效法线。这允许更平滑的阴影和更好地表示照明中的“曲率”,而不必依赖于非常大量的三角形。许多系统使用“凹凸贴图”进行类似的技巧,以在法线中引入扰动并获得纹理感,而无需对单个小刻面进行建模。
现在你可以像其他人已经描述的那样计算插值法线和光矢量之间的角度。
另一个兴趣点是考虑您是否有环境光源。如果你可以假装点光源是无限远的(对于像地球这样的小表面上的直射阳光这样的光源是有效的),你可以省去计算矢量差异的所有麻烦,并简单地假设光的入射角是不变。现在,您的点积有一个恒定的光矢量。