首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >光线追踪与求曲面交点处的法向量

光线追踪与求曲面交点处的法向量
EN

Stack Overflow用户
提问于 2018-08-27 01:01:58
回答 2查看 883关注 0票数 3

当使用rayTraceP进行光线跟踪时,我可以找到光线与图的交点。

代码语言:javascript
复制
> rayTraceP (p2 (0, 0)) (r2 (1, 0)) ((p2 (1,-1) ~~ p2 (1,1))
Just (p2 (1.0, 0.0))

我想用它不仅找出“碰撞点”,而且还要找出碰撞时间和该点到曲面的法向量。

代码语言:javascript
复制
-- 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

代码语言:javascript
复制
end <- rayTraceP start vel dia

我可以用startend之间的距离来计算碰撞时间

代码语言:javascript
复制
time = distance start end / norm vel

但是我被困在寻找法向量上。我在这个函数中工作:

代码语言:javascript
复制
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)

下面是我想让它做的一些例子:

代码语言:javascript
复制
> -- 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.TraceDiagrams.Core.Trace的文档,但也许我找错了地方。

EN

回答 2

Stack Overflow用户

发布于 2018-08-27 03:34:11

一般情况下,没有办法做到这一点;这取决于你具体击中了什么。有一个模块Diagrams.Tangent用于计算轨迹的切线,但是要计算给定点处的切线,您必须知道它相对于轨迹的参数;目前我们缺少的一件事是将给定的点转换为给定线段/轨迹/路径上最近点的参数(它已经在待办事项列表上一段时间了)。

更大的梦想,也许轨迹本身应该返回更多的信息-不仅仅是告诉你命中的射线有多远的参数,还包括关于你命中的信息(通过这些信息,人们可以更容易地做一些事情,比如计算法向量)。

你在计算什么东西的痕迹?也许有一种方法可以利用你用例的特定细节,以一种不太可怕的方式获得你想要的常态。

票数 2
EN

Stack Overflow用户

发布于 2018-08-27 12:18:53

Brent Yorgey的回答指出了Diagrams.Tangent模块,特别是normalAtParam,它适用于Parameteric函数,包括轨迹,但不是所有的图。

幸运的是,许多2D图表函数,如circlesquarerect~~等,实际上可以返回任何TrailLike类型,包括Trail V2 n。因此,函数的类型为

代码语言:javascript
复制
rayTraceTrailC :: forall n . (RealFloat n, Epsilon n)
                  => 
                  Point V2 n 
                  -> V2 n
                  -> Located (Trail V2 n)
                  -> Maybe (Collision V2 n)

如果可以定义,我将实际处理由circlesquarerect~~等返回的值:

代码语言:javascript
复制
> rayTraceTrailC 
    (p2 (0, 0))
    (r2 (1, 0))
    (circle 1 # moveTo (p2 (2,0)))
Just (Collision 1 (p2 (1, 0)) (r2 (-1, 0)))

这个函数可以通过使用fixTrail函数将轨迹分解为一系列固定的线段来定义,这些线段或者是线性曲线,或者是贝塞尔曲线。这将问题简化为更简单的rayTraceFixedSegmentC

代码语言:javascript
复制
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辅助函数添加到愿望列表中:

代码语言:javascript
复制
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类型,如果它是线性的,这很容易:

代码语言:javascript
复制
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打电话。

代码语言:javascript
复制
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。

它可以处理circlerect等函数返回的值,但不能处理组合关系图。因此,只要您需要这种碰撞光线跟踪,您就必须将这些图的构建块作为轨迹保持分离。

使用法线向量来反射光线(出射光线与法线向量的角度相等)如下所示:

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52028449

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档