当使用rayTraceP
进行光线跟踪时,我可以找到光线与图的交点。
> rayTraceP (p2 (0, 0)) (r2 (1, 0)) ((p2 (1,-1) ~~ p2 (1,1))
Just (p2 (1.0, 0.0))
我想用它不仅找出“碰撞点”,而且还要找出碰撞时间和该点到曲面的法向量。
-- A Collision has a time, a contact point, and a normal vector.
-- The normal vector is perpendicular to the surface at the contact
-- point.
data Collision v n = Collision n (Point v n) (v n)
deriving (Show)
给定射线的起点和沿射线的速度矢量,我可以使用rayTraceP
找到接触点end
end <- rayTraceP start vel dia
我可以用start
和end
之间的距离来计算碰撞时间
time = distance start end / norm vel
但是我被困在寻找法向量上。我在这个函数中工作:
rayTraceC :: (Metric v, OrderedField n)
=> Point v n -> v n -> QDiagram B v n Any -> Maybe (Collision v n)
-- Takes a starting position for the ray, a velocity vector for the
-- ray, and a diagram to trace the ray to. If the ray intersects with
-- the diagram, it returns a Collision containing:
-- * The amount of time it takes for a point along the ray going at
-- the given velocity to intersect with the diagram.
-- * The point at which it intersects with the diagram.
-- * The normal vector to the surface at that point (which will be
-- perpendicular to the surface there).
-- If the ray does not intersect with the diagram, it returns Nothing.
rayTraceC start vel dia =
do
end <- rayTraceP start vel dia
let time = distance start end / norm vel
-- This is where I'm getting stuck.
-- How do I find the normal vector?
let normalV = ???
return (Collision time end normalV)
下面是我想让它做的一些例子:
> -- colliding straight on:
> rayTraceC (p2 (0, 0)) (r2 (1, 0)) (p2 (1,-1) ~~ p2 (1,1))
Just (Collision 1 (p2 (1, 0)) (r2 (-1, 0)))
> -- colliding from a diagonal:
> rayTraceC (p2 (0, 0)) (r2 (1, 1)) (p2 (1,0) ~~ p2 (1,2))
Just (Collision 1 (p2 (1, 1)) (r2 (-1, 0))
> -- colliding onto a diagonal:
> rayTraceC (p2 (0, 0)) (r2 (1, 0)) (p2 (0,-1) ~~ p2 (2,1))
Just (Collision 1 (p2 (1, 0)) (r2 (-√2/2, √2/2)))
> -- no collision
> rayTraceC (p2 (0, 0)) (r2 (1, 0)) (p2 (1,1) ~~ p2 (1,2))
Nothing
除了法向量之外,这些示例中的所有内容都是正确的。
我已经查阅了Diagrams.Trace和Diagrams.Core.Trace的文档,但也许我找错了地方。
发布于 2018-08-27 03:34:11
一般情况下,没有办法做到这一点;这取决于你具体击中了什么。有一个模块Diagrams.Tangent用于计算轨迹的切线,但是要计算给定点处的切线,您必须知道它相对于轨迹的参数;目前我们缺少的一件事是将给定的点转换为给定线段/轨迹/路径上最近点的参数(它已经在待办事项列表上一段时间了)。
更大的梦想,也许轨迹本身应该返回更多的信息-不仅仅是告诉你命中的射线有多远的参数,还包括关于你命中的信息(通过这些信息,人们可以更容易地做一些事情,比如计算法向量)。
你在计算什么东西的痕迹?也许有一种方法可以利用你用例的特定细节,以一种不太可怕的方式获得你想要的常态。
发布于 2018-08-27 12:18:53
Brent Yorgey的回答指出了Diagrams.Tangent模块,特别是normalAtParam
,它适用于Parameteric
函数,包括轨迹,但不是所有的图。
幸运的是,许多2D图表函数,如circle
、square
、rect
、~~
等,实际上可以返回任何TrailLike
类型,包括Trail V2 n
。因此,函数的类型为
rayTraceTrailC :: forall n . (RealFloat n, Epsilon n)
=>
Point V2 n
-> V2 n
-> Located (Trail V2 n)
-> Maybe (Collision V2 n)
如果可以定义,我将实际处理由circle
、square
、rect
、~~
等返回的值:
> rayTraceTrailC
(p2 (0, 0))
(r2 (1, 0))
(circle 1 # moveTo (p2 (2,0)))
Just (Collision 1 (p2 (1, 0)) (r2 (-1, 0)))
这个函数可以通过使用fixTrail
函数将轨迹分解为一系列固定的线段来定义,这些线段或者是线性曲线,或者是贝塞尔曲线。这将问题简化为更简单的rayTraceFixedSegmentC
。
rayTraceTrailC start vel trail =
combine (mapMaybe (rayTraceFixedSegmentC start vel) (fixTrail trail))
where
combine [] = Nothing
combine cs = Just (minimumBy (\(Collision a _ _) (Collision b _ _) -> compare a b) cs)
rayTraceFixedSegmentC
可以使用rayTraceP
来计算接触点,但我们不能立即找到法向量,因为我们不知道该接触点的参数是什么。因此,进一步尝试,并将fixedSegmentNormalV
辅助函数添加到愿望列表中:
rayTraceFixedSegmentC :: forall n . (RealFloat n, Epsilon n)
=>
Point V2 n
-> V2 n
-> FixedSegment V2 n
-> Maybe (Collision V2 n)
rayTraceFixedSegmentC start vel seg =
do
end <- rayTraceP start vel (unfixTrail [seg])
let time = distance start end / norm vel
let normalV = normalize (project (fixedSegmentNormalV seg end) (negated vel))
return (Collision time end normalV)
此fixedSegmentNormalV
函数只需返回通过单点的单个线段的法向量,而无需担心vel
方向。它可以破坏FixedSegment
类型,如果它是线性的,这很容易:
fixedSegmentNormalV :: forall n . (OrderedField n)
=>
FixedSegment V2 n -> Point V2 n -> V2 n
fixedSegmentNormalV seg pt =
case seg of
FLinear a b -> perp (b .-. a)
FCubic a b c d ->
???
在FCubic
的例子中,为了计算曲线通过pt
的参数,我不确定该怎么做,但是如果你不介意这里的近似值,我们可以沿着它取一堆点,找到离pt
最近的一个点。在那之后,我们可以按照布伦特·约基的建议给normalAtParam
打电话。
fixedSegmentNormalV seg pt =
case seg of
FLinear a b -> perp (b .-. a)
FCubic a b c d ->
-- APPROXIMATION: find the closest parameter value t
let ts = map ((/100) . fromIntegral) [0..100]
dist t = distance (seg `atParam` t) pt
t = minimumBy (\a b -> compare (dist a) (dist b)) ts
-- once we have that parameter value we can call a built-in function
in normalAtParam seg t
在此情况下,rayTraceTrailC
函数使用此近似值。但是,它不适用于Diagram
%s,只适用于Located Trail
%s。
它可以处理circle
和rect
等函数返回的值,但不能处理组合关系图。因此,只要您需要这种碰撞光线跟踪,您就必须将这些图的构建块作为轨迹保持分离。
使用法线向量来反射光线(出射光线与法线向量的角度相等)如下所示:
https://stackoverflow.com/questions/52028449
复制相似问题