从点 P 到线段 (A,B) 的有符号距离由下式给出:
d = area(A, B, P) / length(B - A)
在哪里
area(A, B, C) = (B.x - A.x)*(C.y - A.y) - (B.y - A.y)*(C.x - A.x)
因此,您正在寻找的距离由下式给出:
dist0 = area(P, B, C) / length(C - B)
dist1 = area(A, P, C) / length(A - C)
dist2 = area(A, B, P) / length(B - A)
minimum distance = min(dist0, dist1, dist2)
剩下的问题是如何将其映射到 GPU 管道中。
一种方法是计算vec3 dist
三角形的每个顶点,然后让 OpenGL 对其进行插值:
dist at A = vec3( area(A, B, C)/length(C - B), 0, 0 )
dist at B = vec3( 0, area(A, B, C)/length(A - C), 0 )
dist at C = vec3( 0, 0, area(A, B, C)/length(B - A) )
然后在片段着色器中,您只需要计算插值 dist 分量的最小值:
in vec3 dist; // interpolated from the above
minimum distance = min(dist.x, min(dist.y, dist.z))
或者,如果您已经有了可用的重心坐标(例如,通过NV_fragment_shader_barycentric扩展),您可以搭载它。请注意,重心坐标公式与上述相同,直到比例因子:
bary0 = area(P, B, C) / area(A, B, C)
bary1 = area(A, P, C) / area(A, B, C)
bary2 = area(A, B, P) / area(A, B, C)
因此,如果bary
已经可用,我们只需要通过以下方式对其进行扩展:
dist0 = bary0 * area(A, B, C) / length(C - B)
dist1 = bary1 * area(A, B, C) / length(A - C)
dist2 = bary2 * area(A, B, C) / length(B - A)
由于三角形的比例因子是恒定的,因此可以减少一些 FLOP。
编辑:我错过了您要求的实际矢量而不仅仅是距离。不过这个概念保持不变:您需要将法线或顶点传递到着色器中,并根据计算出的距离(使用上述任一方法),选择最近边缘的法线,缩放并返回它. 假设n0
, n1
,n2
是与顶点A
, B
,C
相对应的法线;那么您将在片段着色器中执行以下操作:
in vec3 dist; // interpolated from the above
in vec2 n0, n1, n2; // normals
...
vec2 result;
if(dist.x < min(dist.y, dist.z))
result = dist.x*n0;
else if(dist.y < dist.z)
result = dist.y*n1;
else
result = dist.z*n2;