顶点着色和计算光照矢量效果

Dea*_*unt 2 c# graphics shader

给定由三个3D点定义的三角形顶点,如何计算它与给定点之间的角度.

class Point3D
{
    double x, y, z;
}

class Vertex
{
     Point3D P1, P2, P3;
}

Point3D LightPoint;
Run Code Online (Sandbox Code Playgroud)

http://www.ianquigley.com/iq/RandomImages/vextor.png

红点亮点.蓝点 - 显示表面法线的三角形.

我需要计算表面法线,以及它与LightPoint之间的角度.我发现了一些碎片,但没有任何东西将它组合在一起.

Kei*_*thS 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);
Run Code Online (Sandbox Code Playgroud)

现在,法线是两个向量的叉积.使用以下公式:

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);
Run Code Online (Sandbox Code Playgroud)

现在,你需要在LightPoint和A之间的第三个向量:

Vector3D vectorLight = PointA.Subtract(LightPoint);
Run Code Online (Sandbox Code Playgroud)

这是光线从你的光源到达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);
Run Code Online (Sandbox Code Playgroud)

最后,马蒂亚斯的公式提到:

v1*v2 = | v1 || v2 | cos(theta)

重新排列和替换变量名称:

double theta = arccos(dotProduct/(lengthNormal*lengthLight))
Run Code Online (Sandbox Code Playgroud)

或者,如果您足够聪明地使用Media3D对象,请忘记所有长度和点数产品:

double theta = Vector3D.AngleBetween(vectorNormal, vectorLight);
Run Code Online (Sandbox Code Playgroud)

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));
Run Code Online (Sandbox Code Playgroud)